https://www.stitcher.io/rss <![CDATA[ stitcher.io ]]> 2024-03-16T05:00:17+00:00 <![CDATA[ The framework that gets out of your way ]]> https://www.stitcher.io/blog/the-framework-that-gets-out-of-your-way This post was first published for my newsletter subscribers. You can subscribe by mailing brendt@stitcher.io.

Half a year ago, I wrote this:

Ok, so, I've started working on something — probably the biggest project I've worked on in a while — and I'm having a blast. I'm building… A framework 😳

If you haven't heard of that framework before — it's called Tempest — you could check out me building the foundations of it during several livestreams.

I had a break working on Tempest for a couple of months because of other projects, but recently I started diving into it again. I've actually used it to build a small website, and let me tell you: I really enjoyed it.

That probably shouldn't come as a surprise since I built it the way I like to build things. But still, I was very happy seeing all pieces fit together so perfectly. Granted: there is a lot more fine-tuning to do, but I do think there's some potential here.

To be clear: Tempest doesn't aim to replace any frameworks like Laravel or Symfony; but for getting smaller PHP websites up and running? I feel like there's a lot of potential for a small and modern framework.

I especially like how Tempest gets out of your way. There's no need for config files or any setup, Tempest understands your code. I'm not kidding, it works really well.

So, I'd say go check out a very pre-alpha version of what undoubtedly will become something great in the future: https://github.com/tempestphp/tempest-framework.

# On a personal note

Much to my frustration, I've been sick since mid-December. Nothing serious, just a very annoying cough that doesn't want to go away. I've seen my doctor numerous times, a specialist, I'm using an inhaler, nasal spray, tried antacids, nothing seems to work. I actually have had the same issue for multiple years now: periods of coughing without any clear reason or solution.

Like I said, it's not too serious, just an annoying cough that doesn't want to go away, though it is very limiting when trying to record videos or audio. The more I talk, the worse it gets, so proper recording sessions aren't really an option right now. That three-minute video about Tempest I linked a couple paragraphs ago took waaaaay too much time and editing to get all the coughs out…

That's why I've been rather quiet on PHP Annotated, and why I've mostly focussed on writing content for PhpStorm lately. Last week, the first post in a series about JetBrains' AI Assistant for Unit Testing went live, and I've got a couple more scheduled soon.

I really hope my coughing gets over soon, and I really hope we'll find a proper solution. I've got another doctor's appointment scheduled next week and hope new medication will have a better impact.

Anyway, I mainly wanted to share this with you in case people were wondering why PHP Annotated is so quiet lately. I really want to get back into it a soon as possible though!

That's it for today's newsletter, do check out Tempest and give it a star while you're there 😉

Until next time

Brent

PS: I feel real inspired to livestream today. I'm still coughing, but I hope I'll manage. Come say hi:

]]>
2024-02-19T00:00:00+00:00
<![CDATA[ PHP version stats: January, 2024 ]]> https://www.stitcher.io/blog/php-version-stats-january-2024 It's time for another version stats post! This is my biyearly summary of which PHP versions are used across the community. You can read the previous edition here, but I'll also include historic data in this post.

Keep in mind note that I'm working with the data available. That means that these charts are not a 100% accurate representation of the PHP community as a whole, but they are an accurate representation of one of the most prominent parts of PHP: the packagist ecosystem.

This blog post was sponsored by Private Packagist - the private Composer repository from the creators and maintainers of Composer & Packagist.

# Usage Statistics

Let's start with the percentage of PHP versions being used today, and compare it to the previous three editions, note that I've omitted all versions that don't have more than 1% usage:

Version 2022-07 2023-01 2023-07 2024-01
7.1 1.9% 1.8% 1.3% 1.0%
7.2 5.1% 4.3% 4.3% 2.5%
7.3 8.0% 5.3% 4.2% 3.2%
7.4 38.4% 27.7% 19.9% 13.6%
8.0 20.6% 16.2% 12.3% 7.2%
8.1 24.5% 38.8% 39.3% 35.2%
8.2 0.0% 4.7% 17.2% 29.4%
8.3 0.0% 0.0% 0.2% 6.4%

Visualizing this data looks like this:

Evolution of version usage

There seems to be a slightly faster adoption of PHP 8.3 compared to PHP 8.2: 6.4% of projects are using PHP 8.3 within the first two months of its release, for PHP 8.2 it was 4.7%.

Furthermore, the PHP 7.* share continues to shrink — a good thing given that support for the 7.* series ended more than a year ago. Right now PHP 8.1 is the oldest supported version, only receiving security updates until November 25 this year. I can't help it, I keep saying the same thing over and over again, it's important update your PHP installations!

Moving on to the all-time overview chart, here you can see the evolution of version usage across time:

All time evolution

# Required versions

Next, I used Nikita's popular package analyzer to download the 1000 most popular composer packages. I use a script that scans these packages to determine their minimum required version. Here are the results:

Version 2022-07 2023-01 2023-07 2024-01
5.2 10 10 7 7
5.3 77 78 65 58
5.4 40 40 31 28
5.5 35 37 21 16
5.6 42 43 32 30
7.0 29 30 24 24
7.1 153 159 125 100
7.2 130 144 133 123
7.3 104 106 56 49
7.4 86 98 97 87
8.0 94 103 144 126
8.1 125 129 107 154
8.2 - - 94 135
8.3 - - - 0

There are two important notes to make here.

  1. This tables shows the minimum required version. That means that packages with a minimal version of, for example, 8.0, could also support PHP 8.1, PHP 8.2, and PHP 8.3.
  2. If you count the numbers, you'll notice there are some differences between each year. Not every package lists a valid version string.

Instead of comparing absolute numbers, it's best to plot this data into a chart for a relative comparison, so that we can see changes over time:

Minimal PHP requirement over time

Talking about progression, I'd like to remind open source maintainers about the power and responsibility they hold. Image if all modern open source packages only supported PHP versions that were actively worked on. My suspicion is that many more projects would be encouraged to update faster; eventually leading to a healthier, more performant, and more secure ecosystem. Open source maintainers yield quite a lot of power in this regard.

Also keep in mind that forcing a new minimal PHP requirement doesn't automatically block older projects from using your code: outdated projects can still download older versions of your packages, so there's really no good argument not to do it from a package maintainer's point of view.

This blog post was sponsored by Private Packagist - the private Composer repository from the creators and maintainers of Composer & Packagist.

That's all data I have to share for this edition of PHP's version stats. You can always reach me via email if you want to share your thoughts or have questions.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2024-01-29T00:00:00+00:00
<![CDATA[ Passion projects ]]> https://www.stitcher.io/blog/passion-projects Do you remember when you fell in love with programming? It probably wasn't while you were learning code at school or online, or working on a client project.

You can probably relate. That moment when you were writing code because you wanted to, no, because you desired to. No deadlines, no pressure. Only that feeling of power, being able to create something from nothing with code. It could be something as simple as an HTML web page, a program on your calculator to — hmm — cheat on your maths exam, or hacking something together for a game you're playing.

For me, at least, those are some of the examples that resonate.

I'm writing this as a reminder to myself and whoever needs to hear it: don't forget that time when your programming passion sparked. You're older now, you've got other things to do, you have responsibilities now. You might never experience the same amount of joy compared to that first year of programming, behind the computer in the living room while your siblings were playing, making lots of noise and distracting you.

But you can still find passion projects. Keep an open mind. Write a game, a website, a book, whatever. Not because you must, not to impress your social media following, not to make a living out of it — but because you can, because it gives you joy.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2023-12-13T00:00:00+00:00
<![CDATA[ I don't know ]]> https://www.stitcher.io/blog/i-dont-know Being online, I've gotten the impression that two groups of PHP developers are seriously divided: there are Symfony devs saying that Laravel is a hacky framework that leads to disaster, and there are Laravel devs saying that Symfony is a bloated framework that takes huge amounts of time to get things done.

I've always assumed this mentality existed mostly thanks to "being online", and that when it came to face-to-face interactions, people would be more forgiving towards each other.

Apparently, that assumption is wrong 😅

Last week, I went to SymfonyCon, and a year ago I went to Laracon. It turns out people are as harsh towards the other community in real life as they are online.

I'm baffled by this sentiment. There's no denying that people are building successful things with Symfony, just like there's no denying people are building successful things with Laravel. Why does anyone feel the need to convince me or others that their choice is superior? Why do we feel the need to pick sides? Why does it matter that someone else uses another solution to the same problem, if they manage to solve that problem equally well? Why do we feel the need to identify so strongly with a framework, that we need to dismiss the other?

I don't know.

Maybe we should say that more often: "I don't know", especially when it comes to the other side, and give them the benefit of the doubt.

What are your thoughts? Have you had the same experience? Maybe completely the opposite? Let me know!

PS: there were also a bunch of nice people at both SymfonyCon and Laracon, maybe that's good to mention as well 😁

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2023-12-12T00:00:00+00:00
<![CDATA[ The RFC Vote project ]]> https://www.stitcher.io/blog/rfc-vote I've been head over heels into programming the past two weeks. I started a new open source project, and it's been going great! Today, I want to share a bit of what's going on, and hope to peak your interest as well.

Two weeks ago, I had a chat with my colleague Roman about how PHP is evolving: we both want to get a better understanding of how the community feels about RFCs. While technically, the internals mailing list is open to everyone, I know only a very small group of PHP developers who actually participate. So we decided to build a small app that could help us and the community to lower the bar for participation.

Its temporary name is "RFC Vote" — but please share your ideas if you have better names! It's a small project that allows everyone to vote for RFCs. The most interesting part though is how you vote: if you vote directly, you must give argumentation to explain why you voted yes or no. On top of that: everyone can vote for existing arguments individually. Say someone already shared the same reasons as you for voting yes or no, then you can simply vote for that argument, instead of writing something on your own — argument votes are added to the total result as well.

We hope that by using this vote mechanic, we'll get better insights into why people vote the way they do, and not just if people would like a feature or not. And, maybe — we can even inspire internal developers to take a look at the community results, before casting their own vote for upcoming RFCs.

Here's an except from our about page:

This project is dedicated to providing a platform for the PHP community to express their thoughts and feelings about the proposals for the PHP language in an easier way.

Our main goal is to visualize the diverse opinions and arguments surrounding PHP's proposed features, making it easier to understand the benefits and downsides of each proposal. By doing so, we hope to foster a greater understanding of how PHP developers feel about these changes.

While official voting on RFCs is limited to internal qualified developers and a specific group of contributors, RFC Vote offers a space for everyone in the PHP community to share their voice. Your votes and comments won't directly influence the official PHP RFC outcomes, but they can serve as valuable insights for those involved in the decision-making process.

In addition to casting a vote, you are encouraged to share your reasoning behind your choices on each RFC. By explaining why you voted yes or no, we can collectively gain better insights into the popularity or concerns associated with an RFC. This collaborative approach allows us to learn from one another and contributes to a more informed and connected PHP community.

So, next thing I knew, I was programming like a madman, did some livestreams to show off the progress, and got help from 9 other developers.

After two weeks, we're nearing a "version 1", and so I wanted to share it with you as well. Maybe you have some ideas, questions, or just want to take a look? Well, the site is already live here: https://rfc.stitcher.io/, and you can check out the code on GitHub.

Let me know your thoughts!

Oh, by the way, I'll be doing another live stream at 11 CEST today (less than 2 hours from when this post was published), so you might want to tune in? I'll see you there!

]]>
2023-08-15T00:00:00+00:00
<![CDATA[ What's new in PHP 8.3 ]]> https://www.stitcher.io/blog/new-in-php-83

PHP 8.3 will be released on November 23, 2023; it has improvements to readonly classes, the new json_validate() function, additions to the recently added Randomizer class, stack overflow detection, and more.

In this post, we'll go through all features, performance improvements, changes and deprecations one by one. If you want to stay up to date, you can subscribe to my newsletter, follow me on Twitter, or subscribe to my RSS feed.

# Readonly amendments RFC

This RFC proposed two changes, only one was accepted: being able to reinitialize readonly properties while cloning. That might sound like a very big deal, but this RFC only addresses a very specific (but important) edge case: overwriting property values within __clone(), in order to allow deep cloning readonly properties.

readonly class Post
{
    public function __construct(
        public DateTime $createdAt,
    ) {}
    
    public function __clone()
    {
        $this->createdAt = new DateTime(); 
        // This is allowed,
        // even though `createdAt` is a readonly property.
    }
}

You can read an in-depth post about this RFC and some sidenotes here.


# Typed class constants RFC

You can now typehint class constants:

class Foo
{
    const string BAR = 'baz'; 
} 

# #[Override] attribute RFC

The new #[Override] attribute is used to show a programmer's intent. It basically says "I know this method is overriding a parent method. If that would ever change, please let me know".

Here's an example:

abstract class Parent
{
    public function methodWithDefaultImplementation(): int
    {
        return 1;
    }
}

final class Child extends Parent
{
    #[Override]
    public function methodWithDefaultImplementation(): int
    {
        return 2; // The overridden method
    }
} 

Now, let's imagine at one point the parent method changes its method name:

abstract class Parent
{
    public function methodWithNewImplementation(): int
    {
        return 1;
    }
}

Thanks to the #[Override] attribute, PHP will be able to detect that Child::methodWithDefaultImplementation() doesn't override anything anymore, and it will throw an error.

You can read more about the #[Override] attribute here.


# Negative indices in arrays breaking

If you have an empty array, add an item with a negative index, and then add another item, that second item would always start at index 0:

$array = [];

$array[-5] = 'a';
$array[] = 'b';

var_export($array);

//array (
//  -5 => 'a',
//  0 => 'b',
//)

Starting from PHP 8.3, the next item will be added at index -4:

//array (
//  -5 => 'a',
//  -4 => 'b',
//)

# Anonymous readonly classes UPGRADING

Previously, you weren't able to mark anonymous classes as readonly. That's fixed in PHP 8.3:

$class = new readonly class {
    public function __construct(
        public string $foo = 'bar',
    ) {}
};

# The new json_validate() function RFC

Previously, the only way to validate whether a string was valid JSON, was to decode it and detect whether any errors were thrown. This new json_validate() function is beneficial if you only need to know whether the input is valid JSON, since it uses less memory compared to decoding the string.

json_validate(string $json, int $depth = 512, int $flags = 0): bool

# Randomizer additions RFC

PHP 8.2 added the new Randomizer class. This update brings some small additions:

Randomizer::getBytesFromString(string $string, int $length): string

This method allows you to generate a string with a given length that consists of randomly selected bytes from a given string.

Randomizer::getFloat(
    float $min,
    float $max,
    IntervalBoundary $boundary = IntervalBoundary::ClosedOpen
): float

getFloat() returns a float between $min and $max. You can define whether $min and $max should be included thanks to the IntervalBoundary enum. Closed means the value is included, while Open means excluded.

Randomizer::nextFloat(): float {}

Finally, nextFloat() is a shorthand for getFloat(0, 1, IntervalBoundary::ClosedOpen), in other words: it'll give you a random float between 0 and 1, where 1 is excluded.


# Dynamic class constant fetch RFC

PHP 8.3 allows you to fetch constants with a more dynamic syntax:

class Foo 
{
    const BAR = 'bar';
}

$name = 'BAR';
 
// Instead of this:
constant(Foo::class . '::' . $name);

// You can now do this:
Foo::{$name};

# More Appropriate Date/Time Exceptions RFC breaking

In many cases, PHP would simply throw an Exception or Error object; or emit a warning or error when something went wrong in dealing with dates and times. This RFC goes through all those edge cases and adds proper, dedicated exceptions for them.

We now have exceptions like DateMalformedIntervalStringException, DateInvalidOperationException, and DateRangeError.

In general, these additions won't break any code, since these newly added exceptions and errors subclass the generic Exception and Error classes. However, there are three small breaking changes that come with this RFC:

  • The Epoch doesn't fit in a PHP integer now returns a new DateRangeError instead of a generic ValueError, which it does not subclass. This is only an issue for 32-bit platforms.
  • The Only non-special relative time specifications are supported for subtraction warning with DateTime::sub() and date_sub() becomes a new DateInvalidOperationException.
  • The Unknown or bad format (%s) at position %d (%c): %s and String '%s' contains non-relative elements warnings that are created while parsing wrong/broken DateInterval strings will now throw a new DateMalformedIntervalStringException when used with the OO interface, instead of showing a warning and returning false.

# Improved unserialize() error handling RFC

unserialize() will now always emit a E_WARNING when running into problems instead of sometimes an E_NOTICE.

This RFC also proposed adding more exceptions when running unserialize(), but that part didn't get accepted.


# Changes to the range() function breaking

From the changelog:

  • A TypeError is now thrown when passing objects, resources, or arrays as the boundary inputs
  • A more descriptive ValueError is thrown when passing 0 for $step
  • A ValueError is now thrown when using a negative $step for increasing ranges
  • If $step is a float that can be interpreted as an int, it is now done so
  • A ValueError is now thrown if any argument is infinity or NAN
  • An E_WARNING is now emitted if $start or $end is the empty string. The value continues to be cast to the value 0.
  • An E_WARNING is now emitted if $start or $end has more than one byte, only if it is a non-numeric string.
  • An E_WARNING is now emitted if $start or $end is cast to an integer because the other boundary input is a number. (e.g. range(5, 'z');)
  • An E_WARNING is now emitted if $step is a float when trying to generate a range of characters, except if both boundary inputs are numeric strings (e.g. range('5', '9', 0.5); does not produce a warning)
  • range() now produce a list of characters if one of the boundary inputs is a string digit instead of casting the other input to int (e.g. range('5', 'z');)

# Traits and static properties breaking

From the changelog:

Uses of traits with static properties will now redeclare static properties inherited from the parent class. This will create a separate static property storage for the current class. This is analogous to adding the static property to the class directly without traits.


# Stack overflow detection PR

PHP 8.3 adds two new ini directives called zend.max_allowed_stack_size and zend.reserved_stack_size. Programs that are close to overflowing the call stack may now throw an Error when using more than the difference between zend.max_allowed_stack_size and zend.reserved_stack_size.

The benefit of this feature is that stack-overflow-induced segmentation faults won't result in segfaults anymore, making debugging a lot easier.

The default for zend.max_allowed_stack_size is 0, meaning PHP will automatically determine a value. You can also provide -1 to indicate there isn't a limit, or a specific number of bytes. The zend.reserved_stack_size directive is used to determine the "buffer zone", so that PHP is able to still throw an error instead of actually running out of memory. The value here should be a number of bytes, but PHP will determine a reasonable default for you, so you don't necessarily need to set it, unless you're running into edge cases for specific programs.

On a final note, for fibers, the existing fiber.stack_size directive is used as the max allowed stack size.

zend.max_allowed_stack_size=128K

# New mb_str_pad function RFC

From the RFC:

In PHP, various string functions are available in two variants: one for byte strings and another for multibyte strings. However, a notable absence among the multibyte string functions is a mbstring equivalent of str_pad(). The str_pad() function lacks multibyte character support, causing issues when working with languages that utilize multibyte encodings like UTF-8. This RFC proposes the addition of such a function to PHP, which we will call mb_str_pad().

The function looks like this:

function mb_str_pad(
    string $string, 
    int $length, 
    string $pad_string = " ", 
    int $pad_type = STR_PAD_RIGHT, 
    ?string $encoding = null,
): string {}

# Magic method closures and named arguments PR

Let's say you have a class that supports magic methods:

class Test {
    public function __call($name, $args) 
    {
        var_dump($name, $args);
    }
    
    public static function __callStatic($name, $args) {
        var_dump($name, $args);
    }
}

PHP 8.3 allows you to create closures from those methods, and then pass named arguments to those closures. That wasn't possible before.

$test = new Test();

$closure = $test->magic(...);

$closure(a: 'hello', b: 'world'); 

# Invariant constant visibility breaking

Previously, visibility for constants weren't checked when implementing an interface. PHP 8.3 fixes this bug, but it might lead to code breaking in some places if you weren't aware of this behaviour.

interface I {
    public const FOO = 'foo';
}

class C implements I {
    private const FOO = 'foo';
}

# The small deprecations RFC rfc

As is usual with every release, there's a single RFC that adds a bunch of small deprecations. Keep in mind that deprecations are no errors, and these are generally a good thing for the language to move forward. These are the deprecations that passed, you can read more details about them in the RFC:

  • Deprecate passing negative $widths to mb_strimwidth()
  • Deprecate and remove the NumberFormatter::TYPE_CURRENCY constant
  • Deprecate and remove the broken pre-PHP 7.1 Mt19937 implementation (MT_RAND_PHP)
  • Deprecate and remove calling ldap_connect() with 2 parameters $host and $port
  • Deprecate remains of string evaluated code assertions

# Small, but notable changes

Not every change in PHP passes the RFC process. In fact, the majority of changes include maintenance and bugfixing, and don't require an RFC. All of these changes are listed in the UPGRADING document. I'll list some of the most prominent ones, but you should definitely read throughout the whole list if you want to know about the tiniest details.

  • When using FFI, C functions that have a return type of void now return null instead of returning FFI\CData:void
  • posix_getrlimit() now takes an optional $res parameter to allow fetching a single resource limit.
  • gc_status() has four new fields: running, protected, full, and buffer_size.
  • class_alias() now supports creating an alias of an internal class.
  • mysqli_poll() now raises a ValueError when the read nor error arguments are passed.
  • array_pad() is now only limited by the maximum number of elements an array can have. Before, it was only possible to add at most 1048576 elements at a time.
  • New posix functions: posix_sysconf(), posix_pathconf(), posix_fpathconf(), and posix_eaccess()
  • Executing proc_get_status() multiple times will now always return the right value on posix systems.
  • opcache.consistency_checks ini directive was removed
  • Improved array_sum() and array_product()

That's it for now, but this list will grow over time. If you want to stay up to date, you can subscribe to my newsletter, follow me on Twitter, or subscribe to my RSS feed.

]]>
2023-11-23T00:00:00+00:00
<![CDATA[ "Is A" or "Acts As" ]]> https://www.stitcher.io/blog/is-a-or-acts-as I voiced my preference for the recent interface default methods RFC, and many people told me I was wrong: an interface is only a contract and shouldn't provide implementations.

They are, of course, right, but only half. Let's talk about object relations.

You can think of the classic class/interface relation as an Is A relation: you can say Item Is A Purchasable Item, or, in technical terms: class Item implements Purchasable.

I don't think there's any disagreement here amongst developers, this is the classic definition of an interface. It allows us to write code that works with all types of purchasables, without worrying which concrete implementation we're dealing with.

function createPayment(Purchasable $purchasable): Payment
{
    $price = $purchasable->getPrice();
    
    // …
}

This is where people who argue against interface default methods stop. If this is the only way you're using interfaces, then yes, you're right: interface default methods aren't necessary.

However, there is another way interfaces are used. And mind you: I'm not saying "there is another way interfaces can be used", no, I'm saying this is happening in modern PHP code, today, in many projects.

Here goes. Interfaces can be used to model an Acts As relation — I have a perfect example, thanks to Larry.

Here's the so called LoggerInterface, part of PSR-3:

interface LoggerInterface
{
    public function emergency(
        string|\Stringable $message, 
        array $context = []
    ): void;

    public function alert(
        string|\Stringable $message, 
        array $context = []
    ): void;

    public function critical(
        string|\Stringable $message, 
        array $context = []
    ): void;

    public function error(
        string|\Stringable $message, 
        array $context = []
    ): void;

    public function warning(
        string|\Stringable $message, 
        array $context = []
    ): void;

    public function notice(
        string|\Stringable $message, 
        array $context = []
    ): void;

    public function info(
        string|\Stringable $message, 
        array $context = []
    ): void;

    public function debug(
        string|\Stringable $message, 
        array $context = []
    ): void;

    public function log(
        $level, 
        string|\Stringable $message, 
        array $context = []
    ): void;
}

As you can see, most methods are essentially shortcuts for the log method: they are convenience methods so that you don't have to manually provide a logging level. It's a great design choice to include in the interface, as it forces all implementations to have better accessibility.

However, let's be honest, no one is ever going to need a logger with a different implementation of debug or info or any of the other shorthands. These methods will always look the same:

public function debug(
    string|\Stringable $message, 
    array $context = []
): void
{
    $this->log(LogLevel::DEBUG, $message, $context);
}

In essence, this LoggerInterface is not only describing an Is A relation — if that were the case we'd only need the log method. No, it also describes an Acts As relation: a concrete logger implementation can Act As as proper logger, including the convenience methods associated with it. FileLogger Acts As LoggerInterface, Monolog Acts As LoggerInterface.

It all boils down to the question: is this a valid way of writing and using interfaces? I would say, yes.

I'd encourage you to take a close look at your own projects, and be totally honest for a moment: are any of your interfaces describing an Acts As relationship? If they do, then you cannot make the case against interface default methods.

]]>
2023-07-06T00:00:00+00:00
<![CDATA[ PHP version stats: July, 2023 ]]> https://www.stitcher.io/blog/php-version-stats-july-2023 Once again, I'm writing my summary of which PHP versions are used across the community. You can read the previous edition here, but I'll also include historic data in this post.

As always, it's important to note that I'm working with the data available to us. That means that these charts are not a 100% accurate representation of the PHP community as a whole, but they are an accurate representation of one of the most prominent parts of PHP: the packagist ecosystem.

This blog post was sponsored by Private Packagist - the private Composer repository from the creators and maintainers of Composer & Packagist.

# Usage Statistics

Let's start with the percentage of PHP versions being used today, and compare it to the previous three editions, note that I've omitted all versions that don't have more than 1% usage:

Version 2022-01 2022-07 2023-01 2023-07
7.1 2.4% 1.9% 1.8% 1.3%
7.2 6.6% 5.1% 4.3% 4.3%
7.3 12.0% 8.0% 5.3% 4.2%
7.4 43.9% 38.4% 27.7% 19.9%
8.0 23.9% 20.6% 16.2% 12.3%
8.1 9.1% 24.5% 38.8% 39.3%
8.2 0.0% 0.0% 4.7% 17.2%

Visualizing this data looks like this:

Evolution of version usage

It's important to know which PHP versions are currently still supported: PHP 8.2 and PHP 8.1 are still receiving updates. PHP 8.0 is still getting security updates until the end of November, this year. That means that PHP 7.4 and below don't receive any updates more, and should be considered end of life.

In total, that's around 30% of packagist downloads by outdated and insecure version of PHP. At the beginning of this year, that number was closer to 40%, meaning we see a steady decline — a good thing!

Moving on to the all-time overview chart, here you can see the evolution of version usage across time:

All time evolution

It seems that PHP 8.1 saw the biggest growth over time since PHP 7.4 and PHP 5.5. PHP 8.2, in comparison, seems to make a slower start. It's also interesting to note a relative high percentage of PHP 8.1 two years in a row. Granted, PHP 8.1 was a pretty solid release with features like enums and readonly properties. It'll be interesting to see how this graph evolves next year, when PHP 8.1 moves in security fixes only mode.

This blog post was sponsored by Private Packagist - the private Composer repository from the creators and maintainers of Composer & Packagist.

# Required versions

Next, I used Nikita's popular package analyzer to download the 1000 most popular composer packages. I wrote a script that scans these packages to determine their minimum required version. Here are the results:

Version 2022-01 2022-07 2023-01 2023-07
5.2 10 10 10 7
5.3 83 77 78 65
5.4 43 40 40 31
5.5 42 35 37 21
5.6 49 42 43 32
7.0 29 29 30 24
7.1 190 153 159 125
7.2 133 130 144 133
7.3 116 104 106 56
7.4 69 86 98 97
8.0 160 94 103 144
8.1 - 125 129 107
8.2 - - - 94

There are two important notes to make here.

  1. This tables shows the minimum required version. That means that packages with a minimal version of, for example, 8.0, could also support PHP 8.1 or and PHP 8.2.
  2. If you count the numbers, you'll notice there are some differences between each year. Not every package lists a valid version string.

Instead of comparing absolute numbers, it's best to plot this data into a chart for a relative comparison, so that we can see changes over time:

Minimal PHP requirement over time

There seems to be a pretty big leap in PHP 8.0 and PHP 8.1 being the minimal versions — a good thing. After all, the open source community plays a big part in pushing the community forward by increasing their minimal required version.


That's all data I have to share for this edition of PHP's version stats. You can always reach me via email if you want to share your thoughts or have questions. You can also subscribe to my newsletter if you want to receive updates about this blog in the future.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2023-07-01T00:00:00+00:00
<![CDATA[ #[Override] in PHP 8.3 ]]> https://www.stitcher.io/blog/override-in-php-83 There's a new feature in PHP 8.3: the #[Override] attribute. It's a feature already known in other languages, but let me summarize in case you're unaware of what it does.

Marking a method with the #[Override] attributes signifies that you know this method is overriding a parent method. So the only thing it does, is show intent.

Why does that matter? You already know you're overriding a method, don't you? Well, let's imagine these two classes:

abstract class Parent
{
    public function methodWithDefaultImplementation(): int
    {
        return 1;
    }
}

final class Child extends Parent
{
    #[Override]
    public function methodWithDefaultImplementation(): int
    {
        return 2; // The overridden method
    }
} 

Now, let's imagine at one point the parent method changes its method name:

abstract class Parent
{
    public function methodWithNewImplementation(): int
    {
        return 1;
    }
}

Before the #[Override] attribute, there was no way of knowing that Child::methodWithDefaultImplementation() doesn't override the renamed method anymore, which could lead to unforeseen bugs.

Thanks to #[Override] though, PHP now knows something is wrong, thanks to that attribute. It basically says "I know this method should override a parent method. If that would ever change, please let me know".

# Some thoughts

What strikes me most about this RFC, is how irrelevant it could be. Once again we're adding runtime checks for something that could be determined by static analysers.

I don't want to repeat every argument I made in the past, so I'll just link to my previous thoughts on the topic, and summarise: we're missing out. PHP internals should either come with an official spec for static analysers, or with a first-party static analyser. Why? Because so much more would be possible, and it would drive PHP forward tremendously.

Anyway, it seems like many people have tried to make the same argument about this RFC in particular on the Internals mailing list, to no avail.

I don't mind this feature, although I'll probably never use it myself (I use an IDE that prevents me from making these kinds of mistakes, I don't need PHP's runtime to double-check it for me). What I am sad about, is how the PHP community is divided between the static-analysis and non-static-analysis camps, and I don't know if there will ever be someone who'll be able to unify and steer the language into a next decade of progression.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2023-06-21T00:00:00+00:00
<![CDATA[ Don't be clever ]]> https://www.stitcher.io/blog/dont-be-clever Ten years ago, I wrote the most beautiful, clever, over-engineered piece of code ever. I was building a REST API for a startup, and discovered lots of repetition between controllers: I was building the same kind of actions and copying code over and over again. So I came up with a solution: The CRUDController™.

It was a beautifully complex, abstract class that integrated with Doctrine (the ORM that I was using back then). It had every possible CRUD operation you could think of — not just for entities, but also for child- and has many relations; there were automatic overviews, filtering, pagination, validation, data persistence, routing, and whatnot.

And the only thing I had to do was to create a new controller, extend from my amazing CRUDController™, provide an entity class, and be done.

class TimesheetController extends CRUDController
{
    public function getEntity(): string
    {
        return Timesheet::class;
    }
}

Such a blast!

Except, of course: exceptions started to emerge. Not the programming kind, but the business kind. Some controllers had to do some things a little differently. It was small things at first: different URL schemes, different kinds of validation; but soon, things grew more and more complex: support for nested entities or complex filtering, to name a few.

And young me? I just kept going. Adding the proverbial knobs and pulls to my abstract class (which was growing into a set of classes by now).

In the end, I created a monster; and — ironically — it had taken more time than if I had simply copied code between controllers over and over again. On top of that: I was leaving the startup, and no one really understood how more than 50 controllers actually worked.

You might assume I understood, but let me be clear: I didn't really know how much of it worked anymore.

Yes, I had failed to see the proper solution: a class generator — so that I didn't have to manually copy code again. It actually existed back then, I simply didn't know about it, no one told me about it, and I wasn't smart enough to question myself once I started going down a certain path.

It's such a classic example, many of you probably recognise it. I wouldn't say my coding skills were bad, by the way. No, I didn't question myself enough, I lacked self-reflection and wasn't able to critically look at my own ideas.

I wish I could show you The CRUDController™'s source code; but I don't have access to it anymore, unfortunately. Luckily, the memory still exists. And it's a memory I remind myself of very often when I'm going down the path of abstractions and complications. It's often enough to hold me back and look for better solutions.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2023-06-02T00:00:00+00:00
<![CDATA[ Building a procedurally generated game with PHP ]]> https://www.stitcher.io/blog/procedurally-generated-game-in-php Today, we're building a procedurally generated, 2D game with PHP. It's a simple game that's focussed around resource gathering on a procedurally generated map. In case you're unfamiliar with that term: it's a map there's generated by code, based on a seed. Every seed will generate a completely unique map. It looks something like this:

So, how do we go from plain PHP to a map like this? It all starts with noise.

# Noise generation

Let's imagine we have grid of 150 by 100 pixels. Each pixel makes up a point of our map.

$pixels = [];

for($x = 0; $x < 150; $x++) {
    for($y = 0; $y < 100; $y++) {
        $pixels[$x][$y] = drawPixel($x, $y, 0); 
    }
}

For now, the drawPixel function will generate a div per pixel, which can be laid out on a CSS grid. We can refactor to use canvas later, but being able to use CSS's built-in grid saves a lot of time.

function drawPixel(int $x, int $y): string
{
    return <<<HTML
    <div style="--x: {$x}; --y: {$y};"></div>
    HTML;
}

This is the template file:

<style>
    :root {
        --pixel-size: 9px;
        --pixel-gap: 1px;
        --pixel-color: #000;
    }

    .map {
        display: grid;
        grid-template-columns: repeat({{ count($pixels) }}, var(--pixel-size));
        grid-auto-rows: var(--pixel-size);
        grid-gap: var(--pixel-gap);
    }

    .map > div {
        width: var(--pixel-size);
        height: 100%;
        grid-area: var(--y) / var(--x) / var(--y) / var(--x);
        background-color: var(--pixel-color);
    }
</style>

<div class="map">
    @foreach($pixels as $x => $row)
        @foreach($row as $y => $pixel)
            {!! $pixel !!}
        @endforeach
    @endforeach
</div>

This is the result:

By the way, I will get rid of the gaps between pixels, I only added them to show that these are indeed separate grid cells.

Let's play around with our grid. We'll start by assigning a value between 0 and 1 for each individual pixel. We'll use a class called Noise that takes a pixel's point (X/Y coordinates), and returns a value for that point. First, we'll return a random value.

final readonly class Noise
{
    public function __construct(
        private int $seed,
    ) {}
    
    public function generate(Point $point): float
    {
        return rand(1, 100) / 100;
    }
}

// …

drawPixel($x, $y, $noise->generate($x, $y)); 

Here's the result:

Relying on randomness won't get us very far though. We want a given seed to generate the same map over and over again. So instead of randomness, let's write a hash function: a function that, for any given point and seed, will generate the same value over and over again. You could make it as simple as multiplying the seed with the x and y coordinates, and turning that into a fraction:

public function generate(Point $point): float
{
    $hash = $this->seed * $point->x * $point->y;
    
    return floatval('0.' . $hash);
}

The result, however, doesn't seem random enough. Remember that we want to generate a world map: we want some randomness, but also some cohesion. So our hash function will need to be a bit more complex.

Let's try an existing hash function that we don't need to invent ourselves. We probably want a performant one, since we're generating a hash for thousands of pixels. PHP has support for xxHash, which is an "extremely fast hashing algorithm". Let's give it a try.

private function hash(Point $point): float
{
    $hash = bin2hex(
        hash(
            algo: 'xxh32',
            data: $this->seed * $point->x * $point->y,
        )
    );

    $hash = floatval('0.' . $hash);

    return $hash;
}

This noise looks promising: it's quite random, but always yields the same result for a given seed. But going from this to a cohesive world map still seems like a leap. Let's change our hash function, so that it'll return the same color within a square of 10 pixels:

private function hash(Point $point): float
{
    $baseX = ceil($point->x / 10);
    $baseY = ceil($point->y / 10);

    $hash = bin2hex(
        hash(
            algo: 'xxh32',
            data: $this->seed * $baseX * $baseY,
        )
    );

    $hash = floatval('0.' . $hash);

    return sqrt($hash);
}

Here's the result:

Oh, by the way: I take the square root of the hash, just to increase all values a little bit. That'll be useful in the future, but not necessary. Without the square root, the map looks like this:

Let's imagine something for a second. Let's say all pixels with a value higher than 0.6 are considered land, and all pixels with a lower value are considered water. Let's make some changes to our drawPixel method to reflect that behaviour:

function drawPixel(int $x, int $y, float $value): string
{
    $hexFromNoise = hex($value);
    
    $color = match(true) {
        $noise < 0.6 => "#0000{$hexFromNoise}", // blue
        default => "#00{$hexFromNoise}00", // green
    };
    
    return <<<HTML
    <div style="--x: {$x}; --y: {$y}; --pixel-color: {$color}"></div>
    HTML;
}

By the way, that hex function converts a value between 0 and 1 to a two-digit hexadecimal. It looks like this:

function hex(float $value): string
{
    if ($value > 1.0) {
        $value = 1.0;
    }

    $hex = dechex((int) ($value * 255));

    if (strlen($hex) < 2) {
        $hex = "0" . $hex;
    }

    return $hex;
}

The result is looking a lot more like a map already:

Ok, pretty nice! But these sharp edges don't really look realistic. Can we find a way to make transitions between the edges more smooth?

# Lerp

Time for some maths. Let's say we have two values: 0.34 and 0.78. We want to know the value exactly in the middle between these two. How do we do that?

Well, there's a simple mathematical formula for that. It's called "Linear Interpolation" — "LERP" for short:

function lerp(float $a, float $b, float $fraction): float
{
    return $a + $fraction * ($b - $a);
}

lerp(0.34, 0.78, 0.5); // 0.56

So, given a number $a (0.34), a number $b (0.78), and a fraction (0.5, also known as: "half"); we get 0.56 — the number exactly in the middle between 0.34 and 0.78.

Thanks to the fraction part in our lerp formula, we can determine the value at any place between these points, not just the middle:

lerp(0.34, 0.78, 0.25); // 0.45

Ok, so why is this important? Well, we can use our lerp function to smooth edges! Let go back to our noise pattern and explain:

Let's say that, instead of coloring each pixel in this grid, we only color a pixel when it's exactly on a 10x10 lattice. In other words: when its x and y coordinates are divisible by 10.

final readonly class Noise
{
    public function generate(Point $x): float
    {
        if ($point->x % 10 === 0 && $point->y % 10 === 0) {
            return $this->hash($point);
        } else {
            return 0.0;
        }
    }

    // …
}

Here's the lattice:

Let's imagine that these pixels are the $a and $b boundaries we pass into our lerp function. For any given pixel, it's trivial to determine these surrounding "fixed" points (they are on a fixed 10x10 grid) and we can also calculate the pixel's relative distance to those points. We can use the hash of these fixed points and the distance from any pixel to these points as input values for our lerp function. The result will be a value that's somewhere between the values of our two edge points — in other words: a smooth transition.

First, we'll use our lerp function on the y-axis (whenever x is divisible by 10). We'll determine the relative top and bottom points on our "lattice", calculate the distance between our current point and the top point, and then we'll use our lerp function to determine the right value between the top and bottom point, with that distance fraction:

if ($point->x % 10 === 0 && $point->y % 10 === 0) {
    $noise = $this->hash($point);
} elseif ($point->x % 10 === 0) {
    $topPoint = new Point(
        x: $point->x,
        y: (floor($point->y / 10) * 10), 
        // The closest point divisible by 10, above our current pixel
    );

    $bottomPoint = new Point(
        x: $point->x, 
        y: (ceil($point->y / 10) * 10) 
        // The closest point divisible by 10, below our current pixel
    );

    $noise = lerp(
        // The hash value (or color) of that top point:
        a: $this->hash($topPoint),
        
        // The hash value (or color) of that bottom point:
        b: $this->hash($bottomPoint),
        
        // The distance between our current point and the top point
        // — the fraction
        fraction: ($point->y - $topPoint->y) / ($bottomPoint->y - $topPoint->y),
    );
}

Here's the result, you can already see the smooth transition within the lines:

Next, let's add the same functionality in the other direction, when y is divisible by 10:

if ($point->x % 10 === 0 && $point->y % 10 === 0) {
    // …
} elseif ($point->x % 10 === 0) {
    // …
} elseif ($point->y % 10 === 0) {
    $leftPoint = new Point(
        x: (floor($point->x / 10) * 10),
        y: $point->y,
    );

    $rightPoint = new Point(
        x: (ceil($point->x / 10) * 10),
        y: $point->y,
    );

    $noise = lerp(
        $this->hash($leftPoint),
        $this->hash($rightPoint),
        ($point->x - $leftPoint->x) / ($rightPoint->x - $leftPoint->x),
    );
}

No surprises:

Finally, for the remainder of the pixels, we won't be able to do a simple lerp function (which only works in one dimension). We'll have to use Bilinear Interpolation: we'll first make two lerp values for both x-axes, and then one final lerp value for the y-axis. We'll also need four edge points instead of two, because these pixels aren't aligned with our lattice.

if ($point->x % 10 === 0 && $point->y % 10 === 0) {
    // …
} elseif ($point->x % 10 === 0) {
    // …
} elseif ($point->y % 10 === 0) {
    // …
} else {
    $topLeftPoint = new Point(
        x: (floor($point->x / 10) * 10),
        y: (floor($point->y / 10) * 10),
    );

    $topRightPoint = new Point(
        x: (ceil($point->x / 10) * 10),
        y: (floor($point->y / 10) * 10),
    );

    $bottomLeftPoint = new Point(
        x: (floor($point->x / 10) * 10),
        y: (ceil($point->y / 10) * 10)
    );

    $bottomRightPoint = new Point(
        x: (ceil($point->x / 10) * 10),
        y: (ceil($point->y / 10) * 10)
    );

    $a = lerp(
        $this->hash($topLeftPoint),
        $this->hash($topRightPoint),
        ($point->x - $topLeftPoint->x) / ($topRightPoint->x - $topLeftPoint->x),
    );

    $b = lerp(
        $this->hash($bottomLeftPoint),
        $this->hash($bottomRightPoint),
        ($point->x - $bottomLeftPoint->x) / ($bottomRightPoint->x - $bottomLeftPoint->x),
    );

    $noise = lerp(
        $a,
        $b,
        ($point->y - $topLeftPoint->y) / ($bottomLeftPoint->y - $topLeftPoint->y),
    );
}

Note that there's some repetition in our code that we could get rid of. But I prefer explicitly making all four edge points for clarity. Take a look at the result though:

This looks much smoother! Let's apply our colours:

Hm. You can probably see where we're going, but I still think these lines are far too… rough. Luckily, there are two more tricks we can apply! First, instead of using a plain lerp function, we can apply a so-called "shaping function" to our fraction. With this shaping function, we can manipulate our fraction before passing it to the lerp function. By default, our fraction will have a linear value — it's the distance from any given point to the starting edge:

But by applying a function to our fraction, we can manipulate it so that the values closer to the edges are even more smooth.

We could use whatever function we'd want. For our case I'll use a shaping function called smoothstep, which smooths edges.

function smooth(float $a, float $b, float $fraction): float
{
    $smoothstep = function (float $fraction): float {
        $v1 = $fraction * $fraction;
        $v2 = 1.0  - (1.0 - $fraction) * (1.0 -$fraction);

        return lerp($v1, $v2, $fraction);
    };

    return lerp($a, $b, $smoothstep($fraction));
}

The difference is subtle, but it's a little bit better.

The second trick is to apply a new layer of noise. This one shouldn't be as random as our first one though. We'll use a simple circular pattern, and apply it as a height map on our existing noise. The further a pixel is from the center, the smaller its value:

private function circularNoise(int $totalWidth, int $totalHeight, Point $point): float
{
    $middleX = $totalWidth / 2;
    $middleY = $totalHeight / 2;

    $distanceFromMiddle = sqrt(
        pow(($point->x - $middleX), 2)
        + pow(($point->y - $middleY), 2)
    );

    $maxDistanceFromMiddle = sqrt(
        pow(($totalWidth - $middleX), 2)
        + pow(($totalHeight - $middleY), 2)
    );

    return 1 - ($distanceFromMiddle / $maxDistanceFromMiddle) + 0.3;
}

This is the pattern on its own:

And now we combine this pattern with our existing noise, which is as easy as multiplying them:

final readonly class Noise
{
    public function __construct(
        private int $seed,
    ) {}
    
    public function generate(Point $point): float
    {
        return $this->baseNoise($point) 
             * $this->circularNoise($point);
    }
}

Here's the result:

This looks a lot better! Thanks to our circular pattern, the middle portion of our map is raised, while the outer portions are lowered. It creates a neat island look. Let's try out some seeds to see the difference between them:

Pretty nice! But we're far from done: we'll want to add different areas on our map: forests, plains, mountains, vegetation, …. Simply using a match in our drawPixel method won't suffice anymore.

# Improved drawing

Let's make an interface Biome, which will determine our pixel color, and can determine what kind of vegetation should be added. We'll also represent pixels as a proper value object.

interface Biome
{
    public function getPixelColor(Pixel $pixel): string;
}

Let's add seas and plains first.

final readonly class SeaBiome implements Biome
{
    public function getPixelColor(Pixel $pixel): string
    {
        $base = $pixel->value;

        while ($base < 0.25) {
            $base += 0.01;
        }

        $r = hex($base / 3);
        $g = hex($base / 3);
        $b = hex($base);

        return "#{$r}{$g}{$b}";
    }
}

final readonly class PlainsBiome implements Biome
{
    public function getPixelColor(Pixel $pixel): string
    {
        $g = hex($pixel->value);
        $b = hex($pixel->value / 4);

        return "#00{$g}{$b}";
    }
}

Depending on a pixel's biome, we'll use its noise to generate a different kind of color. In our drawPixel function, we can now make some changes:

function drawPixel(Pixel $pixel): string
{
    $biome = BiomeFactory::for($pixel);
    
    $color = $biome->getPixelColor($pixel);
    
    return <<<HTML
    <div style="
        --x: {$x}; 
        --y: {$y};
        --pixel-color: {$color};
    "></div>
    HTML;
}

For now, our BiomeFactory will only take a pixel's value into account to determine the biome. We could add other conditions later.

final readonly class BiomeFactory
{
    public static function for(Pixel $pixel): Biome
    {
        return match(true) {
            $pixel->value < 0.6 => new SeaBiome(),
            default => new PlainsBiome(),
        };
    }
}

It still works:

Let's go ahead and add all biomes now:

final readonly class BiomeFactory
{
    public static function make(Pixel $pixel): Biome
    {
        return match(true) {
            $pixel->value < 0.4 => new SeaBiome(),
            $pixel->value >= 0.4 && $pixel->value < 0.44 => new BeachBiome(),
            $pixel->value >= 0.6 && $pixel->value < 0.8 => new ForestBiome(),
            $pixel->value >= 0.8 => new MountainBiome(),
            default => new PlainsBiome(),
        };
    }
}

Note that I also decided to change the sea level: from 0.6 to 0.4, so the map looks a bit different now. Take a look:

Not bad, right? But this is far from a finished game — in fact, this is only the very first step: we'll need a way of interacting with this map, we'll need to define some form of gameplay. Maybe you remember the intro? I mentioned resource gathering. I imagine this game to be some kind of cookie-clicker style game: the more resources you gather, the more you can advance in the tech tree, the more resources you can gather, …

Anyway, I've done a lot more work on this game already: I actually started this project as an experiment to explore the limits of Laravel Livewire, so interactivity and gameplay were the primary focus. I've explained the basic concepts already on my YouTube channel in two videos. I'm also working on a third video where I discuss how I added interaction to this particular game board, as well as the problems I ran in to (there are a bunch of caveats I haven't mentioned yet in this blog post).

So, if you want to follow along, make sure to subscribe on YouTube — I hope to make the third and final video of this series soon. Alternatively, you can subscribe to my mailing list, where I'll send you an update as well.

In the meantime, you can watch the first two parts of this series; and don't forget to subscribe!

]]>
2023-05-30T00:00:00+00:00
<![CDATA[ Things considered harmful ]]> https://www.stitcher.io/blog/things-considered-harmful Someone on the internet shared their concerns about Laravel. They made a post on /r/php, a subreddit I moderate. They titled it "Laravel considered harmful", and currently it's the fifth most upvoted post of all-time on /r/php. We can talk all day about the arguments this person is making against Laravel — I agree with a handful of them, and disagree with another handful. But I'll let you join the discussion on Reddit, if you want to.

I do want to write about the underlying mindset of these kinds of posts, and of the people upvoting — that mindset of calling something "harmful". I can understand that someone doesn't like a framework, an ecosystem, the community or some individuals of that community. But calling something "harmful"? As if it's proven that Laravel hurts? Hurts what exactly? The PHP community? PHP's reputation? Laravel's competing frameworks? A programmer's "best practices"?

For what it's worth, I'd say that Laravel has done mostly good to the PHP community: it seems that a lot of people are able to make a living thanks to it; it seems that Laravel is widely known and praised within the programming community; in what way could it be considered "harmful"?

"It creates unmaintainable projects and doesn't teach best practices" — people say.

The argument of "best practices" baffles me. What defines a "best practice"? Aren't "best practices" meant to help you in building successful software? And hasn't Laravel proven to be able to do exactly that: build successful software? Shouldn't our assumptions of what defines a "best practice" be redefined if the most popular PHP framework doesn't seem to fit those assumptions?

Unmaintainable? To what extent? There are huge projects running Laravel in production — they have been for years? Some people will point towards failed Laravel projects, trying to prove their point. Well, let me show you success stories. Now what? Let me show you some failed Symfony projects. Who's right? Who's wrong?

This kind of mindset isn't uncommon in the programming world: we're used to doing things a certain way, and we're comfortable doing so. Whenever something comes along that doesn't fit our way of doing things, we experience friction. However, instead of questioning ourselves, too often do we point towards the thing that's causing friction, blaming it for everything that's wrong in the world. The safety of the internet makes it even more profound: we can write whatever we want with little to no consequences.

I would say that the inability to self-reflect, to adapt, and to question ourselves, is probably way more harmful than Laravel will ever be.

]]>
2023-05-02T00:00:00+00:00
<![CDATA[ Limited by committee ]]> https://www.stitcher.io/blog/limited-by-committee Camels are weird. Granted, they do manage to survive in some of the harshest environments on our planet, so, obviously, they are doing something right. But, I mean, look at them:

It looks like someone combined a horse with a giraffe, and kept adding stuff until they met the requirement of "being able to survive in the desert". Who came up with that? Don't get me wrong: camels do work, so they must have gotten something right. But honestly, the finishing touches are far from ideal:

PHP is like a camel: there's little to say when it comes to beauty or elegance, but it does seem to survive many harsh conditions. So, who am I to judge? Coincidentally, PHP was originally inspired by PERL, whose logo is a camel; so maybe there is a deeper connection still? But let's not go too deep down the camel rabbit hole; instead, let's talk about how programming languages are designed.

PHP is one of those programming languages that's not backed by a big company or a group of people whose job it is to work on it. PHP is mostly driven by a group of volunteers. A year ago, we got the PHP Foundation which employs a handful of developers to work on the language, but it's not like they have any ownership. They have to submit their ideas, and are at the mercy of "PHP Internals" to decide what goes in the language, and what not.

So about that group of volunteers: PHP Internals are a group of people who discuss and vote on what gets added into the language. Part of that group consists of core developers, and then there are some prominent PHP community members, release managers, past contributors, docs maintainers, and probably a bunch of other people. We're talking somewhere around 200 members — probably, but there aren't any public numbers, as far as I know.

That group is the perfect example of a committee: they all come together to decide on how PHP should evolve next. They don't have a unified plan or vision, but all of them bring their own agenda. While a core programmer might favor cleaning up PHP's internal code (even when it brings some breaking changes), a prominent community member might push forward ideas that help with the development of a PHP framework or package.

This phenomenon — designing a language with such a large group — is called "design by committee". At first glance, it might sound like a very democratic approach to software design where everyone can pitch an idea, and if enough people vote for it, it gets added. However, because there's such a large diversity of opinions, a committee rarely achieves excellence.

Say you want to add a feature in PHP that benefits you — a prominent framework developer. Then you'll have to convince enough people to vote for it. First, you request Internals to share their comments on your idea — an RFC (request for comments). Naturally, you'll make compromises, trying to get as many people on your side as possible before voting starts. On top of that: people can lobby others to influence their vote and, in the end, the result. Suddenly you're playing a game of politics, instead of proper software design.

And so, design by committee often leads to average results at best; or (in many cases) no result, because people couldn't find consensus. Camels were probably designed by a committee as well. Sure, they work, but they are far from excellence and full of compromise.

The alternative to design by committee is called "design by dictator" or having a "benevolent dictator for life". It might sound rather negative at first, but hear me out. A benevolent dictator (or group of benevolent dictators) don't want to push their own agenda; they want the best for their product. It's their product — they have full ownership. And exactly because it is their product, they will listen to "the masses". Because, their product is nothing, without their people.

In the end though, a dictator will make decisions, even when those decisions aren't agreed upon within the community as a whole. There's much less need for compromise, and so, more room for excellence.

Maybe you are skeptical of the idea? Well, don't take my word for it, there are quite a lot of open source projects applying this technique. You might even recognise a couple:

  • Rust
  • Laravel
  • Ruby
  • Linux
  • Ruby on Rails
  • Ubuntu
  • Vue.js
  • And more

I think it's fair to say that all of these projects are a huge success. I believe much of their success comes from that one person in charge, leading the way, having a clear vision. These leaders still listen to their audience, and they often allow for voting on features. But, in the end, there's one person carrying the ownership. There's one leader to look up to.

When people trust a benevolent dictator, much more becomes possible than when they settle with a committee.

PHP will never change its leadership model; the committee would have to give up its power which, of course, they don't want to. I think this is one of the reasons I'm excited about the idea of a superset for PHP: it could be an independent project, led by one person or a company, regardless of what the committee wants. If that person or company gained my trust, I would be fine with them make the decisions to move the language forward, rather than settling with average.

]]>
2023-03-22T00:00:00+00:00
<![CDATA[ Cloning readonly properties in PHP 8.3 ]]> https://www.stitcher.io/blog/cloning-readonly-properties-in-php-83 PHP 8.3 adds the possibility of overwriting readonly property values while cloning an object. Don't be mistaken though: you're not able to clone any object and overwrite their readonly values from any place. This feature only addresses a very specific (but important) edge case.

Let's take a look!

In an ideal world, we'd be able to clone classes with readonly properties, based on a user-defined set of values. The so called clone with syntax (which doesn't exist):

readonly class Post
{
    public function __construct(
        public string $title,
        public string $author,
        public DateTime $createdAt,
    ) {}
}

$post = new Post(
    title: 'Hello World',
    // …
);

 // This isn't possible!
$updatedPost = clone $post with {
    title: 'Another One!',
};

Reading the title of the current RFC: "Readonly properties can be reinitialized during cloning" — you might think something like clone with this is now possible. However… it isn't. The RFC allows only allows for one specific operation: to overwrite readonly values in the magic __clone method:

readonly class Post
{
    public function __construct(
        public string $title,
        public string $author,
        public DateTime $createdAt,
    ) {}
    
    public function __clone()
    {
        $this->createdAt = new DateTime(); 
        // This is allowed,
        // even though `createdAt` is a readonly property.
    }
}

Is this useful? It is! Say you want to clone objects with nested objects — a.k.a. making "deep clones"; then this RFC allows you to clone those nested objects as well, and overwrite them in your newly created clone — even when they are readonly properties.

readonly class Post
{
    public function __clone()
    {
        $this->createdAt = clone $this->createdAt; 
        // Creates a new DateTime object,
        // instead of reusing the reference
    }
}

Without this RFC, you'd be able to clone $post, but it would still hold a reference to the original $createdAt object. Say you'd make changes to that object (which is possible since readonly only prevents the property assigned from changing, not from its inner values being changed):

$post = new Post(/* … */);

$otherPost = clone $post;

$post->createdAt->add(new DateInterval('P1D'));

$otherPost->createdAt === $post->createdAt; // true :(

Then you'd end up with the $createdAt date changed on both objects!

Thanks to this RFC, we can make real clones, with all their nested properties cloned as well, even when these properties are readonly:

$post = new Post(/* … */);

$otherPost = clone $post;

$post->createdAt->add(new DateInterval('P1D'));

$otherPost->createdAt === $post->createdAt; // false :)

# On a personal note

I think it's good that PHP 8.3 makes it possible deep cloning readonly properties. However, I have mixed feelings about this implementation. Imagine for a second that clone with existed in PHP, then all of the above would have been unnecessary. Take a look:

// Again, this isn't possible!
$updatedPost = clone $post with { 
    createdAt: clone $post->createdAt,
};

Now imagine that clone with gets added in PHP 8.4 — pure speculation, of course. It means we'd have two ways of doing the same thing in PHP. I don't know about you, but I don't like it when languages or frameworks offer several ways of doing the same thing. As far as I'm concerned, that's suboptimal language design at best.

This is, of course, assuming that clone with would be able to automatically map values to properties without the need of manually implementing mapping logic in __clone. I'm also assuming that clone with can deal with property visibility: only able to change public properties from the outside, but able to change protected and private properties when used inside a class.

Awhile ago, I wrote about how PHP internals seem to be divided, one group comes up with one solution, while another group wants to take another approach. To me, it's a clear drawback of designing by committee.

Full disclosure — the RFC mentions clone with as a future scope:

None of the envisioned ideas for the future collide with the proposals in this RFC. They could thus be considered separately later on.

But I tend to disagree with this statement, at least assuming that clone with would work without having to implement any userland code. If we'd follow the trend of this current RFC, I could imagine someone suggesting to add clone with only as a way to pass data into __clone, and have users deal with it themselves:

readonly class Post
{
    public function __clone(...$properties)
    {
        foreach ($properties as $name => $value) {
            $this->$name = $value;
        }
    }
}

However, I really hope this isn't the way clone with gets implemented; because you'd have to add a __clone implementation on every readonly class.

So, assuming the best case where clone with gets added, and where it is able to automatically map values; then the functionality of this current RFC gets voided, and makes it so that we have two ways of doing the same thing. It will confuse users because it faces them with yet another a decision when coding. I think PHP has grown confusing enough as is, and I'd like to see that change.

On the other hand, I do want to mention that I don't oppose this RFC on its own. I think Nicolas and Máté did a great job coming up with a solid solution to a real-life problem.


PS: in case someone wants to make the argument for the current RFC because you only need to implement __clone once per object and not worry about it on call-site anymore. There's one very important details missing from these examples in isolation: deep copying doesn't happen with a simple clone call. Most of the time, packages like deep-copy are used, and thus, the potential overhead that comes with my clone with example is already taken care off by those packages and don't bother end users.

]]>
2023-03-20T00:00:00+00:00
<![CDATA[ Thank you, Kinsta ]]> https://www.stitcher.io/blog/thank-you-kinsta A couple of weeks ago, I shared a bit about the financial side of running stitcher.io. I explained how I would like stitcher.io to be break-even after a large sponsor deal had ended.

Today I want to thank Kinsta in particular for stepping in and helping out! They've committed to a generous monthly sponsorship, which is getting me closer to achieving that break-even goal.

If you haven't heard of Kinsta before, they are a cloud platform targeted towards companies and dev teams to help them ship and manage their web projects. What I like about companies like Kinsta is that they are always on the lookout for helping out the community members like they did with my sponsor issue. They contacted me out of the blue saying they wanted to help, which I really appreciate!

So big thanks to Kinsta for helping me work on stitcher.io in my free time!

]]>
2023-03-15T00:00:00+00:00
<![CDATA[ Slashdash ]]> https://www.stitcher.io/blog/slashdash A year or so ago, I stumbled upon a website for yet another document language similar to YAML, JSON or XML; it was called KDL. I don't know what became of it, but it had one little feature that stood out: something they called "slashdash comments": /-. You can use it to mark code as comments, but it is scope aware.

For example, you can use it to comment out individual keywords and statements like final or implements X:

/-final class Foo /-implements Bar

But you could also use the /-- variant to comment out a whole section based on its scope:

/--public function fromInterface(int $i): int {
    return $i + 1;
}

It's just a small thing, but when I saw it, it immediately clicked. Especially in a language like PHP where "dump and die debugging" is the de-facto standard, it would be nice to have a slightly shorter and more convenient way to comment out code.

/-final class Foo /-implements Bar
{
    public /-readonly string $prop;
    
    /--public function fromInterface(int $i): int {
        return $i + 1;
    }
}

]]>
2023-02-20T00:00:00+00:00
<![CDATA[ I'm a light schemer ]]> https://www.stitcher.io/blog/light-colour-schemes

I’ve been laughed at and ridiculed. But don’t worry, I won’t give in. I will keep using a light colour scheme.

I know I’m a minority, and people do ask me why I keep using light colours schemes. Well, I’ve got three reasons to do so, and I think they make a pretty good point for everyone to switch.

First, people have been doing research into text readability. Studies show that our eyes can more easily distinguish a dark foreground from a light background, than the other way around. I will leave some links in the footnotes if you want to read about it. I find it a compelling first reason for light colour schemes: they are easier to read.

Second: the majority of websites and UIs are still made with a light colour scheme first, so switching between dark code and light applications becomes tiring after a while, especially if you’re doing it day in, day out. Granted, more and more applications support dark mode, but then we’re back to reason one: they are more difficult to read.

Third, and this is probably the thing most people think about: there’s this misconception that “light colour schemes” hurt your eyes, especially in dark rooms. Let me tell you: sitting in an all dark room programming isn’t good for your eyes anyways, regardless of the colour scheme you’re using. If you feel that a light colour scheme hurts your eyes, it might be better to lower the brightness of your screen and make sure your room is properly lit because — a dark theme isn’t the solution.

When I first learned about the real arguments for light colour schemes, I couldn’t do anything else but to use them. I’ve got decades to go in front of a screen, and I really need to do all I can to make it as easy as possible for my eyes.

Here’s my challenge: use a light colour scheme for a week, but do it in a properly lit room, and with proper screen brightness. Come back here after a week, and let me know your thoughts.

]]>
2023-02-10T00:00:00+00:00
<![CDATA[ I'm a code folder ]]> https://www.stitcher.io/blog/code-folding

I know it looks strange the first time you see it, but hear me out for a minute: I am a code folder.

class TwitterSyncCommand extends Command
{
    protected $signature = 'twitter:sync {--clean}';

    /** @var \Illuminate\Database\Eloquent\Collection<Mute> */
    private Collection $mutes;

    public function handle(Twitter $twitter) { /* … */ }

    public function syncFromList(Twitter $twitter): void { /* … */ }

    public function syncFromSearch(Twitter $twitter): void { /* … */ }

    private function storeTweets(array $tweets, TweetFeedType $feedType): void { /* … */ }

    private function shouldBeRejected(Tweet $tweet): ?RejectionReason { /* … */ }
}

I hide most of my code, most of the time. I have keyboard shortcuts to easily show and hide blocks of code; and when I open a file, all method and function bodies are collapsed by default.

The reason? I’m not a superhuman speed reader that understands dozens of lines of code at one glance. And… I also don’t have a two-metre-high screen.

I just can’t read and understand all of this — you know?

class TwitterSyncCommand extends Command
{
    protected $signature = 'twitter:sync {--clean}';

    /** @var \Illuminate\Database\Eloquent\Collection<Mute> */
    private Collection $mutes;

    public function handle(Twitter $twitter)
    {
        $this->mutes = Mute::query()->select('text')->get();

        if ($this->option('clean')) {
            $this->error('Truncating tweets!');

            Tweet::truncate();
        }

        $this->syncFromSearch($twitter);

        $this->syncFromList($twitter);

        $this->info('Done');
    }

    public function syncFromList(Twitter $twitter): void
    {
        do {
            $lastTweet = Tweet::query()
                ->where('feed_type', TweetFeedType::LIST)
                ->orderByDesc('tweet_id')
                ->first();

            $tweets = $twitter->request('lists/statuses.json', 'GET', [
                'list_id' => config('services.twitter.list_id'),
                'since_id' => $lastTweet?->tweet_id,
                'count' => 200,
                'tweet_mode' => 'extended',
            ]);

            $count = count($tweets);

            if ($count === 0) {
                $this->comment('No more new tweets');
            } else {
                $this->comment("Syncing {$count} tweets from list");

                $this->storeTweets($tweets, TweetFeedType::LIST);
            }
        } while ($tweets !== []);
    }

    public function syncFromSearch(Twitter $twitter): void
    {
        do {
            $lastTweet = Tweet::query()
                ->where('feed_type', TweetFeedType::SEARCH)
                ->orderByDesc('tweet_id')
                ->first();

            $tweets = $twitter->request('/search/tweets.json', 'GET', [
                'q' => 'phpstorm',
                'since_id' => $lastTweet?->tweet_id,
                'count' => 200,
                'tweet_mode' => 'extended',
            ])->statuses;

            $count = count($tweets);

            if ($count === 0) {
                $this->comment('No more new tweets');
            } else {
                $this->comment("Syncing {$count} tweets from search");

                $this->storeTweets($tweets, TweetFeedType::SEARCH);
            }
        } while ($tweets !== []);
    }

    private function storeTweets(array $tweets, TweetFeedType $feedType): void
    {
        foreach ($tweets as $tweet) {
            $subject = $tweet->retweeted_status ?? $tweet;

            $tweet = Tweet::updateOrCreate([
                'tweet_id' => $tweet->id,
            ], [
                'state' => TweetState::PENDING,
                'feed_type' => $feedType,
                'text' => $subject->full_text ,
                'user_name' => $subject->user->screen_name,
                'retweeted_by_user_name' => isset($tweet->retweeted_status)
                    /** @phpstan-ignore-next-line  */
                    ? $tweet->user->screen_name
                    : null,
                'created_at' => Carbon::make($subject->created_at),
                'payload' => json_encode($tweet),
            ]);

            if ($reason = $this->shouldBeRejected($tweet)) {
                $tweet->update([
                    'state' => TweetState::REJECTED,
                    'rejection_reason' => $reason->message,
                ]);
            }

            (new ParseTweetText)($tweet);
        }
    }

    private function shouldBeRejected(Tweet $tweet): ?RejectionReason
    {
        if ($tweet->isRetweet() && $tweet->feed_type === TweetFeedType::SEARCH) {
            return RejectionReason::retweetedFromSearch();
        }

        // Reject tweets containing a specific word
        foreach ($this->mutes as $mute) {
            if ($tweet->containsPhrase($mute->text)) {
                return RejectionReason::mute($mute->text);
            }
        }

        // Reject replies
        if ($tweet->getPayload()->in_reply_to_status_id) {
            return RejectionReason::isReply();
        }

        // Reject mentions
        if (str_starts_with($tweet->text, '@')) {
            return RejectionReason::isMention();
        }

        // Reject non-english tweets
        $language = $tweet->getPayload()->lang;

        if ($language !== 'en') {
            return RejectionReason::otherLanguage($language);
        }

        return null;
    }
}

I feel overwhelmed looking at all that code, especially when I’m searching for one specific method.

Now, when I create a new file, it’s all fine, it’s a blank slate, and I’ve got control. But code grows rapidly, and real life projects more often than not include work in existing files instead of blank slates. So I really need a way to reduce cognitive load, I can’t “take it all in at once”. And that’s why I’ve grown to like code folding so much.

Give yourself a week to get used to it. Configure your IDE to automatically fold method bodies and functions, and assign proper key bindings to show and hide blocks. I promise you, you’ll like it.

]]>
2023-02-06T00:00:00+00:00
<![CDATA[ Acronyms ]]> https://www.stitcher.io/blog/acronyms I'm suspicious of acronyms. Think about it: is it a coincidence that the five most important principles in programming just happen to line up to form the word "SOLID"?

I'm suspicious of acronyms because there's always someone, somewhere, making compromises to make them work. Whether it is by omitting equally (or more) important elements because they don't fit the acronym; whether less important elements are included to make it work; or whether names are changed, their meaning obfuscated, so that they'd fit perfectly.

Acronyms are of course a great choice to market your ideas because, well, people tend to remember them. SOLID is the perfect example.

]]>
2023-01-29T00:00:00+00:00
<![CDATA[ Fonts matter ]]> https://www.stitcher.io/blog/fonts-matter .fonts-01 code { font-size: 12px; line-height: 1.1em; font-family: "Courier New", monospace; } .fonts-02 code { line-height: 1.1em; font-family: "Courier New", monospace; } .fonts-03 code { line-height: 1.2em; }

I like to think of my code as a book. Not just any book, I think of it as a precious, beautifully designed work of art. Something I want to WANT to read. You know why? Because programming is so much more about reading and understanding code, than it is about writing.

I would say that “writing code” is only the lesser part of my programming life. So naturally, I have much to gain by making the “reading part” as pleasant as possible.

So, let's work from an example:

final class CodeController
{
    public function __construct(private MarkdownConverter $markdown) {}

    public function __invoke(string $slug)
    {
        $code = $this->markdown->convert(file_get_contents(__DIR__ . "/code/{$slug}.md"))->getContent();

        return view('code', [
            'code' => $code,
        ]);
    }
}

First things first, I choose a large font. My brain can only read so many characters per second, so I don’t need to try and fit as much code as possible on screen, at all times.

final class CodeController
{
    public function __construct(private MarkdownConverter $markdown) {}

    public function __invoke(string $slug)
    {
        $code = $this->markdown->convert(file_get_contents(__DIR__ . "/code/{$slug}.md"))->getContent();

        return view('code', [
            'code' => $code,
        ]);
    }
}

I choose a font that’s pleasant to read, modern fonts suit me better than the ones that originated back in the 80s or 90s.

final class CodeController
{
    public function __construct(private MarkdownConverter $markdown) {}

    public function __invoke(string $slug)
    {
        $code = $this->markdown->convert(file_get_contents(__DIR__ . "/code/{$slug}.md"))->getContent();

        return view('code', [
            'code' => $code,
        ]);
    }
}

I increase the line height, because it gives my code some room to breathe, and makes it even easier to read.

final class CodeController
{
    public function __construct(private MarkdownConverter $markdown) {}

    public function __invoke(string $slug)
    {
        $code = $this->markdown->convert(file_get_contents(__DIR__ . "/code/{$slug}.md"))->getContent();

        return view('code', [
            'code' => $code,
        ]);
    }
}

Finally, I make sure that my code isn’t too wide. The less I need to move my eyes from left to right, the easier it is.

final class CodeController
{
    public function __construct(
        private MarkdownConverter $markdown,
    ) {}

    public function __invoke(string $slug)
    {
        $path = file_get_contents(__DIR__ . "/code/{$slug}.md");
        
        $code = $this->markdown
            ->convert($path)
            ->getContent();

        return view('code', [
            'code' => $code,
        ]);
    }
}

Looking at typography guidelines, the maximum advised length is somewhere between 60 and 80 characters. I think somewhere between 80 and 100 works well, because code also includes lots of tabs.

Have you considered typography when programming? Give it a try, it’ll make a lasting impression.

]]>
2023-01-28T00:00:00+00:00
<![CDATA[ My top-10 favourite functions in PHP ]]> https://www.stitcher.io/blog/my-10-favourite-php-functions More than once, I've been amazed by what's actually built-into PHP. Here are some of my personal favourite functions.

# Levenshtein

"Levenshtein" is the name of an algorithm to determine the difference — aka "distance" — between two strings. The name comes — unsurprisingly — from its inventor: Vladimir Levenshtein.

It's a pretty cool function to determine how similar two related words or phrases are. For example: passing in "PHP is awesome" twice, will result in a "distance" of 0:

levenshtein("PHP is awesome", "PHP is awesome"); // 0

However, passing in two different phrases will result in a larger distance:

levenshtein("Dark colour schemes", "are awesome"); // 13

Unsurprisingly, given how incompatible above two statements are 😉

# Easter dates

PHP has — believe it or not — a built-in function to determine the date of Easter for any given year. Given that Easter's date is determined by "the first Sunday after the full Moon that occurs on or after the spring equinox", I'm in awe of PHP being able to calculate it for me.

Or maybe it simply is hard coded?

date('Y-m-d', easter_date(2023)); // 2023-04-08

# Forks

Did you know PHP can be async? CLI versions of PHP have access to the pcntl functions, including the pcntl_fork function. This function is basically a wrapper for creating process forks, allowing one PHP process to spawn and manage several!

Here's a simple example using sockets to create an async child process in PHP:

function async(Process $process): Process {
    socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets);
    [$parentSocket, $childSocket] = $sockets;

    if (($pid = pcntl_fork()) == 0) {
        socket_close($childSocket);
        socket_write($parentSocket, serialize($process->execute()));
        socket_close($parentSocket);
        exit;
    }

    socket_close($parentSocket);

    return $process
        ->setStartTime(time())
        ->setPid($pid)
        ->setSocket($childSocket);
}

I actually wrote a little package that wraps everything in an easy-to-use API: spatie/async.

# Metaphone?

Similar to levenshtein, methaphone can generate a phonetic representation of a given string:

metaphone("Light color schemes!"); // LFTKLRSXMS
metaphone("Light colour schemes!"); // LFTKLRSXMS

# Built-in DNS

PHP understands DNS, apparently. It has a built-in function called dns_get_record, which does as its name implies: it gets a DNS record.

dns_get_record("stitcher.io");

{
    ["host"] => "stitcher.io"
    ["class"] => "IN"
    ["ttl"] => 539
    ["type"] => "NS"
    ["target"] => "ns1.ichtushosting.com"
}

// …

# Recursive array merging

I mainly wanted to include array_merge_recursive because, for a long time, I misunderstood what it did. I used to think you'd have to use it for merging multidimensional arrays, but that's not true!

It might be better to let past-me explain it but, in summary, it works like this:

$first = [
    'key' => 'original'
];

$second = [
    'key' => 'override'
];

array_merge_recursive($first, $second);

{
    ["key"] => {
        "original",
        "override",
    }
}

# Mail

PHP has a mail function. A function to send mail. I wouldn't use it, but it's there:

mail(
    string $to,
    string $subject,
    string $message,
    array|string $additional_headers = [],
    string $additional_params = ""
): bool

# DL

Apparently, there's a function in PHP that allows you to dynamically load extensions, while your script is running!

if (! extension_loaded('sqlite')) {
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        dl('php_sqlite.dll');
    } else {
        dl('sqlite.so');
    }
}

# Blob… I mean glob

glob is a seriously awesome function: it finds pathnames according to a pattern. It's pretty easy to explain, but it's oh so useful:

glob(__DIR__ . '/content/blog/*.md');
glob(__DIR__ . '/content/*/*.md');

{
    /path/to/content/blog/foo.md,
    /path/to/content/other/bar.md,
    …
}

# Sun info

Finally, PHP not only knows about Easter, it also knows about when the sun rises and sets, for any given date! It also requires a longitude and latitude, which of course makes sense because the sunrise and sunset times depend on your location:

date_sun_info(
    timestamp: strtotime('2023-01-27'), 
    latitude: 50.278809, 
    longitude: 4.286095,
)

{
  ["sunrise"] => 1674804140
  ["sunset"] => 1674836923
  ["transit"] => 1674820532
  ["civil_twilight_begin"] => 1674802111
  ["civil_twilight_end"] => 1674838952
  ["nautical_twilight_begin"] => 1674799738
  ["nautical_twilight_end"] => 1674841325
  ["astronomical_twilight_begin"] => 1674797441
  ["astronomical_twilight_end"] => 1674843622
}

What's your favourite PHP function? Let me know on Twitter!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2023-01-27T00:00:00+00:00
<![CDATA[ Why curly brackets go on new lines ]]> https://www.stitcher.io/blog/why-curly-brackets-go-on-new-lines

This is a curly bracket, or brace: {.

It’s rarely used as a punctuation mark, but it is one of the most used symbols in programming languages, where they are used to group code and create scopes. It’s also one of the more debated topics when it comes to code styles.

The question is simple: should an opening brace go on a new line or not? You might think: it’s all personal preference; but I would say: it’s not.

Take a look at this code snippet:

public function __construct(string $publicDirectory, string $configurationFile, PageParser $pageParser, PageRenderer $pageRenderer) {
    // ...
}

It’s a constructor that takes a couple of arguments. So what's wrong with this code? Well first of all, you probably have to scroll sideways to read it. That's a bad thing. Scrolling requires an extra interaction with your code. You'll have to consciously search for information about the arguments of this method. That time distracts you from focusing on the application code.

Second, if you're into web development, you probably know that people don't read text, they scan. Usually from left to right and top to bottom. This is especially true for websites, but the same goes for reading code. Putting important information to the right makes it more difficult to find.

In case of this argument list, all arguments are equally important; yet a lot of useful information is pushed to that right, blurry side, where our focus isn’t by default.

So how do we pull useful information more to the left?

public function __construct(string $publicDirectory,
                            string $configurationFile,
                            PageParser $pageParser,
                            PageRenderer $pageRenderer) {
    // ...
}

This could be the first solution you think about. But it doesn't really scale. As soon as you're refactoring a method name, the alignment breaks. Say we want to make this a static constructor instead of a normal one.

public static function create(string $publicDirectory,
                            string $configurationFile,
                            PageParser $pageParser,
                            PageRenderer $pageRenderer) {
    // ...
}

See the alignment breaking?

Another issue is that arguments are still pushed rather far to the right; so let's take a look at another approach.

public function __construct(
    string $publicDirectory, string $configurationFile,
    PageParser $pageParser, PageRenderer $pageRenderer) {
    // ...
}

The advantage here is that our alignment issue is solved. However, how will you decide how many arguments should go on one line? Will you make some styling guidelines about this? How will you enforce them? This example has four arguments, but what if it had three or five?

public function __construct(
    string $publicDirectory, string $configurationFile, 
    string $cachePath, PageParser $pageParser, 
    PageRenderer $pageRenderer) {
    // ...
}

Consistency is key. If we can find a consistent rule, we don't have to think about it anymore. And like I said before, if we don't have to think about it, there's room in our heads for more important things.

So let's continue our search for consistency.

public function __construct(
    string $publicDirectory,
    string $configurationFile,
    PageParser $pageParser,
    PageRenderer $pageRenderer) {
    $this->publicDirectory = rtrim($publicDirectory, '/');
    $this->configurationFile = $configurationFile;
    $this->pageParser = $pageParser;
    $this->pageRenderer = $pageRenderer;
}

By giving each argument its own line, we solve all our problems. But we also created a new one: it's now more difficult to distinguish between the argument list and the method body.

I can illustrate it for you. Let's replace all characters in this code with X's:

XXXXXX XXXXXXXX __XXXXXXXXX(
    XXXXXX XXXXXXXXXXXXXXXX,
    XXXXXX XXXXXXXXXXXXXXXXXX,
    XXXXXXXXXX XXXXXXXXXXX,
    XXXXXXXXXXXX XXXXXXXXXXXXX) {
    XXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXXXXXXXXXXXXXX;
    XXXXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXXXX;
    XXXXXXXXXXXXXXXXX = XXXXXXXXXXX;
    XXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXX;
}

Can you see how difficult it becomes to spot where the argument list ends and the method body starts?

You might say "there's still the curly bracket on the right indicating the end". But that’s not where our focus is! We want to keep the important information to the left. How do we solve it? It turns out there is one true place where to put your curly brackets:

XXXXXX XXXXXXXX __XXXXXXXXX(
    XXXXXX XXXXXXXXXXXXXXXX,
    XXXXXX XXXXXXXXXXXXXXXXXX,
    XXXXXXXXXX XXXXXXXXXXX,
    XXXXXXXXXXXX XXXXXXXXXXXXX
) {
    XXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXXXXXXXXXXXXXX;
    XXXXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXXXX;
    XXXXXXXXXXXXXXXXX = XXXXXXXXXXX;
    XXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXX;
}

On a new line. Placing curly brackets on new lines gives our code space to breathe. It creates a visual boundary between argument lists and method bodies, it helps us to focus on things that matter.

public function __construct(
    string $publicDirectory,
    string $configurationFile,
    PageParser $pageParser,
    PageRenderer $pageRenderer
) {
    $this->publicDirectory = rtrim($publicDirectory, '/');
    $this->configurationFile = $configurationFile;
    $this->pageParser = $pageParser;
    $this->pageRenderer = $pageRenderer;
}

]]>
2023-01-24T00:00:00+00:00
<![CDATA[ Sponsors ]]> https://www.stitcher.io/blog/sponsors Hi! I usually don't write about the financial side of managing this blog and my newsletters, and yet, here we are. I must admit it feels a little awkward, so let's just get to the point first; I'll provide some backstory if you're interested in it as well.

I work on stitcher.io and my newsletters solely in my free time. I used to have a large company sponsoring me for two years, but that partnership has ended. I'm now looking into other ways to get stitcher.io to break even again, and one of the options is to try out GitHub sponsors. If you're a regular reader and if my content helps you, I kindly want to ask you to consider sponsoring me on GitHub.

Finally, if you're a company looking into dedicated ad placements on my blog or newsletters, you can contact me via email.

# Backstory

I've been working on stitcher.io as a hobby project for over five years now, and I've been enjoying it tremendously. In the beginning, stitcher.io was just a static blog that I hosted on a cheap $5 digital ocean droplet (in fact, my previous employer provided it for free, thanks Spatie!).

Over time though, costs began to grow. I got a long-term sponsor contract, meaning I had a couple of hundred euros extra monthly income. It also meant I was now legally required to start a side business in my name, which of course, increased my monthly costs: paying an accountant, social security fees, paying taxes on that newly created income, …

I also started sending out a regular newsletter, meaning I had to upscale my cheap droplet, had to deal with mailing providers, etc. I managed to always break even thanks to that long-term sponsor contract, but I always knew that if they decided to pull the plug, I'd have to search for other solutions. I am still running carbon ads, but honestly, they earn too little to rely on: $50 a month on average.

So here we are: I've got a monthly cost of around 250 euros for hosting, mailing, accounting, and software, which doesn't take into account any yearly or biyearly costs such as social security costs, taxes, domain names, and of course all the time I spend on it.

Now, to be clear: I don't want to turn my blog and newsletters into a profitable business. And I will keep working on it regardless of whether it's making any profit. Though, it would be nice to be able to break even again. I also did consider other ways to finance this project, like, for example, by writing a new book, but I have to admit that my family situation right now (2 kids, a third is on the way) doesn't permit such a time investment in something that I don't know will work.

So, long story short: stitcher.io right now is making a loss, I'm fine with it, but I would like to see it break even. I feel a bit awkward writing about it, but on the other hand: lots of other people do it, so I figured I'd give it a chance. If you benefit from my work and want to help out on a one-time or recurring basis, you can check out my GitHub Sponsors page. If you're a company looking into dedicated ad placements on my blog or newsletters, you can contact me via email.

Thanks!

]]>
2023-01-21T00:00:00+00:00
<![CDATA[ Tabs are better ]]> https://www.stitcher.io/blog/tabs-are-better

Do you want to know something dangerous?

Habits.

Not the kind where you practise long and hard to make something a habit. No, I’m talking about the kind where you’ve taught yourself something without noticing. The kind of habit that has been “there” for years, but you can’t remember how it came to be.

I’m not impeccable myself. I have several such habits, and I even have a couple that I know are wrong and should be changed.

For example: I use spaces instead of tabs for indentation.

We could talk about preference all day, but in the end we should ask: what are the rational arguments for tabs and spaces?

For tabs, there’s one big argument: accessibility. First and foremost, the size of a tab is configurable, it doesn’t have to be a fixed width of x spaces. Say there are two visually impaired people working on the same codebase. One person has to use a really large font size, so their tab length should be shorter than the default; and the other one prefers much larger tabs on a very wide monitor so that the difference between nested levels of code is more clear. Just like colour schemes or fonts, each developer can choose what style fits them best.

On top of that: blind people also code. They often use braille displays. These displays show a fixed number of characters, and every space wastes one. Say you’ve got 4 spaces per indentation level, and want to read something that’s nested 3 levels deep; then we’re talking about 12 braille characters being wasted on an already limited display. Compare that to only three when you’d use tabs.

But let’s not jump to early conclusions and look at the rational arguments for using spaces as well: Someone decided it would be the best. That’s it. “Consistency” is often coined as an argument because different editors might show different tab lengths, and some programmers… don’t like that?

A huge part of the programming community — including myself — has gotten used to spaces. Not because there are any good arguments for them, but because it has grown into a habit — a habit so big that popular code styles force users to write spaces, without any good reason.

So maybe we should rethink that habit? I would say that accessibility and writing more inclusive code is a very good argument. But unfortunately, as we all know: breaking habits is hard.

]]>
2023-01-20T00:00:00+00:00
<![CDATA[ PHP in 2023 ]]> https://www.stitcher.io/blog/php-in-2023 From its humble beginnings as a personal project in the mid-90s, PHP has grown to become one of the most popular languages for web development, powering everything from small blogs to large enterprise applications.

It's a language that has seen an astonishing transformation over the course of almost three decades. Even within the last 10 years, PHP has transformed in ways we couldn't imagine.

Every year, I write a post about the current state of PHP, where I look back and forward. Let's begin!

# The PHP Foundation

I usually start these posts with a summary of the latest PHP version, but this time I want focus on the PHP Foundation first. It's been a little over a year since the foundation was created: a collective of 10 volunteers and 6 developers being paid to work on PHP, the language.

Last year, I wrote this:

I'm a little worried now that Nikita has stepped down. He's definitely not the only person capable of working on PHP's core, but he did a tremendous amount of work these past years with PHP 8.0 and 8.1. I hope the PHP Foundation will be up to speed soon and that there are enough core developers who have time to work on PHP next year. PHP 8.2 is already in development, although there aren't many RFCs drafted yet.

I don't think 2022 will be the most mind blowing year for PHP, but rather a year of adding stability. Nothing wrong with that.

I think it's fair to say that the Foundation has delivered. They recently published their 2022 report, and it shows some pretty impressive numbers:

  • In total, $580,000 was raised in 2022
  • The Foundation pays 6 developers to work on PHP
  • Foundation members made almost half of all commits in php-src
  • They created 8 new RFCs, only one of those RFCs didn't make it

I think the Foundation is one of the best things to happen to PHP in a long time, and I hope they'll be able to improve the language even more in 2023. If you're working at a company that uses PHP, I'd highly recommend you consider donating.

# PHP 8.2

Moving on to PHP 8.2. It's generally regarded as a smaller release, but nevertheless it has a bunch of nice features. Just to name a couple:

Readonly classes:

readonly class PostData
{
    public function __construct(
        public string $title,
        public string $author,
        public string $body,
        public DateTimeImmutable $createdAt,
        public PostState $state,
    ) {}
}

A brand new randomizer:

$rng = $is_production
    ? new Random\Engine\Secure()
    : new Random\Engine\Mt19937(1234);
 
$randomizer = new Random\Randomizer($rng);

$randomizer->shuffleString('foobar');

Standalone null, true and false:

function alwaysFalse(): false
{
    return false;
}

Disjunctive normal form types:

function generateSlug((HasTitle&HasId)|null $post) 
{ /* … */ }

Redacted parameters:

function connect(
    string $user,
    #[\SensitiveParameter] string $password
) {
    // …
}

And more.

It's kind of crazy to realise how much PHP has evolved over the years. I made a little video comparison that clearly shows the difference:

# The ecosystem

Just like every year, I should mention Packagist, PHP's package manager: it now lists 361,000 packages; 60,000 more than last year:

One impressive number is the total amount of installations. Last year I mentioned this:

Oh, by the way, just recently Packagist passed the milestone of having handled over more than 50 billion installs. Congrats Packagist!

I just checked, and we're now at 74,492,061,634 installs. That's an increase of 24 billion installs in one year, 2 billion installs per month. All of that to say: the PHP ecosystem is growing a lot.


Twice a year, I publish my version stats post. In these posts, I analyze PHP version usage across the community based on Packagist's data. I wanted to share a graph from that post again: the timeline between 2013 and now, showing the per-version usage history.

While it's great to see PHP 8.* usage rise steeply, there's also a big chunk of people still stuck on older, slow and insecure PHP versions. My hope for 2023 is to see those old version numbers decline even more rapidly. I wrote it like this in my version stats post:

This data beautifully visualizes the division within the PHP community: one part is keeping up with modern PHP, while another one stays helplessly behind.

Speaking of upgrades, I want to mention one tool in particular: Rector. Rector is a free automation tool that helps upgrade your PHP codebase. All it takes is a tiny amount of configuration, and it'll do a huge amount of work for you.

I recently used it to update my community-driven content aggregator, Aggregate to PHP 8.2, and it was really fun and easy to use.

When, after publishing my version stats post, several people told me they hadn't updated yet and were stuck on PHP 7.*, I asked them why. They told me it was simply too much manual work. Interestingly enough, no one had even tried to use tools like Rector to help them…

I firmly believe that a "programming language" is so much more than a compiler: it's the tools and ecosystem that play an equal part in defining that "programming language", and I really think lots of people, projects and businesses would benefit if they looked into using automation tools like Rector.


Since I'm talking about the ecosystem, I can't go without mentioning PHP's two largest frameworks: Laravel and Symfony.

Over the past years, Laravel has grown tremendously. They now employ 8 full time developers to work on the framework and its ecosystem. On top of that, JetBrains' dev survey reports that 67% of PHP developers work with Laravel.

While Symfony as a framework might be less popular compared to Laravel these days, it's still one of the most mature and stable frameworks within the PHP community. It's more often used for enterprise app development, but its standalone components are popular throughout the whole PHP ecosystem — Laravel also has a couple of dependencies on Symfony components. No surprise that more than a handful of Symfony packages make it into Packagist's top package list.

I should also mention WordPress. I'll be honest, I have a love/hate relationship with it. As a user, WordPress is great. It's so easy to install and use, and I think it has earned every bit of its popularity over the years. As a developer though, WordPress makes me sad. The inability to stay up to date with modern and safe PHP versions casts a shadow on the whole PHP community.

Right now, WordPress only has beta support for PHP 8.0. Now, to be clear: PHP 8.0 was released in 2020, and is now end of life, three years later — and WordPress doesn't yet support it…

Of course, there are reasons for not properly supporting newer PHP versions. Up to you to decide whether they are good or not. My personal opinion is that the decision to hold on to backwards compatibility as much as WordPress does is mostly business driven: a big part of WordPress is the commercial part, and a big part of their customer base is running old PHP versions. It's a vicious circle where both parties are holding each other back and, by extent, hold back the PHP community as a whole.

On the other hand, we should recognise the fact that not many software projects are able to stay as popular and relevant as WordPress after almost 20 years, so maybe their strategy about backwards compatibility is the right one?

# Superset

Finally, I can't help but mention my long-term dream for PHP. I write about it, I speak about it, and I hope that one day it'll become true: a superset of PHP, with proper IDE and static analyser support.

There are a bunch of reasons why I want it to happen, you can read and hear about them if you want to; but I really hope it'll become a reality. I doubt we'll get to see a widely accepted and supported superset in 2023, but some baby steps are already being made. I'm definitely keeping a close eye on PXP, which might take things in the right direction.


With all of that being said, I hope that you'll enjoy 2023! And just in case you're new here: I'm Brent, developer advocate at JetBrains, I write and vlog about PHP; and I'd really appreciate it if you checked out the YouTube channel I've been working on lately. Take a look, and maybe consider subscribing? Thanks!

If you're not into videos but still want to follow me, you can join 15k newsletter subscribers instead, I hope to see you there!

]]>
2023-01-17T00:00:00+00:00
<![CDATA[ PHP version stats: January, 2023 ]]> https://www.stitcher.io/blog/php-version-stats-january-2023 It's that time again: my biyearly summary of which PHP versions are used across the community. You can read the previous edition here.

As always, it's important to note that I'm working with the data available to us. That means that these charts are not a 100% accurate representation of the PHP community as a whole, but they are an accurate representation of one of the most prominent parts of PHP: the packagist ecosystem.

This blog post was sponsored by Private Packagist - the private Composer repository from the creators and maintainers of Composer & Packagist.

# Usage Statistics

Let's start with the percentage of PHP versions being used today, and compare it to the previous three editions, note that I've omitted all versions that don't have more than 1% usage:

Version 2021-07 2022-01 2022-07 2023-01
8.2 0.0% 0.0% 0.0% 4.7%
8.1 0.1% 9.1% 24.5% 38.8%
8.0 14.7% 23.9% 20.6% 16.2%
7.4 46.8% 43.9% 38.4% 27.7%
7.3 19.2% 12.0% 8.0% 5.3%
7.2 10.4% 6.6% 5.1% 4.3%
7.1 3.8% 2.4% 1.9% 1.8%

Visualizing this data looks like this:

Evolution of version usage

We can see a decent growth for PHP 8.* versions, I think that's great news! It's also great to see PHP 8.0 usage already declining: PHP 8.0 went into security fixes only mode at the end of last year, and I hope to see its usage decline a lot more this year. Keep in mind that PHP 8.0 will reach end of life on November 26, 2023. So it's crucial that projects start preparing to upgrade in the coming months.

PHP 7.4 reached end of life last year, so at the same time it's worrying that more than 25% of projects are still using it! Let's hope to see this number decline soon.

This data beautifully visualizes the division within the PHP community: one part is keeping up with modern PHP, while another one stays helplessly behind. I know there are many reasons to stay behind — often driven by business requirements and constraints — but it's crucial to realise that a lot of PHP projects are in fact running insecure and slow versions in production because of it.

Moving on to the all-time overview chart, here you can see the evolution of version usage across time:

All time evolution

Like I said: the decline of 7.4 is going too slow to my liking. Compare it to the much steeper decline of PHP 5.5 back in 2015 when PHP 7.0 was released: I would have liked to see the same happen with PHP 7.4 and have people move to PHP 8.0, but unfortunately that's not the case.

I feel like I'm repeating myself every year, but I really hope that people upgrade their projects sooner in the future. I'm curious to learn how this part of the PHP community can be helped. I feel that tools like Rector to automate upgrades have so much potential, if only people started using it.

# Required versions

Next, I used Nikita's popular package analyzer to download the 1000 most popular composer packages. I used a little script to get their minimum required version. Here are the results:

Version 2021-07 2022-01 2022-07 2023-01
8.2 - - - -
8.1 - - 125 129
8.0 117 160 94 103
7.4 56 69 86 98
7.3 133 116 104 106
7.2 142 133 130 144
7.1 182 190 153 159
7.0 31 29 29 30
5.6 61 49 42 43
5.5 43 42 35 37
5.4 41 43 40 40
5.3 97 83 77 78
5.2 12 10 10 10

There are two important notes to make here.

  1. This tables shows the minimum required version. It makes sense that none of the 1000 packages already supports PHP 8.2, since it's been only here for a month.
  2. If you count the numbers, you'll notice there are some differences between each year. Not every package lists a version, so not all 1000 packages can be parsed.

Instead of comparing absolute numbers, it's best to plot this data into a chart for a relative comparison, so that we can see changes over time:

Minimal PHP requirement over time

You can see there's a slight increase in PHP 7.* requirements. It's a good evolution, but still very slow compared to how fast PHP is moving forward.

In my opinion, package authors should push more to require only supported PHP versions. I think it's the only way for PHP to keep moving forward at a decent rate, and for the community to keep up. On top of that: yearly upgrades are much easier to do than to stay on, for example, PHP 7.4 and try to make the jump directly to PHP 8.2.

This blog post was sponsored by Private Packagist - the private Composer repository from the creators and maintainers of Composer & Packagist.

In closing, if you take one thing away from this post, I hope it's that it's time to upgrade to at least PHP 8.1, preferably PHP 8.2. It's not as difficult as you might think, and it's definitely worth your time.

What are your thoughts on these stats? Are you using PHP 8.2? Let me know your thoughts on Twitter and subscribe to my newsletter if you want to be kept up-to-date about these posts!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2023-01-11T00:00:00+00:00
<![CDATA[ Upgrading to PHP 8.2 ]]> https://www.stitcher.io/blog/upgrading-to-php-82 In this video I talk you through how I upgraded Aggregate to PHP 8.2.

]]>
2023-01-09T00:00:00+00:00
<![CDATA[ All I want for Christmas… ]]> https://www.stitcher.io/blog/all-i-want-for-christmas Note: this post was first published as part of PHP's "24 Days in December" Advent event.

Let's set aside all practical concerns for a moment — it's Christmas, after all. If you could choose — freely choose: what would you change about PHP?

Would you want generics or the pipe operator? Maybe you'd like to see consistent function signatures or get rid of the dollar sign. Type aliases, scalar objects, autoloading for namespaced functions, improved performance, less breaking changes, more breaking changes — the list goes on.

But what if I told you, you had to pick one, and only one. What would it be?

My number one feature isn't in this list. Even worse: my number one wish for PHP will probably never happen. But like I said at the beginning: let's set aside all practical concerns. Let's dream for a moment, not because we believe all dreams come true; but because dreams, in themselves, are valuable and give hope — it is Christmas, after all.


Let me tell you the story of another programming language. A language that, just like PHP, had been the underdog for decades. It didn't have sexy syntax, it was a poor performer, it had messy methods and confusing classes. It was… JavaScript.

It was a language that — legend says — was written in two weeks. Yet it grew to be the most popular programming language people had ever seen, almost by accident.

Indeed, for years, developers were stuck with JavaScript: it was the only option to do any kind of frontend programming at all. And despite everyone mocking poor JavaScript, there simply were no other alternatives, so they had to use it.

That is: until a bunch of smart people started thinking out of the box. The idea was simple: what if we don't write JavaScript anymore, but instead write a program in another language, and convert that code to JavaScript? That way our programs could still work in the browser, but we didn't have to put up with JavaScript's ugliness and quirkiness.

And so history was written: we compiled languages like C to a subset of JavaScript — an optimised subset that was much more performant. It was called asm.js. It allowed us to run existing game engines in the browser. We compiled JavaScript to JavaScript with Babel: newer syntax that wasn't available in browsers was "transpiled" to older JavaScript versions. Supersets like CoffeeScript came into existence. They added better and more convenient syntax.

More and more, the JavaScript community transformed into a host of languages, with JavaScript simply being a compilation target.

Sure, compiling JavaScript meant adding a build step, but it seemed like developers got used to it. First and foremost: build steps were optimised for performance, and second: you could suddenly add a lot of static functionality to your language: tasks that were performed only when compiling your program but not while running it. TypeScript was created, and static type checking became a thing. It turned out: computers were awfully good at detecting mistakes, as long as we provide them with just enough information — types.

And everyone marvelled at JavaScript: it grew from a small frontend scripting language to the number 1 programming language in the world.


Let's talk about PHP. I've been writing it for more than a decade now. I love the PHP community, I love the ecosystem, I love how the language has managed to evolve over the years. At the same time I believe there's room for PHP to grow — lots of room. And so I dream.

I dream of a world where PHP follows in JavaScript footsteps. Where it becomes more than just PHP. I dream of a TypeScript for PHP: a language that's still 100% compatible with all PHP code out there, but one that adds generics and proper static type checking — without having to worry about runtime performance costs. I dream of a language that has all (or most) modern-day language features, that are compiled to plain, old, boring — but working — PHP.

But dreams seldom come true.

Someone once said: "For JavaScript to become as popular as it is today, there had to be two things: it had to suck, and it had to be the only viable option available". And here's the thing: unlike JavaScript, PHP isn't the only option available for backend programming. I also dare to say that it's way better than JavaScript back in the day. These two main components that were needed for JavaScript to grow into something more than itself, aren't equally present in PHP.

And that's ok. It means my dream is probably unrealistic, but it also means something much more important.

My realisation recently is that PHP is already awesome. People are already building great things with it. Sure, maybe PHP is boring compared to the latest and greatest programming languages, and sure you might need to use another language if you're building something for those 0.1% of edge cases that need insane performance. My dream of a superset of PHP might be one of many approaches, but it sure isn't the only viable path forward.

Even without that dream of mine: PHP is doing great. It's not because of how its designed, it's not because of its syntax. It's not because of its top-notch performance and it's not because of its incredible type system. It's because people like you are building amazing things with it. Whether you're using PHP 8.2 or not; whether your running it serverless or asynchronous or not; whether you're writing OOP, FP, DDD, ES, CQRS, Serverless or whatever term you want to throw at it — you are building awesome things. It turns out a language is rarely the bottleneck, because it's merely a tool. But "PHP" is so much more than just a language, it's so much more than just a tool. That's because of you.

Thank you for being part of what makes PHP great. Happy holidays.

]]>
2023-01-03T00:00:00+00:00
<![CDATA[ You cannot find me on Mastodon ]]> https://www.stitcher.io/blog/you-cannot-find-me-on-mastodon I started this blog originally on Medium — did you know that? That was 7 years ago. I managed to build a small audience over there — it felt large at the time but numbers are of course relative. Medium changed a lot in that period though, and while I can't remember the final trigger, at one point I decided I wanted to take full control and ownership of my content. And so I created a static blog — nothing fancy, but it worked for me.

I've never felt any regrets for that decision. On the contrary: I believe that if I stuck with Medium, I would have never been able to grow my blog to where it is today. It's because of my blog that I was able to join my current and previous job — it's safe to say this has been life defining for me and my family. Full control meant taking ownership and responsibility, I started caring for my content in another way and that paid off.

It's been five years since I took ownership of my content, but I didn't do the same with my audience. My audience is spread across different places on the web (Reddit, RSS, other blogs, Twitter, HackerNews, …), with Twitter being the largest by far.

With all the Twitter drama these past weeks, I realised how dependent I am — once again — on a platform that's totally out of my control.

I've managed to build a following of 15k people on Twitter. That's small for many people but it's larger than what I imagined a couple of years ago. And while I have 15k followers (that's maybe 1k of real people actually seeing my tweets), if Twitter shuts down tomorrow, those people are gone. To me that would mean all the time and effort I put into my Twitter audience will simply be… gone.

Now, I'm not making any predictions about Twitter's future, and I don't believe anyone talking about it actually has a clue. But, I also won't move to the next platform anymore. At least not for building an audience like I did on Twitter. Instead, I've managed to build a decent newsletter audience of 17k people by now. That are around 5k actual readers, despite double opt-in (email is weird 🙄). But at least it's in my control: my newsletter is self hosted and I keep full control over my audience. No one can pull the plug but me.

Now the Mastodon fans will tell me how Mastodon is decentralized and safe and how they are in full control, but that's simply not true. You're not owning your Mastodon servers — at least not the ones that are actually large enough to matter: phpc.social for example (that's where my audience is), is hosted on masto.host.

Remember how WhatsApp was considered the "safe and encrypted messaging app" once? Remember how everyone and their mother encouraged you to migrate to Signal or Telegram a couple of years later, which also turned out to have privacy and security flaws on their own? You're never in control.

I'm not saying I won't use any of those platforms — if Twitter goes down I probably will join the PHP community on Mastodon. But I won't build and rely on it like I did with Twitter. I'll take full control of my audience from this point forward, meaning you can either follow me via my newsletter or via RSS, whatever works best for you.

]]>
2022-11-19T00:00:00+00:00
<![CDATA[ What's new in PHP 8.2 ]]> https://www.stitcher.io/blog/new-in-php-82 PHP 8.2 is released on December 8, 2022. In this post, we'll go through all features, performance improvements, changes and deprecations one by one.

# Readonly classes RFC

Readonly properties were introduced in PHP 8.1. This RFC builds on top of them, and adds syntactic sugar to make all class properties readonly at once. Instead of writing this:

class Post
{
    public function __construct(
        public readonly string $title, 
        public readonly Author $author,
        public readonly string $body,
        public readonly DateTime $publishedAt,
    ) {}
}

You can now write this:

readonly class Post
{
    public function __construct(
        public string $title, 
        public Author $author,
        public string $body,
        public DateTime $publishedAt,
    ) {}
}

Functionally, making a class readonly is entirely the same as making every property readonly; but it will also prevent dynamic properties being added on a class:

$post = new Post(/* … */);

$post->unknown = 'wrong';

Uncaught Error: Cannot create dynamic property Post::$unknown

Note that you can only extend from readonly classes if the child class is readonly as well.

PHP has changed quite a lot, and readonly classes are a welcome addition. You can take a look at my video about PHP's evolution if you want to as well:


# Deprecate dynamic properties RFC

I'd say this is a change for the better, but it will hurt a little bit. Dynamic properties are deprecated in PHP 8.2, and will throw an ErrorException in PHP 9.0:

class Post
{
    public string $title;
}

// …

$post->name = 'Name';

Keep in mind that classes implementing __get and __set will still work as intended:

class Post
{
    private array $properties = [];
    
    public function __set(string $name, mixed $value): void
    {
        $this->properties[$name] = $value;
    }
}

// …

$post->name = 'Name';

If you want to learn more about why deprecations are useful and how to deal with them, you can read this followup post on how to deal with deprecations, or you can check out my vlog:


# New random extension RFC

PHP 8.2 adds a new random number generator that fixes a lot of problems with the previous one: it’s more performant, more secure, it’s easier to maintain, and doesn’t rely on global state; eliminating a range of difficult to detect bugs when using PHP’s random functions.

There’s a new class called Randomizer, which accepts a randomizer engine. Now you can change that engine, depending on your needs. For example, to differentiate between a production and testing environment.

$rng = $is_production
    ? new Random\Engine\Secure()
    : new Random\Engine\Mt19937(1234);
 
$randomizer = new Random\Randomizer($rng);
$randomizer->shuffleString('foobar');

# null, true, and false as standalone types RFC

PHP 8.2 adds three new types — or something that looks like it. We'll avoid going down the rabbit hole of type safety in this post, but technically null, true, and false could be considered valid types on their own. Common examples are PHP's built-in functions, where false is used as the return type for when an error occurs. For example in file_get_contents:

file_get_contents(/* … */): string|false

Before PHP 8.2, you could already use false together with other types as a union; but now it can be used as a standalone type as well:

function alwaysFalse(): false
{
    return false;
}

The same now also goes for true and null.


# Disjunctive Normal Form Types RFC

DNF types allow us to combine union and intersection types, following a strict rule: when combining union and intersection types, intersection types must be grouped with brackets. In practice, that looks like this:

function generateSlug((HasTitle&HasId)|null $post) 
{
    if ($post === null) {
        return '';
    }

    return 
        strtolower($post->getTitle()) 
        . $post->getId();
}

In this case, (HasTitle&HasId)|null is the DNF type.

It's a nice addition, especially since it means that we can now have nullable intersection types, which is probably the most important use case for this feature.


# Constants in traits RFC

You can now use constants in traits:

trait Foo 
{
    public const CONSTANT = 1;
 
    public function bar(): int 
    {
        return self::CONSTANT;
    }
}

You won't be able to access the constant via the trait's name, either from outside the trait, or from inside it.

trait Foo 
{
    public const CONSTANT = 1;
 
    public function bar(): int 
    {
        return Foo::CONSTANT;
    }
}

Foo::CONSTANT;

You can however access the constant via the class that uses the trait, given that it's public:

class MyClass
{
    use Foo;
}

MyClass::CONSTANT; // 1

# Redact parameters in back traces RFC

A common practice in any codebase is to send production errors to a service that keeps track of them, and will notify developers when something goes wrong. This practice often involves sending stack traces over the wire to a third party service. There are cases however where those stack traces can include sensitive information such as environment variables, passwords or usernames.

PHP 8.2 allows you to mark such "sensitive parameters" with an attribute, so that you don't need to worry about them being listed in your stack traces when something goes wrong. Here's an example from the RFC:

function login(
    string $user,
    #[\SensitiveParameter] string $password
) {
    // …
    
    throw new Exception('Error');
}
 
login('root', 'root');
 
Fatal error: Uncaught Exception: Error in login.php:8
Stack trace:
#0 login.php(11): login('root', Object(SensitiveParameterValue))
#1 {main}
  thrown in login.php on line 8

# Fetch properties of enums in const expressions RFC

From the RFC:

This RFC proposes to allow the use of ->/?-> to fetch properties of enums in constant expressions. The primary motivation for this change is to allow fetching the name and value properties in places where enum objects aren't allowed, like array keys

That means that the following code is now valid:

enum A: string 
{
    case B = 'B';
    
    const C = [self::B->value => self::B];
}

# Return type changes for DateTime::createFromImmutable() and DateTimeImmutable::createFromMutable() breaking

Previously, these methods looked like this:

DateTime::createFromImmutable(): DateTime
DateTimeImmutable::createFromMutable(): DateTimeImmutable

In PHP 8.2 those method signatures are changed like so:

DateTime::createFromImmutable(): static
DateTimeImmutable::createFromMutable(): static

This change makes a lot more sense, as it improves static insight possibilities for classes extending from DateTime and DateTimeImmutable. However, technically, this is a breaking change that might affect custom implementations that extend from either of those two classes.


# utf8_encode() and utf8_decode() deprecations RFC

In PHP 8.2, using either utf8_encode() or utf8_decode() will trigger these deprecation notices:

Deprecated: Function utf8_encode() is deprecated
Deprecated: Function utf8_decode() is deprecated

The RFC argues that these functions have a inaccurate name that often causes confusion: these functions only convert between ISO-8859-1 and UTF-8, while the function name suggest a more broader use. There's a more detailed explanation about the reasoning in the RFC.

The alternative? The RFC suggests using mb_convert_encoding() instead.


# Locale-insensitive strtolower() and strtoupper() breaking RFC

Both strtolower() and strtoupper() are no longer locale-sensitive. You can use mb_strtolower() if you want localized case conversion.


# Signature changes to several SPL methods breaking

Several methods of SPL classes have been changed to properly enforce their correct type signature:

SplFileInfo::_bad_state_ex()
SplFileObject::getCsvControl()
SplFileObject::fflush()
SplFileObject::ftell()
SplFileObject::fgetc()
SplFileObject::fpassthru()
SplFileObject::hasChildren()
SplFileObject::getChildren()

# New n modifier in PCRE upgrading

You can now use the n modifier (NO_AUTO_CAPTURE) in pcre* functions.


# ODBC username and password escaping breaking

From the UPGRADING guide:

The ODBC extension now escapes the username and password for the case when both a connection string and username/password are passed, and the string must be appended to.

The same applies to PDO_ODBC.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.


# Deprecate ${} string interpolation RFC

PHP has several ways of embedding variables in strings. This RFC deprecates two ways of doing so, since they are rarely used, and often lead to confusion:

"Hello ${world}";
Deprecated: Using ${} in strings is deprecated
 
"Hello ${(world)}";
Deprecated: Using ${} (variable variables) in strings is deprecated

To be clear: the two popular ways of string interpolation still work:

"Hello {$world}";
"Hello $world";

# Deprecate partially supported callables RFC

Another change, although one with a slightly smaller impact, is that partially supported callables are now deprecated as well. Partially supported callables are callables which can be called using call_user_func($callable), but not by calling $callable() directly. The list of these kinds of callables is rather short, by the way:

"self::method"
"parent::method"
"static::method"
["self", "method"]
["parent", "method"]
["static", "method"]
["Foo", "Bar::method"]
[new Foo, "Bar::method"]

The reason for doing this? It's a step in the right direction towards being able to use callable for typed properties. Nikita explains it very well in the RFC:

all of these callables are context-dependent. The method that "self::method" refers to depends on which class the call or callability check is performed from. In practice, this usually also holds for the last two cases, when used in the form of [new Foo, "parent::method"].

Reducing the context-dependence of callables is the secondary goal of this RFC. After this RFC, the only scope-dependence still left is method visibility: "Foo::bar" may be visible in one scope, but not another. If callables were to be limited to public methods in the future (while private methods would have to use first-class callables or Closure::fromCallable() to be made scope-independent), then the callable type would become well-defined and could be used as a property type. However, changes to visibility handling are not proposed as part of this RFC.


That's all there is for now, I'll keep this list updated throughout the year. You can subscribe to my newsletter if you want to receive occasional updates!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-12-08T00:00:00+00:00
<![CDATA[ PHP Annotated, the YouTube channel ]]> https://www.stitcher.io/blog/php-annotated Some very exciting news today! If you're a regular reader of this blog, you know that I've been working on my YouTube channel for the past years as a hobby project. You might also know that I've been a developer advocate at JetBrains for half a year now.

Today, these two blend together! My personal channel will now be known as "PHP Annotated", and I'll be able to work on it as part of my job!

I'm really excited about this, especially since the goal of the channel will stay exactly the same: creating quality content, focussed on the PHP community. So don't worry: the channel won't become a "marketing channel", it'll still be me, doing the same things I was already doing — but now more and better!

If you're curious what I've been doing, you can watch my brand new "What's new in PHP 8.2" video right now!

I hope you're excited, and would love for you to spread the word! Also make sure to subscribe if you want to be kept in the loop. If you've got any questions, you can reach me — as always — via Twitter or e-mail.

]]>
2022-11-08T00:00:00+00:00
<![CDATA[ Deprecating spatie/data-transfer-object ]]> https://www.stitcher.io/blog/deprecating-spatie-dto It's been four years since I published the first version of spatie/data-transfer-object together with my then-colleagues at Spatie.

Back then, PHP 7.3 was just around the corner and the package started out as a way to add complex runtime type checks for class properties. It gave programmers certainty about whether they were actually dealing with the right data in a typed way:

class PostData extends DataTransferObject
{
    /**
     * Built in types: 
     *
     * @var string 
     */
    public $property;
    
    /**
     * Classes with their FQCN: 
     *
     * @var \App\Models\Author
     */
    public $property;
    
    /**
     * Lists of types: 
     *
     * @var \App\Models\Author[]
     */
    public $property;
    
    /**
     * Iterator of types: 
     *
     * @var iterator<\App\Models\Author>
     */
    public $property;
    
    /**
     * Union types: 
     *
     * @var string|int
     */
    public $property;
    
    /**
     * Nullable types: 
     *
     * @var string|null
     */
    public $property;
    
    /**
     * Mixed types: 
     *
     * @var mixed|null
     */
    public $property;
    
    /**
     * Any iterator: 
     *
     * @var iterator
     */
    public $property;
    
    /**
     * No type, which allows everything
     */
    public $property;
}

Fast forward a year to PHP 7.4, and typed properties were added. From the very start I said that one of the package's goal was to become obsolete in the future, and it seemed like we were one step closer to achieving it.

However, another (accidental) feature of the package started to gain popularity: the ability to cast raw data into nested DTOs. So a DTO like this:

class PostData extends DataTransferObject
{
    public AuthorData $author;
}

Could be created from this input:

$postData = new PostData([
    'author' => [
        'name' => 'Foo',
    ],
]);

So while typed properties were now a thing, we decided to continue the package, albeit in a slightly other form.

Next came along PHP 8.0 with attributes and named properties, allowing for even more functionality to be added:

class MyDTO extends DataTransferObject
{
    public OtherDTO $otherDTO;
    
    public OtherDTOCollection $collection;
    
    #[CastWith(ComplexObjectCaster::class)]
    public ComplexObject $complexObject;
    
    public ComplexObjectWithCast $complexObjectWithCast;
    
    #[NumberBetween(1, 100)]
    public int $a;
    
    #[MapFrom('address.city')]
    public string $city;
}

At this point though, we were far away from the problem the package initially set out to solve. It did no more runtime type checking: in part because of PHP's improved type system, in part because I believe static analysis is a much better approach to solving type-related problems these days.

On top of that, there are better solutions to "data mapping" than what this package does: there's spatie/laravel-data with an incredible Laravel-specific approach to mapping data between requests, databases, views, etc; there's cuyz/valinor which offers much more functionality than our package; and there is symfony/serializer which is a little more bare-bones, but more powerful as well.

And so, the question that has been on my mind for two years, has been answered: it is time to deprecate spatie/data-transfer-object.

I of course discussed the matter with my ex-colleagues at Spatie, as well as with Aidan who has helped maintaining the package for a couple of years now. We all agreed that this is a good time:

  • PHP has evolved a lot, meaning that the original goal of the package has been fulfilled.
  • There are great alternatives out there, both Laravel-specific and framework agnostic.
  • It's better to give the package a worthy ending, than a slow death (which is already happening, kind of).

Now, keep in mind that a deprecation doesn't mean the package is gone! The code is still here for you to use, and I don't foresee any issue for the near future.

If you were to have any serious concerns though: don't hesitate to let me know on Twitter!

]]>
2022-11-01T00:00:00+00:00
<![CDATA[ Upgrade to PHP 8.2 with Homebrew on Mac ]]> https://www.stitcher.io/blog/php-82-upgrade-mac

# Upgrading with Homebrew

Start by making sure brew is up-to-date:

brew update

Next, upgrade PHP. You can either use the built-in php recipe, but I recommend to use the shivammathur/homebrew-php tap.

# Normal upgrade

brew upgrade php

# Upgrade with shivammathur/homebrew-php

brew tap shivammathur/php
brew install shivammathur/php/php@8.2

To switch between versions, use the following command:

brew link --overwrite --force php@8.2

You can read more in the repository.

# Next steps

Check the current version by running php -v:

php -v

Restart Nginx or Apache, if you're using Laravel Valet you can skip to the next section; you need some extra steps in order for the web server to properly work.

sudo nginx -s reload
sudo apachectl restart

And make sure that your local web server also uses PHP 8.2 by visiting this script:

# index.php, accessible to your web server

phpinfo();

The version should show 8.2.x.

# Valet

If you're using Laravel Valet, you should do the following steps to upgrade it:

composer global update

You can use valet use to switch between PHP versions:

valet use php@8.2
valet use php@8.1

# Extensions

PHP extensions are installed using pecl. I personally use Redis and Xdebug. They can be installed like so:

pecl install redis
pecl install xdebug

You can run pecl list to see which extensions are installed:

pecl list

# Installed packages, channel pecl.php.net:
# =========================================
# Package Version State
# redis   5.3.4   stable
# xdebug  3.1.1   stable

You can search for other extensions using pecl search:

pecl search pdf

# Retrieving data...0%
# ..
# Matched packages, channel pecl.php.net:
# =======================================
# Package Stable/(Latest) Local
# pdflib  4.1.4 (stable)        Creating PDF on the fly with the PDFlib library

Make sure to restart your web server after installing new packages:

sudo nginx -s reload
sudo apachectl restart
valet restart

Make sure all extensions are correctly installed and loaded by checking both your PHP webserver and CLI installs:

php -i | grep redis
var_dump(extension_loaded('redis'));

If extensions aren't properly loaded, there are two easy fixes.

First, make sure the extensions are added in the correct ini file. You can run php --ini to know which file is loaded:

Configuration File (php.ini) Path: /opt/homebrew/etc/php/8.2
Loaded Configuration File:         /opt/homebrew/etc/php/8.2/php.ini
Scan for additional .ini files in: /opt/homebrew/etc/php/8.2/conf.d
Additional .ini files parsed:      /opt/homebrew/etc/php/8.2/conf.d/error_log.ini,
/opt/homebrew/etc/php/8.2/conf.d/ext-opcache.ini,
/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini

Now check the ini file:

extension="redis.so"
zend_extension="xdebug.so"

Note that if you're testing installed extensions via the CLI, you don't need to restart nginx, apache or Valet when making changes to ini settings.

The second thing you can do, if you're updating from an older PHP version which also used pecl to install extension; is to reinstall every extension individually.

pecl uninstall redis
pecl install redis

# Last step

Finally you should test and upgrade your projects for PHP 8.2 compatibility.

]]>
2022-11-01T00:00:00+00:00
<![CDATA[ Readonly classes in PHP 8.2 ]]> https://www.stitcher.io/blog/readonly-classes-in-php-82 PHP 8.2 adds a new way of declaring classes: you can make them readonly. In practice, it means that all properties of that class will be readonly. This is especially useful when you're using data transfer objects or value objects, where a class only has public readonly properties.

In other words, instead of writing this:

class BlogData
{
    public function __construct(
        public readonly string $title,
        public readonly Status $status,
        public readonly ?DateTimeImmutable $publishedAt = null,
    ) {}
}

You can now write this:

readonly class BlogData
{
    public function __construct(
        public string $title,
        public Status $status,
        public ?DateTimeImmutable $publishedAt = null,
    ) {}
}

I've written about readonly properties before, so let's quickly summarise first:

  • readonly properties can only be written once — usually in the constructor;
  • only typed properties can be made readonly;
  • they cannot have a default value (unless you're using promoted properties);
  • the readonly flag cannot be changed during inheritance; and finally
  • you cannot unset readonly properties.

Since readonly classes are merely syntactic sugar for making all properties of that class readonly, it means that the same rules apply to readonly classes as well.

# Write once

All properties of a readonly class can only be written once, and can not be unset:

readonly class BlogData { /* … */ }

$blogData = new BlogData(/* … */);

$blogData->title = 'other';

unset($blogData->title);

# Only typed properties

A readonly class can only have typed properties:

readonly class BlogData
{
    public string $title;
    
    public $mixed;
}

# No static properties

Since readonly properties cannot be static, readonly classes cannot have any static properties:

readonly class BlogData
{
    public static string $title;
}

# No default values

Properties of a readonly class can not have a default value unless you're using promoted properties:

readonly class BlogData
{
    public string $title = 'default';
}
readonly class BlogData
{
    public function __construct(
        public string $title = 'default', // This works
   ) {}
}

# No changes during inheritance

You cannot change the readonly class flag during inheritance:

readonly class BlogData { /* … */ }

class NewsItemData extends BlogData { /* … */ }

# No dynamic properties

Readonly classes also don't allow dynamic properties. This won't have a big impact since dynamic properties are deprecated in PHP 8.2 anyway, but means that you cannot add the #[AllowDynamicProperties] attribute to readonly classes:

#[AllowDynamicProperties]
readonly class BlogData { /* … */ }

# Reflection

Finally, there's a new reflection method to determine whether a class is readonly: ReflectionClass::isReadOnly(). You can also use ReflectionClass::getModifiers(), which will include the ReflectionClass::IS_READONLY flag.


]]>
2022-10-25T00:00:00+00:00
<![CDATA[ PHP 8.2 in 8 code blocks ]]> https://www.stitcher.io/blog/php-82-in-8-code-blocks

readonly class PostData
{
    public function __construct(
        public string $title,
        public string $author,
        public string $body,
        public DateTimeImmutable $createdAt,
        public PostState $state,
    ) {}
}

Readonly classes


$rng = $is_production
    ? new Random\Engine\Secure()
    : new Random\Engine\Mt19937(1234);
 
$randomizer = new Random\Randomizer($rng);

$randomizer->shuffleString('foobar');

New random extension


function alwaysFalse(): false
{
    return false;
}

null, true, and false as standalone types


function generateSlug((HasTitle&HasId)|null $post) 
{ /* … */ }

Disjunctive Normal Form Types


trait Foo 
{
    public const CONSTANT = 1;
 
    public function bar(): int 
    {
        return self::CONSTANT;
    }
}

Constants in traits


function connect(
    string $user,
    #[\SensitiveParameter] string $password
) {
    // …
}

Redacted parameters


class Post {}

$post = new Post();

$post->title = 'Name';

// Deprecated: Creation of dynamic property is deprecated

Deprecated dynamic properties


enum A: string 
{
    case B = 'B';
    
    const C = [self::B->value => self::B];
}

Enum properties in const expressions


]]>
2022-10-24T00:00:00+00:00
<![CDATA[ Uses ]]> https://www.stitcher.io/blog/uses It's time. People keep asking me about my setup, so here it is — short and to the point, no life story or sponsored links attached to it.

# Audio

# Video

# Recording and editing

  • Apple Quicktime for screen and cam recording — I always record them separately
  • For cam recording — I always have a script written word by word
  • PhpStorm for code and blog content
  • Premiere Pro for video editing
  • Audition for audio editing
  • This is my EQ preset (for my voice in my room):

# Colour scheme

]]>
2022-09-09T00:00:00+00:00
<![CDATA[ Asymmetric visions ]]> https://www.stitcher.io/blog/thoughts-on-asymmetric-visibility There's a new RFC in town called asymmetric visibility, its aim is to define properties which can be written to from a protected or private scope, but which from the outside — the public scope — can only be read.

It looks something like this:

final class Post
{
    public function __construct(
        public private(set) string $title,
    ) {}
}

There are a couple of things going on here:

  • we're using a promoted property to define the title; and
  • we're explicitly saying that $title can only be set (and thus overwritten) within the private scope of Post.

In other words, this is allowed:

final class Post
{
    public function __construct(
        public private(set) string $title,
    ) {}
    
    public function changeTitle(string $title): void
    {
        // Do a bunch of checks
        if (strlen($title) < 30) {
            throw new InvalidTitle('Title length should be at least 30');
        }
        
        // Change the title from within the private scope
        $this->title = $title;
    }
}

While this isn't:

$post = new Post('Title');

// Setting $title from the public scope isn't allowed:
$post->title = 'Another title';

I would say it's a pretty decent proposal, I can come up with a bunch of use cases where you want public readonly access to an object's properties (without the overhead of implementing getters), while still being able to change a property's value from within its class. That class could for example add internal checks to ensure the value adheres to any number of business rules — as a simplified example: ensuring the title of our post is at least 30 characters long.

So all things good, here's hoping the RFC passes.

Right?

Well, I have a problem with it. Actually, not with the RFC itself — I think it's a very good proposal. No, my concern is not with this RFC on its own, but rather with how it's closely related to readonly properties.

Here we have an RFC which scope overlaps with readonly properties (albeit not entirely the same), with a future possibility to replace readonly properties altogether. Here's a quote from the RFC:

At this time, there are only two possible operations to scope: read and write. In concept, additional operations could be added with their own visibility controls. Possible examples include:

  • once - Allows a property to be set only once, and then frozen thereafter. In this case, public private(once) would be exactly equivalent to readonly, whereas public protected(once) would be similar but also allow the property to be set from child classes.

It's clear that this RFC and its successors have the potential to replace readonly properties entirely. Readonly properties — a feature that only has been added one year ago in PHP 8.1, not to mention readonly classes, which are coming to PHP 8.2 later this year.

Despite asymmetric visibility being a great proposal, I'm afraid of what PHP will become if we're adding features only to make them irrelevant three or four years later, as could potentially happen here with readonly properties. We should be very careful and deliberate about how we're changing PHP, and not dismiss existing features too quickly.

If we did, we'd contribute to a lot of uncertainty and instability within the community. Imagine someone adopting readonly properties today, only to hear a year later that by PHP 9.0, they'll probably be deprecated in favor of asymmetric visibility.

Even if readonly properties would stay and coexist with asymmetric visibility, there would be so much room for confusion: when could you use readonly properties? Should you always use asymmetric visibility instead? I would say it's bad language design if a language allows room for these kinds of questions and doubts.

Furthermore, I totally agree with Marco's sentiment on the matter:

I use readonly properties aggressively, and I try to make the state as immutable as possible.

In the extremely rare cases where public get and private set are needed, I rely on traditional getters and setters, which are becoming extremely situational anyway, and still work perfectly fine.

[…]

In fact, I'm writing so few getters and setters these days, that I don't see why I'd need getter and setter semantics to creep into the language, especially mutable ones, not even with the reflection improvements.

Now to be clear: I'm very well aware that asymmetric visibility and readonly properties aren't the same thing. Asymmetric visibility covers a much larger scope and offers much more flexibility. However: Nikita already coined a very similar idea to asymmetric visibility last year, which wasn't pursued in favour of readonly properties. The discussion about whether we want more flexibility has already been had, and the conclusion back then was: no; readonly properties cover 95% of the use cases, and that's good enough.

I would be sad to see PHP become a language that throws out core features every couple of years, for the sake of a little more flexibility. If we wanted more flexibility in this case, we should have decided on that two years ago when readonly properties were discussed in depth; now — in my opinion — is too late.


On a final note, if you are worried about cloning objects with new values (a problem this RFC would solve and readonly properties don't): people are already working on an RFC to allow rewriting readonly properties while cloning. I'd say it's better to focus our efforts in that area, instead of coming up with an entirely different approach.

Even more: the original example I showed with asymmetric visibility allowing for more functionality (internally guarding business rules) wasn't entirely correct. The same is possible with readonly properties, given that we have a way to overwrite readonly values when cloning them:

final class Post
{
    public function __construct(
        public readonly string $title,
    ) {}
    
    public function changeTitle(string $title): self
    {
        // Do a bunch of checks
        if (strlen($title) < 30) {
            throw new InvalidTitle('Title length should be at least 30');
        }
        
        return clone $this with {
            title: $title,
        }
    }
}

Oh, and while the above syntax isn't available yet, it's already possible to overwrite readonly properties while cloning today with some additional userland code:

final class Post
{
    use Cloneable;
    
    public function __construct(
        public readonly string $title,
    ) {}
    
    public function changeTitle(string $title): self
    {
        // Do a bunch of checks
        if (strlen($title) < 30) {
            throw new InvalidTitle('Title length should be at least 30');
        }
        
        return $this->with(
            title: $title,
        );
    }
}

In summary: I think asymmetric visibility is a great feature for some use cases, although there are alternatives as well. All in all, I don't think it's worth adding asymmetric visibility now that we have readonly properties. We decided on readonly properties, we'll have to stick with them for the sake of our users and to prevent ambiguous features from making a mess.

I think a unified vision and direction for PHP is lacking these days, and this RFC — great as it is on its own — is a good example of that lacking in practice. I hope that we (PHP internals, that is) can come up with a solution, maybe the PHP Foundation has an important role to play in all of this, in the future?

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-08-25T00:00:00+00:00
<![CDATA[ What I would change about PHP ]]> https://www.stitcher.io/blog/php-reimagined-part-2 If you could change something about PHP, without having to worry about backwards compatibility or breaking changes; what would it be? I know for sure I'd change a thing or two — or ten.

I like thinking about these topics. Not because I believe all of these things necessarily need be added to PHP, but because it's good to challenge our own views and critically think about language design.

I'll keep this list short and to the point, but I'll always link to more in-depth content where relevant. Let's take a look!

# Generics 🥺

The obvious one first. If I'd have to order my wishlist in descending priority, generics are on places one to five, and the rest follows afterwards.

function app(classString<ClassType> $className): ClassType
{
    // …
}

It's pretty clear however that generics aren't coming to PHP. That is: as long as we want to validate them at runtime. That's actually what my second wishlist-item is about.

# No more runtime type checks

It's not just because of generics, but because it would allow so much more cool stuff, without having a runtime impact: switching to an opt-in, statically analysed compiler.

I actually wrote an in-depth article about not needing runtime type checks. Static analysis is incredibly powerful, and it would benefit PHP's growth a lot if we could get rid of "trying to do everything at runtime".

Keep in mind: it's my opinion, you don't have to agree 😉.

# A superset of PHP

Like TypeScript for JavaScript. Imagine what would be possible if we'd be able to compile a superset language to plain PHP: complex static type checking, lots of cool syntax that would be too difficult to implement with PHP's runtime constraints, generics 🥺, …

There are a lot of caveats and sidenotes to be made here. I made a little vlog about the topic a while ago where I discuss some of the cons, but still I hope that one day this dream may become reality.

# Cascading attributes

This one might actually be doable: an easy way of getting attributes from parent classes.

So instead of writing this:

$attributes = [];

do {
    $attributes = $reflection->getAttributes(
        name: RouteAttribute::class,
        flags: ReflectionAttribute::IS_INSTANCEOF
    );
    
    $reflection = $reflection->getParentClass();
} while ($attributes === [] && $reflection);

We could do this:

$attributes = $reflection->getAttributes(
    name: RouteAttribute::class,
    flags: ReflectionAttribute::IS_INSTANCEOF 
         | ReflectionAttribute::CASCADE
);

# Scalar objects

It's not that high up on my list since there are userland implementations available, although it would be nice if scalar objects could have proper names. So String instead of StringHelper (which is how Laravel solves the problem of String being a reserved word).

$string = new String(' hello, world ')->trim()->explode(',');

# Pipe operator

There has been an RFC for it a while ago: the pipe operator. However, it has been inactive for a while now. I actually think Larry Garfield still wants to do it some day, but I'm not sure how concrete the plans are currently.

Anyway, a pipe operator would be cool:

$result = "Hello World"
    |> htmlentities(...)
    |> str_split(...)
    |> array_map(strtoupper(...), $$)
    |> array_filter($$, fn($v) => $v != 'O');

Note that I'm taking some creative liberties in how argument placeholders would work with the $$ syntax.

# Unified function names

"It would be such a breaking change 😱!!!" to finally make all PHP functions follow the same naming convention. No more str_replace and strlen, but rather string_replace and string_length.

It wouldn't be that big of a breaking change though: we could automate the upgrade process with tools like Rector, and static analysers would tell us about the correct function names while writing code. It would take some getting used to, but at least there would be a consistent API.

# Stricter everything

If you weren't yelling at me by now, many of you probably will after reading this section. I'd make PHP much stricter overall. Why? Because I like clarity in my code. A stricter language means less room for interpretation or behind-the-scenes-magic that leads to lots of confused moments. So that would include:

  • Final by default
  • A return type is required
  • Omitting a return type means void
  • Everything must be typed
  • Visibility modifiers are required

This will probably never happen, and that's ok; I can enforce most of these things by using PHP CS anyway.

# Looking back

On a final note, I made a similar list to this one in the past, and I'm actually happy to see that some of the things I wished for back then are implemented in PHP today:

So, who knows! Many more things of this list might end up in PHP after all? Please let it be generics though 🥺!

What would you change about PHP? Let me know on Twitter or send me e-mail!

]]>
2022-08-17T00:00:00+00:00
<![CDATA[ Deprecated dynamic properties in PHP 8.2 ]]> https://www.stitcher.io/blog/deprecated-dynamic-properties-in-php-82 As is common with minor releases, PHP 8.2 adds some deprecations. Deprecations often are a source of frustration, though it's important to realise they are actually very helpful. I already wrote about dealing with deprecations in general, so if you're already feeling frustrated, maybe it's good to take a look at that post first. Today, I want to focus on one deprecation in particular in PHP 8.2: deprecated dynamic properties.

So first things first, what are dynamic properties exactly? Well, they are properties that aren't present on a class' definition, but are set on objects of those classes dynamically, at runtime.

For example this Post class doesn't have a name property, but nevertheless we set it at runtime:

class Post
{
}

// …

$post = new Post();

$post->name = 'Name';

var_dump($post->name); // 'Name'

As of PHP 8.2, these dynamic properties will be deprecated:

// …

$post->name = 'Name';

You'll see this message: Deprecated: Creation of dynamic property Post::$name is deprecated.

# Implementing __get and __set still works!

You might be panicking at this point, because dynamic properties are a big part of meta programming in PHP — many frameworks rely on it!

Not to worry: this new deprecation won't affect any class that implements __get and __set. Classes that implement these magic functions will keep working as intended:

class Post
{
    private array $properties = [];
    
    public function __set(string $name, mixed $value): void
    {
        $this->properties[$name] = $value;
    }
    
    // …
}

// …

$post->name = 'Name';

The same goes for objects of stdClass, they will support dynamic properties just as before:

$object = new stdClass();

$object->name = 'Name'; // Works fine in PHP 8.2

Now some clever readers might wonder: if stdClass still allows dynamic properties, what would happen if you'd extend from it?

Indeed, it is possible to extend from stdClass to prevent the deprecation notice from being shown. However, I'd say this solution is far from ideal:

// Don't do this

class Post extends stdClass
{
}

$post = new Post();

$post->name = 'Name'; // Works in PHP 8.2

# A better alternative

If you really want to use dynamic properties without implementing __get and __set, there is a much better alternative than to extend from stdClass.

The PHP core team has provided a built-in attribute called AllowDynamicProperties. As its name suggests, it allows dynamic properties on classes, without having to rely on sketchy extends:

#[\AllowDynamicProperties]
class Post
{
}

$post = new Post();

$post->name = 'Name'; // All fine

# Closing thoughts

PHP used to be a very dynamic language, but has been moving away from that mindset for a while now. Personally I think it's a good thing to embrace stricter rules and rely on static analysis wherever possible, as I find it leads to writing better code.

I can imagine developers who relied on dynamic properties, who are less happy with this change. If you're in that group, you might find it useful to take a closer look into static analysis. You can check out my Road to PHP: Static Analysis series if you want to learn more!

If you're willing to invest some time in figuring out static analysis, I'm fairly certain that most of you won't ever want to return back to the mess that is a dynamic programming language. With PHP we're lucky that both options are available and that you can migrate gradually towards a stricter type system.

So, yes: this deprecation might be a little painful, but I believe it's for the best of the language to do so. And remember that it won't be a fatal error until PHP 9.0, so there's plenty of time to deal with it.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-08-08T00:00:00+00:00
<![CDATA[ Light colour schemes are better ]]> https://www.stitcher.io/blog/light-colour-schemes-are-better I just released a video about light colour schemes. I've been encouraging people to try them out for a while now, with pretty good results, actually. I've challenged people to try it out for a week, it certainly doesn't work for everyone, but I've gotten lots of positive reactions on it as well. People actually got rid of persisting headaches because of it, they found their code to be more readable, etc.

Simply switching to a light colour scheme isn't enough though, you need to take some extra steps. And that's exactly the point of this video, to explain what those steps are. I hope you give it a look, and let me know in the comments whether you're taking up the one-week light theme challenge!

]]>
2022-07-30T00:00:00+00:00
<![CDATA[ PHP performance across versions ]]> https://www.stitcher.io/blog/php-performance-across-versions Do you need a reason besides awesome syntax to update to the latest PHP version? Is performance a good one?

I did some casual benchmarks on a WordPress installation from PHP 5.6 to PHP 8.1, here are the results:

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-07-25T00:00:00+00:00
<![CDATA[ The Road to PHP 8.2 ]]> https://www.stitcher.io/blog/road-to-php-82 I'm very happy to announce that my Road to PHP series is back! This time you can join The Road to PHP 8.2!

If you're unfamiliar with the concept: the Road to PHP is a newsletter series where you'll receive a daily email teaching you about a new feature of PHP. This time around we'll look at PHP 8.2 — in previous editions we looked at PHP 8.1 and static analysis.

In total, almost 7000 people have followed one of my Road to PHP series, and many have told me they like the format where they can start their day with a 5-minute read, straight to their inbox.

By the way: you won't receive any followup every afterwards: you're automatically unsubscribed, unless you specifically decide to subscribe to my main newsletter afterwards.

I'd say: give it a try, and join the Road to PHP 8.2!

]]>
2022-07-21T00:00:00+00:00
<![CDATA[ Uncertainty, doubt, and static analysis ]]> https://www.stitcher.io/blog/uncertainty-doubt-and-static-analysis PHP is a strange language when it comes to type systems and static analysis. Back when it was created, it was a very dynamic and weakly typed language, but it has been slowly evolving towards a language with a stricter type system — albeit opt-in. Developers can still write dynamic and untyped PHP code if they want to, but more and more people seem to lean towards using PHP's type system regularly.

You can see this trend all throughout the community:

  • PHP's internal team has been creating more and more type-system related features in recent years;
  • the rise of external static analysis tools like PHPStan, PhpStorm and Psalm; and
  • frameworks are more and more relying on stricter types and even embracing third-party static analysis syntax like generics in Laravel.

While I think this is a good evolution, I also realise there is a large group within the PHP community that don't want to use a stricter type system or rely on static analysis.

I've had several discussions with that group over the years, and it seems that we cannot get on the same page. I lay out my arguments in favour of stricter type systems and static analysis and as a response I get something like this: sure, but it's way too verbose to write all those types, it makes my code too strict to my liking, and I don't get enough benefit from it.

So when working on my latest video about the problem with null, I came up with yet another way to phrase the argument, in hopes to convince some people to at least consider the possibility that types and static analysis — despite their overhead — can still benefit them.

So, here goes. Attempt number I-lost-count:

My main struggle with writing and maintaining code isn't with what patterns to use or which performance optimizations to apply, it isn't about clean code, project structure or what not; it is about uncertainty and doubt. Here's what that looks like:

  • Will this variable always be an object of interface X?
  • Should I write an extra null check here, to be sure my program won't crash?
  • What order should I pass these parameters in again?
  • What kind of data is in this array?
  • I don't understand what this function does without reading external documentation.

It is those kinds of questions and doubts that I'm bothered by, and it is those kinds of questions that a static analyser answers for me — most of the time.

So no, using a stricter type system and relying on static analysis doesn't slow you down. It increases productivity tenfold, it takes away so much uncertainty and doubt, it's liberating, and I cannot code without it anymore.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-07-16T00:00:00+00:00
<![CDATA[ The evolution of a PHP object throughout the years ]]> https://www.stitcher.io/blog/evolution-of-a-php-object

This is just a fun little post I wrote because I wanted to visualise how my data transfer objects have evolved over the years.

If you prefer, you can watch my 2-minute as well:

# August 2014: PHP 5.6

Let's start with PHP 5.6, this is what most people without modern-day PHP knowledge probably think PHP code still looks like. I'll just give you the code, and I'll mention what changes in future versions.

class BlogData
{
    /** @var string */
    private $title;
    
    /** @var State */
    private $state;
    
    /** @var \DateTimeImmutable|null */
    private $publishedAt;
   
   /**
    * @param string $title 
    * @param State $state 
    * @param \DateTimeImmutable|null $publishedAt 
    */
    public function __construct(
        $title,
        $state,
        $publishedAt = null
    ) {
        $this->title = $title;
        $this->state = $state;
        $this->publishedAt = $publishedAt;
    }
    
    /**
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;    
    }
    
    /**
     * @return State 
     */
    public function getState() 
    {
        return $this->state;    
    }
    
    /**
     * @return \DateTimeImmutable|null 
     */
    public function getPublishedAt() 
    {
        return $this->publishedAt;    
    }
}

# December 2015: PHP 7.0

PHP 7.0 introduced some major new syntax features: scalar types and return types being the most notable here. Nullable types aren't a thing yet, so we still need to use doc block types for our nullable $publishedAt:

class BlogData
{
    /** @var string */
    private $title;
    
    /** @var State */
    private $state;
    
    /** @var \DateTimeImmutable|null */
    private $publishedAt;
   
   /**
    * @param \DateTimeImmutable|null $publishedAt 
    */
    public function __construct(
        string $title,
        State $state,
        $publishedAt = null
    ) {
        $this->title = $title;
        $this->state = $state;
        $this->publishedAt = $publishedAt;
    }
    
    public function getTitle(): string
    {
        return $this->title;    
    }
    
    public function getState(): State 
    {
        return $this->state;    
    }
    
    /**
     * @return \DateTimeImmutable|null 
     */
    public function getPublishedAt() 
    {
        return $this->publishedAt;    
    }
}

# December 2016: PHP 7.1

With PHP 7.1 finally came nullable types, so we could remove some more doc blocks:

class BlogData
{
    /** @var string */
    private $title;
    
    /** @var State */
    private $state;
    
    /** @var \DateTimeImmutable|null */
    private $publishedAt;
   
    public function __construct(
        string $title,
        State $state,
        ?DateTimeImmutable $publishedAt = null
    ) {
        $this->title = $title;
        $this->state = $state;
        $this->publishedAt = $publishedAt;
    }
    
    public function getTitle(): string
    {
        return $this->title;    
    }
    
    public function getState(): State 
    {
        return $this->state;    
    }
    
    public function getPublishedAt(): ?DateTimeImmutable
    {
        return $this->publishedAt;    
    }
}

# November 2017: PHP 7.2

While there were some exciting features in 7.2 like parameter type widening and the object type, there's nothing we could do to clean up our specific DTO in this release.

# December 2018: PHP 7.3

The same goes for PHP 7.3, nothing to see here.

# November 2019: PHP 7.4

PHP 7.4 is a different story though! There now are typed properties — finally!

class BlogData
{
    private string $title;
    
    private State $state;
    
    private ?DateTimeImmutable $publishedAt;
   
    public function __construct(
        string $title,
        State $state,
        ?DateTimeImmutable $publishedAt = null
    ) {
        $this->title = $title;
        $this->state = $state;
        $this->publishedAt = $publishedAt;
    }
    
    public function getTitle(): string
    {
        return $this->title;    
    }
    
    public function getState(): State 
    {
        return $this->state;    
    }
    
    public function getPublishedAt(): ?DateTimeImmutable
    {
        return $this->publishedAt;    
    }
}

# November 2020: PHP 8.0

Another game changer: PHP 8 adds promoted properties; also, trailing commas in parameter lists are now a thing!

class BlogData
{
    public function __construct(
        private string $title,
        private State $state,
        private ?DateTimeImmutable $publishedAt = null,
    ) {}
    
    public function getTitle(): string
    {
        return $this->title;    
    }
    
    public function getState(): State 
    {
        return $this->state;    
    }
    
    public function getPublishedAt(): ?DateTimeImmutable
    {
        return $this->publishedAt;    
    }
}

# November 2021: PHP 8.1

Next, we arrive at PHP 8.1. Readonly properties are a thing, and allow us to write our DTO like so:

class BlogData
{
    public function __construct(
        public readonly string $title,
        public readonly State $state,
        public readonly ?DateTimeImmutable $publishedAt = null,
    ) {}
}

# November 2022: PHP 8.2

And finally, we arrive at PHP 8.2 — not released yet. Whenever a class only has readonly properties, the class itself can be marked as readonly, instead of every individual property:

readonly class BlogData
{
    public function __construct(
        public string $title,
        public State $state,
        public ?DateTimeImmutable $publishedAt = null,
    ) {}
}

That's quite the difference, don't you think?

It's interesting to see how the language has evolved over the course of almost a decade. If you had proposed the 8.2 syntax 10 years ago, you'd probably be called a madman. The same is true today, and I'm sure we'll look back at this point, ten years from now and wonder "how did we ever put up with that?".

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-07-15T00:00:00+00:00
<![CDATA[ PHP version stats: July, 2022 ]]> https://www.stitcher.io/blog/php-version-stats-july-2022 It's that time again: my biyearly summary of which PHP versions are used across the community. I know I'm a little early, that's because I had some spare time today and wanted to make sure I got it ready in time. You can read the January edition here.

As always, it's important to note that I'm working with the data available to us. That means that these charts are no 100% accurate representation of the PHP community as a whole, but they are an accurate representation of one of the most prominent parts of PHP: the packagist ecosystem.

# Usage Statistics

Let's start with the percentage of PHP versions being used today, and compare it to the previous two editions:

Version July, 2021 (%) January, 2022 (%) July, 2022 (%)
8.1 0.1 9.1 24.5
8.0 14.7 23.9 20.6
7.4 46.8 43.9 38.4
7.3 19.2 12.0 8.0
7.2 10.4 6.6 5.1
7.1 3.8 2.4 1.9

Note that I've omitted all versions that don't have more than 1% usage. Visualizing this data looks something like this:

Evolution of version usage

As expected during a year with a minor release instead of a major one: PHP 8.1 is growing, and PHP 8.0's usage is already declining. A good sign that developers are updating! Keep in mind that PHP 8.0 is still actively supported for another four months. So if you kept off on updating to PHP 8.1, now is a good time.

Less good news — although not unexpected: more than 50% of developers are still on PHP 7.4 or lower. That's not an insignificant number, considering that PHP 7.4 only receives security updates for 5 more months, and all older versions simply aren't supported anymore.

I did hope to see PHP 8.X adoption to be climbing more rapidly, I've shared some of my thoughts about it here, if you want some more reading.

Moving on the the all-time overview chart, here you can see the evolution of version usage across time:

All time evolution

It's interesting to compare the 5.5 peak in 2014 to the 7.4 peak two years ago. PHP 5.5 and subsequent versions saw a much faster decline as soon as PHP 7.0 became available, compared to PHP 7.4's decline when PHP 8.0 was released. I'm a little worried that PHP 8.0 wasn't as exciting as PHP 7.0 back in the day.

Fear for upgrading shouldn't be a blocker these days compared to eight years ago: we now have mature tools like Rector and PHP CS that take care of almost the whole upgrade path for you.

So why aren't people upgrading to PHP 8.0? Why are more people staying with PHP 7.4 compared to the 5.5 and 5.6 days? I don't have a definitive answer.

# Required versions

Part of the answer though (I think) lies with the open source community: what are packages requiring as their minimal version? Are they encouraging their users to update, or not?

I used Nikita's popular package analyzer to download the 1000 most popular composer packages. Next, I used a little script to get the lowest version each package supports from their composer.json file. Here are the results:

Version July, 2021 (#) January, 2022 (#) July, 2022 (#)
8.1 - - 125
8.0 117 160 94
7.4 56 69 86
7.3 133 116 104
7.2 142 133 130
7.1 182 190 153
7.0 31 29 29
5.6 61 49 42
5.5 43 42 35
5.4 41 43 40
5.3 97 83 77
5.2 12 10 10
5.0 2 2 1

I have mixed feelings about this data. On the one hand it's good to see PHP 8.1 as the minimum required version for 125 packages. However, look at how many packages still require a version lower than PHP 8.0: 707 out of 926 packages analysed. That's more than 75%!

Oh, as a side note: there only are 926 packages because some of the 1000 most popular packages don't specifically require a PHP version.

Let's plot this data into a chart:

Minimal PHP requirement over time

I won't say that the open source community is the only responsible factor here, but I do want to encourage you to think carefully about your responsibilities if you are an open source maintainer. We're not just talking about new and shiny PHP features here: we're talking about performance, software security for the most popular programming language on the web, and even about the impact of old PHP versions on electricity usage and server requirements, in Rasmus' words we can help save the planet.


What are your thoughts on these stats? Are you already using PHP 8.1? Let me know your thoughts on Twitter and subscribe to my newsletter if you want to be kept up-to-date about these posts!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-06-27T00:00:00+00:00
<![CDATA[ Stitcher turns 5 🎉 ]]> https://www.stitcher.io/blog/stitcher-turns-5 Some exciting news today: Stitcher — this blog — turned 5! In a way, that feels like a long time, although I was also thinking this week: "it feels longer than that". Anyway, it's a reason to celebrate! So first things first: I'm giving away 5 Stitcher-themed elephpants, keep on reading if you want one!

Before explaining how you can get an elephpant, let me say how much I appreciate it that you're here. I never imagined my blog to turn out the way it did when I first started it 5 years ago, and that's in large part thanks to you!

So, thank you!

Now, how to get an elephpant: I'm giving them away on Twitter, my newsletter and YouTube. By the time you're reading this, my newsletter is probably already sent, but you can still follow me on Twitter and check out YouTube if you want one.

Here's the YouTube video with more instructions:

Thanks for reading, and thank you for being here!

Brent

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-06-16T00:00:00+00:00
<![CDATA[ Clean and minimalistic PhpStorm ]]> https://www.stitcher.io/blog/clean-and-minimalistic-phpstorm This is a list of things I do when setting up PhpStorm from scratch. I prefer a clean and minimalistic look, which takes less than 5 minutes to set up. You can watch the full video, or scroll through the sections on this page!

# Hide all toolbars

I only use keyboard shortcuts and the actions menu, so I hide all toolbars.

# Light colour schemes

Light colour schemes are better. That's not just an opinion, it is a fact. I prefer a colour scheme that might be less cool and fancy, but forces me to properly light my room and put less strains on my eyes.

# Increased font size

Similar to light colour schemes, pick a font size and line height that works on your monitor, make sure you don't have to squint your eyes in order to read code.

# Code folding

I enable code folding for method bodies by default. It gives me a proper overview of classes, without too much noise. I've learned to use my keyboard for navigation and managing folds, so that it doesn't impact my performance.

# Tabs at the bottom

For some reason, placing tabs at the bottom of my screen feels way more natural and less intrusive.

# Undocked sidebar

Instead of moving sidebars to the right, I undock it, so that it slides on top of my code without moving that code around.

# The navigation bar

If you prefer an even cleaner look, you can hide the sidebar altogether and use the navigation bar instead.

# Distraction free mode

Distraction free mode centers my code, making it easier to read on large displays. By default it also hides tabs and line numbers, but you can show them again using the actions menu.

# Scopes and file colours

I use scopes to group and colour my code, allowing for easier navigation.


That's it! Take a look at my YouTube channel if you like these kinds of videos!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-06-08T00:00:00+00:00
<![CDATA[ My PHP enum style guide ]]> https://www.stitcher.io/blog/php-enum-style-guide This is my personal style guide for using enums in PHP. Each section describes the rule, but also the personal, opinionated reasoning behind the rule.

# 1. Uppercase

You must use uppercase notation for enum cases:

enum PostState
{
    case PENDING;
    case PUBLISHED;
    case STARRED;
    case DENIED;
}

# Why?

Enums are very close to "constant values". Even though the RFC showed them using PascalCase (inspired by other languages), it doesn't give a good reason for doing so.

Some people argue to use PascalCase for enums specifically to visually distinguish between constants and enums. I don't believe that relying on naming conventions to distinguish between concepts is a good idea. It's better to rely on your IDE's or editor's insights to know what something is, instead of holding onto a naming convention.

Ironically, uppercase constants exist because of the same reason: to distinguish them from normal variables. I want to acknowledge that fact, however there's little we can do to change decades of programming history. I think it's fair to say that the majority of PHP projects use uppercase constants, and so I would like to keep following that convention for closely related concepts, despite how the convention originated.

Nevertheless, I have tried using PascalCase for enums, but it always felt unnatural to me: I wrote my enum cases uppercase automatically, only to notice I did afterwards; meaning I had to go back and refactor them. I decided not to fight my instincts and just go with uppercase notation which feels the most natural to me, because of how constant values have been written for years, and how close enums are to constants anyway.

# 2. Don't overuse backed enums

You should only use backed enums when you actually have a need for them. A common use case is serialization. If an enum is only going to be used within code, you shouldn't make it a backed enum.

enum PostState: string
{
    case PENDING = 'pending';
    case PUBLISHED = 'published';
    case STARRED = 'starred';
    case DENIED = 'denied';
}

# Why?

Manually assigning values to enums introduces maintenance overhead: you're not only maintaining the enum cases itself, but also their values. It's important to take into consideration when to add overhead, and when not.

On top of that: enums have their built-in name property that's always accessible in case you need a textual representation:

Status::PUBLISHED->name; // PUBLISHED

When in doubt, apply this rule: do you need to create this enum from scalar values (from a database, from user input, …): use a backed enum. If the answer is "no" however, hold off assigning values until your use case requires it.

# 3. Simple match methods are allowed

Enums shouldn't contain complex methods. Ideally, they only contain methods using the match expression that provide richer functionality for specific enum cases:

enum Status
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;

    public function label(): string
    {
        return match($this) {
            Status::DRAFT => 'work in progress',
            Status::PUBLISHED => 'published',
            Status::ARCHIVED => 'archived',
        };
    }
}

# Why?

Enums shouldn't provide complex functionality, if you find yourself writing too much logic in your enums, you probably want to look into using the state pattern instead.

A good rule of thumb is that if it can fit into a match expression (without returning closures or callables or what not), you're good to go. Otherwise consider alternatives.

# 4. Values shouldn't be used as labels

A backed enum's value should only be used as a value for (de)serialization, not for functionality like providing labels.

# Why?

You might be tempted to rewrite the example from the previous point as follows:

enum Status
{
    case DRAFT = 'work in progress';
    case PUBLISHED = 'published';
    case ARCHIVED = 'archived';
}

While shorter, you're now mixing two concerns into one: representing labels in a user-readable format, combined with their serialization value. The first one is very likely to change over time, the second one should stay unchanged as much as possible to prevent complex data migrations.

# 5. No index values

You should only use integer backed enums when their values actually are integers, not to assign some notion of "order" or "indexing" to your enums.

enum VatPercentage: int 
{
    case SIX = 6;
    case TWELVE = 12;
    case TWENTY_ONE = 21;
}

enum Status: int 
{
    case DRAFT = 1;
    case PUBLISHED = 2;
    case ARCHIVED = 3;
}

# Why?

It might be tempting to assign integer values to, for example, status enums where DRAFT comes before PUBLISHED, comes before ARCHIVED.

While such sequential indexing works in a limited amount of cases, it's a mess to maintain. What happens when you're required to add another state called DENIED? It should probably get the value 2 or 3, meaning that you're stuck with data migrations for all published and archived entries.

When in doubt, you can again use the state pattern to model complex transitions between states and their order instead.


Do you agree? Disagree? Let me know on Twitter or via email!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-05-30T00:00:00+00:00
<![CDATA[ Attribute usage in top-1000 PHP packages ]]> https://www.stitcher.io/blog/attribute-usage-in-top-php-packages Attributes were originally introduced in PHP 8.0 two years ago. Yesterday, I came across an interesting post from Exakat: they analysed 535 open source packages to see which attributes were used the most often.

However: they didn't share their raw data or absolute numbers, and they also didn't mention which packages were analysed exactly and how.

I think we can do better. So let's take a closer look at how attributes are used in the PHP open source community.

# Setup

I used Nikita's Popular Package Analyser: it downloads the top-1000 packages from Packagist, and allows you to run analysis on them by writing simple PHP scripts.

In this case I looped over all PHP files, and scanned whether they contained attributes. You can check out the full script here.

A couple of remarks:

  • This is a quick and dirty script, it gets the job done and that's all it should do.
  • I filtered out JetBrains packages, because they only contain stubs for IDE usage, and heavily skew the result: JetBrains' packages alone account for ±65% of the total attribute usage.
  • Matching is done with a simple regex, I believe I took every edge case into account: names with and without backslashes and multiline and single-line attributes. If you believe there's something missing, don't hesitate to let me know.
  • You can browse the raw data here.
  • We should recognise the limits of this data set: client projects will probably use much more attributes compared to reusable packages. Think about Symfony's route attributes for example: they will only represent a very small share in this analysis, while it's reasonable to assume they are use significantly more in real projects.

The first metric that stood out is that #[\ReturnTypeWillChange] outnumbers all others by far: out of 2786 attributes in total, 1668 are either #[\ReturnTypeWillChange] or #[ReturnTypeWillChange], that's almost 60% being ReturnTypeWillChange attributes.

This attribute is the first built-in attribute in PHP, introduced in PHP 8.1, and meant to deal with a newly added deprecation notice. It was a design goal of the original attribute RFC to use them for these kinds of cases where we want userland code to communicate information to PHP's interpreter. In this case: to suppress a deprecation notice.

Speaking of the two variants (with and without a leading \): some developers import their attributes and others use the fully qualified class name: the latter option is by far the preferred one: out of 1668 ReturnTypeWillChange usages, only 524 imported the attribute — that's only 31%. It seems that most developers like to use the FQCN variant.

# The most active packages

Out of 997 packages, only 200 packages are using attributes. Out of those 200, Symfony, Drupal and Laravel have large pieces of the pie. Symfony is the leader here with 9.6%: not unlikely caused by the fact that Symfony has been using docblock annotations for years.

# Custom attributes

Another point of interest is the use of #[Attribute] and #[\Attribute]: representing custom attributes provided by packages themselves. In total there are 561 custom attributes provided by these packages.

Looking on a per-package basis: Symfony provides 88 custom attributes, with PHPUnit providing 49, and doctrine's mongodb implementation providing 42. Again a clear example of how Symfony is an early adopter, thanks to them being used to docblock annotations for years. Interestingly, Laravel provides no custom attributes.

# Multiline attributes

It's remarkable that there are no vendors using multiline attributes:

#[
    AsCommand,
    ReturnTypeWillChange,
]

It makes sense that vendors opt for the single-line notation, since it's compatible with older PHP versions because these lines are treated like comments before PHP 8.0:

#[AsCommand]
#[ReturnTypeWillChange]

# Conclusions

  • Only 20% of the top-1000 most popular packages use attributes
  • Only 23% of the top-1000 most popular packages have PHP 8.0 or higher as their minimum required version
  • The ReturnTypeWillChange attribute is by far the most used one
  • Symfony clearly is the frontrunner, embracing attributes thanks to experience with docblock annotations in the past
  • Laravel provides no custom attributes for their users, although they use some internally, mostly the AsCommand attribute, which is provided by Symfony

On a personal note: I'd say there's room for improvement. I think Laravel should start embracing custom attributes, especially since they now require PHP 8.0 as their minimum version.

What's your opinion? Let me know via Twitter or email!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-05-20T00:00:00+00:00
<![CDATA[ Dealing with deprecations ]]> https://www.stitcher.io/blog/dealing-with-deprecations You've probably dealt with them at one point your programming career:

Deprecated: Creation of dynamic property Post::$name 
is deprecated in /in/4IreV on line 10

Deprecation notices 🤢

Despite them being annoying and frustrating to many developers, they actually serve a purpose. I would even say you will appreciate them once you understand their goal, and how to deal with them. Let's take a look!

Oh, by the way: you can watch this blog post as a vlog if you prefer that:

# 1. Deprecations are helpful

It's a common complaint: "why do my PHP scripts break with minor version updates??". And quite right: PHP has a tendency to add deprecation notices in minor releases, which tend to be audibly present when upgrading a project. Take for example PHP 8.1, where suddenly logs were filled with these kinds of warnings:

Return type should either be compatible with 
IteratorAggregate::getIterator(): Traversable, 
or the #[ReturnTypeWillChange] attribute should be used 
to temporarily suppress the notice

It's important to understand what deprecations are about: they aren't errors, they are notices. They are a way of notifying PHP developers about a breaking change in the future. They want to warn you up front, to give you plenty of time to deal with that upcoming breaking change.

Of course, one could ask: are these breaking changes and fancy features really necessary? Do we really need to change internal return types like IteratorAggregate::getIterator(): Traversable, do we really need to disallow dynamic properties?

In my opinion — and it's shared by the majority of PHP internal developers — yes. We need to keep improving PHP, it needs to grow up further. And that sometimes means introducing a breaking change, like for example when internals add return types to built-in class methods: if you're extending IteratorAggregate in userland code, you will need to make some changes. The language needs to evolve.

Overall I'd say that, despite some of the annoyances that come with such an evolving language, it's for the better.

And luckily we have a mechanic like deprecation notices: they tell us that something will break in the future, but that we can still use it today. We can incrementally make changes and updates around our codebase.

# 2. Deprecations can be silenced

Second, PHP internals go to great lengths to help userland developers in dealing with deprecations. Thanks to the addition of attributes in PHP 8.0, we now have a much better and standardized way of communication between our code and PHP's interpreter.

For example: you can tag userland code with the ReturnTypeWillChange attribute in order to prevent deprecation notices being shown.

final class MyIterator implements \IteratorAggregate
{
    #[\ReturnTypeWillChange]
    public function getIterator()
    {
        // …
    }
}

Of course, this code will break in PHP 9.0, so while silencing deprecation notices is a short-term solution; you will need to fix them if you ever want to upgrade to the next major version:

final class MyIterator implements \IteratorAggregate
{
    public function getIterator(): \Traversable
    {

    }
}

One more example maybe? With dynamic properties being deprecated in PHP 8.2, you can mark classes with the AllowDynamicProperties attribute, making it so that they allow dynamic properties again and suppress the deprecation notice:

#[\AllowDynamicProperties]
class Post
{
}

// …

$post->title = 'Name';

# 3. They are notices, not fatal errors

PHP code will keep working just fine, even when parts of it trigger deprecation notices. Of course, you know by now that it's in your best interest to fix them if you ever want to upgrade to the next major version, but you don't need to do it right now. It's perfectly ok to upgrade your production projects and deal with deprecations notices over time.

I'd even recommend disabling deprecation notices on production altogether, or at least not show them to your end users:

error_reporting(E_ALL ^ E_DEPRECATED);

Maybe you can keep track of them using an external error tracker for the first months, to get a clear image of the places you'll need to fix those deprecations. But above all: deprecation notices shouldn't be blockers when upgrading to the latest minor PHP version.

# 4. Automation

Lastly, keep in mind that you don't need to do the boring tasks by hand. There are tools like Rector and phpcs that can take care of many upgrading issues for you. Usually it's a matter of running a script that takes a couple of minutes at most to scan and fix your codebase. That's work that you might need days for if you'd do it by hand.

It's not difficult or time consuming anymore to deal with PHP upgrades. In fact, deprecations help tremendously to create a smoother upgrade path and prepare your codebase for the future.

I like deprecations, you should too.

]]>
2022-05-18T00:00:00+00:00
<![CDATA[ Dynamic Strategies ]]> https://www.stitcher.io/blog/strategies Just a note up front: I wrote this post as a thought exercise, not as an absolute source of truth. I'd love to hear people disagree and tell me why, so don't hesitate to reply wherever you want.

You've probably used the strategy pattern before: a behavioral pattern that enables selecting an algorithm at runtime.

Let's consider a classic example: the user provides some input either in the form of XML, JSON or an array; and we want that input to be parsed to a pretty JSON string.

So all these inputs:

'{"title":"test"}'
'<title>test</title>'
['title' => 'test']

Would convert to this:

{
    "title": "test"
}

Oh, there's one more requirement: we need these strategies to be extensible. Developers should be allowed to add their own strategies for dealing with other kinds of inputs: YAML, interfaces, iterable objects, whatever they need.

Let's take a look at the classic solution, and its problems.


Usually, we start by introducing some kind of interface that all strategies should implement:

interface ParserInterface
{
    public function canParse(mixed $input): bool;
    
    public function parse(mixed $input): mixed;
}

Each strategy must define whether it can run on a given input, and provide its actual implementation.

Next we can provide several implementations of that interface:

final class ArrayParser implements ParserInterface
{
    public function canParse(mixed $input): bool
    {
        return is_array($input);
    }
    
    public function parse(mixed $input): mixed
    {
        return json_encode($input, JSON_PRETTY_PRINT);
    }
}

final class JsonParser implements ParserInterface
{
    public function canParse(mixed $input): bool
    {
        return 
            is_string($input) 
            && str_starts_with(trim($input), '{') 
            && str_ends_with(trim($input), '}');
    }
    
    public function parse(mixed $input): mixed
    {
        return json_encode(
            json_decode($input), 
            JSON_PRETTY_PRINT
        );
    }
}

final class XmlParser implements ParserInterface
{
    public function canParse(mixed $input): bool
    {
        return
            is_string($input) 
            && str_starts_with(trim($input), '<') 
            && str_ends_with(trim($input), '>');
    }
    
    public function parse(mixed $input): mixed
    {
        return json_encode(
            simplexml_load_string(
                $input, 
                "SimpleXMLElement", 
                LIBXML_NOCDATA
            ), 
            JSON_PRETTY_PRINT
        );
    }
}

Full disclosure: these are very naive implementations. The strategy detection in the canParse method simply looks at the first and last character of the input string, and probably isn't fool-proof. Also: the XML decoding doesn't properly work; but it's good enough for the sake of this example.

The next step is to provide a class that developers can use as the public API, this one will use our different strategies underneath. It's configured by adding a set of strategy implementations, and exposes one parse method to the outside:

final class Parser
{
    /** @var ParserInterface[] */
    private array $parsers = [];
    
    public function __construct() {
        $this
            ->addParser(new ArrayParser)
            ->addParser(new JsonParser)
            ->addParser(new XmlParser);
    }
    
    public function addParser(ParserInterface $parser): self
    {
        $this->parsers[] = $parser;
        
        return $this;
    }
    
    public function parse(mixed $input): mixed
    {
        foreach ($this->parsers as $parser) {
            if ($parser->canParse($input)) {
                return $parser->parse($input);
            }
        }
        
        throw new Exception("Could not parse given input");
    }
}

And we're done, right? The user can now use our Parser like so:

$parser = new Parser();

$parser->parse('{"title":"test"}');
$parser->parse('<title>test</title>');
$parser->parse(['title' => 'test']);

And the output will always be a pretty JSON string.

Well… let's take a look at it from the other side: a developer who wants to extend the existing parser with their own functionality: an implementation that transforms a Request class to a JSON string. We designed our parser with the strategy pattern for this exact reason; so, easy enough:

final class RequestParser implements ParserInterface
{
    public function canParse(mixed $input): bool
    {
        return $input instanceof Request;
    }
    
    public function parse(mixed $input): mixed
    {
        return json_encode([
            'method' => $input->method,
            'headers' => $input->headers,
            'body' => $input->body,
        ], JSON_PRETTY_PRINT);
    }
}

And let's assume our parser is registered somewhere in an IoC container, we can add it like so:

Container::singleton(
    Parser::class,
    fn () => (new Parser)->addParser(new RequestParser);
);

And we're done!

Except… have you spotted the one issue? If you've used the strategy pattern in this way before (many open source packages apply it), you might already have an idea.

It's in our RequestParser::parse method:

public function parse(mixed $input): mixed
{
    return json_encode([
        'method' => $input->method,
        'headers' => $input->headers,
        'body' => $input->body,
    ], JSON_PRETTY_PRINT);
}

The problem here is that we have no clue about the actual type of $input. We know it should be a Request object because of the check in canParse, but our IDE of course doesn't know that. So we'll have to help it a little bit, either by providing a docblock:

/**
 * @var mixed|Request $input 
 */
public function parse(mixed $input): mixed
{
    return json_encode([
        'method' => $input->method,
        'headers' => $input->headers,
        'body' => $input->body,
    ], JSON_PRETTY_PRINT);
}

Or by doing the instanceof check again:

public function parse(mixed $input): mixed
{
    if (! $input instanceof Request) {
        // error?
    }
    
    return json_encode([
        'method' => $input->method,
        'headers' => $input->headers,
        'body' => $input->body,
    ], JSON_PRETTY_PRINT);
}

So because of how we designed our ParserInterface, developers who want to implement it, will have to do double work:

final class RequestParser implements ParserInterface
{
    public function canParse(mixed $input): bool
    {
        return $input instanceof Request;
    }
    
    public function parse(mixed $input): mixed
    {
        if (! $input instanceof Request) {
            // error?
        }
        
        // …
    }
}

This kind of code duplication isn't the end of the world, at most it's a minor inconvenience. Most developers won't even bat an eye.

But I do. As a package maintainer, I want my public APIs to be as intuitive and frictionless as possible. To me, that means that static insights are a crucial part of the developer experience, and I don't want the users of my code to be hindered because of how I designed this parser.

So, let's discuss a couple of ways to fix this problem.

# No more duplication

If the problem of duplication happens because we've split our canParse and parse methods, maybe the easiest solution is to simply… not split them?

What if we design our strategy classes in such a way that they will throw an exception if they can't parse it, instead of using an explicit conditional?

interface ParserInterface
{
    /**
     * @throws CannotParse 
     *         When this parser can't parse 
     *         the given input. 
     */
    public function parse(mixed $input): mixed;
}

final class RequestParser implements ParserInterface
{
    public function parse(mixed $input): mixed
    {
        if (! $input instanceof Request) {
            throw new CannotParse;
        }
        
        // …
    }
}

Our generic parser class would change like so:

final class Parser
{
    // …
    
    public function parse(mixed $input): mixed
    {
        foreach ($this->parsers as $parser) {
            try {
                return $parser->parse($input);
            } catch (ParseException) {
                continue;
            }
        }
        
        throw new Exception("Could not parse given input");
    }
}

Of course, now we're opening up the rabbit hole of "what an exception is" and whether we're allowed to use exceptions to control our program flow in this way. My personal opinion is "yes, definitely"; because passing a string to a method that can only work with a Request object is in fact, an exception to the rule. At least, that's my definition.

Some people might opt for returning null instead of throwing an exception, although that feels more wrong to me: null doesn't communicate that this particular method wasn't able to handle the input. In fact, null could very well be a valid result from this parser, depending on its requirements. So no, no null for me.

However, I share the opinion that probably a couple of people have when reading this: either returning null or throwing an exception doesn't feel like the cleanest solution. If we're embarking on this journey for the sole purpose of fixing a detail that only a handful of developers might be bothered about, we might explore other options as well, and dive even deeper into the rabbit hole.

# Types

We've written this manual check to guard against invalid input: $input instanceof Request; but did you know there's an automated way for PHP to do these kinds of checks? Its built-in type system! Why bother rewriting stuff manually that PHP can do for us behind the scenes? Why not simply type hint on Request?

final class RequestParser implements ParserInterface
{
    public function parse(Request $input): mixed
    {
        // …
    }
}

Well we can't, because of two problems:

  • We're not allowed to narrow parameter types from mixed to Request according to the Liskov Substitution Principle, which is enforced by PHP and our ParserInterface; and
  • not every input can be represented as a dedicated type: both XML and JSON are strings, there's some ambiguity there.

So, end of story? Well… we're already so deep into the rabbit hole, we might as well give it a shot.

Let's start by imagining that the two problems mentioned aren't an issue: could we in fact design our parser in such a way that it's able to detect each strategy's accepted input, and select the proper strategy based on that information?

We sure can! The most simple solution is to loop over all strategies, try to pass them some input and continue if they can't handle it; let PHP's type system handle the rest:

final class Parser
{
    public function handle(mixed $input): mixed
    {
        foreach ($this->parsers as $parser) {
            try {
                return $parser->parse($input);
            } catch (TypeError) {
                continue;
            }
        }
        
        throw new Exception("Could not parse given input");
    }
}

I actually prefer this approach over any kind of runtime reflection trying to determine which method can accept which input. Let's not try to recreate PHP's type checker at runtime. The only real requirement for this approach to work is that your strategy methods won't have any side effects and that they'll always properly type hint their input. That's one of my personal cornerstones when programming, and so I have no problems writing code that assumes this principle.

Ok so it is possible to match any given input to its correct strategy based on its method signature. But we still need to deal with our two initial problems.

The first one is that we're not allowed to write this:

final class RequestParser implements ParserInterface
{
    public function parse(Request $input): mixed
    {
        // …
    }
}

Because we defined the signature of parse in our ParserInterface like so:

interface ParserInterface
{
    public function parse(mixed $input): mixed;
}

We can't narrow down parameter types, we can only widen them; that's called contravariance.

So on the one hand we have an interface that says that our strategies can take any type of input (mixed); but on the other hand we have our strategy classes that tell us they can only work with a specific type of input.

If we want to go further into the rabbit hole, then there's no other conclusion to make than that our interface isn't actually telling the truth: we're not making strategies that work with any kind of input, and so it doesn't make sense to have an interface tell us that we do. This interface is essentially telling a lie, and there's no reason to keep it.

Well, actually: there is a reason to have this interface: it guides a developer in understanding how they can add their own strategies, without having to rely on documentation. When a developer sees this method signature:

final class Parser
{
    // …
    
    public function addParser(ParserInterface $parser): self
    {
        $this->parsers[] = $parser;
        
        return $this;
    }
}

It's clear to them that they'll need to implement ParserInterface for their custom strategies to work. So I'd say that getting rid of this interface might do more harm than good, because without it, developers are operating in the dark.

There is one solution that I can think of that can counter this problem: accepting callables.

public function addParser(callable $parser): self
{
    $this->parsers[] = $parser;
    
    return $this;
}

callable is a special type in PHP, because it doesn't only cover functions and closures, but also invokable objects. The only real thing missing here is that we can't tell — with certainty — from our code what our callables should look like.

We've established a rule saying that it should accept any kind of input that it can work with, but there's no way we can tell developers extending our code that, without providing an additional docblock. This is definitely a downside of this approach, and might be reason enough for you not to go with it.

I personally don't mind, I think the code duplication we had in the beginning and manual type validation annoys me more than having to read a docblock:

/**
 * @param callable $parser A callable accepting one typed parameter.
 *                         This parameter's type is used to match 
 *                         the input given to the parser to the
 *                         correct parser implementation.
 */
public function addParser(callable $parser): self
{
    $this->parsers[] = $parser;
    
    return $this;
}

Then there's our second problem: not everything can be represented by a type. For example: both JSON and XML parsers should match on a string of either JSON or XML, and we can't type hint those. I can think of two solutions.

  • Do some manual checks in the parse method for these edge cases, and throw an TypeError when they don't match; or
  • introduce JsonString and XmlString as custom classes, and have a factory first convert those raw strings to their proper types.

The first option would look like this:

final class JsonParser
{
    public function __invoke(string $input): string
    {
        if (
            ! str_starts_with(trim($input), '{') 
            || ! str_ends_with(trim($input), '}')
        ) {
            throw new TypeError("Not a valid JSON string");   
        }
        
        return json_encode(
            json_decode($input), 
            JSON_PRETTY_PRINT
        );
    }
}

final class XmlParser 
{
    public function __invoke(string $input): string
    {
        if (
            ! str_starts_with(trim($input), '<') 
            || ! str_ends_with(trim($input), '>')
        ) {
            throw new TypeError("Not a valid XML string");
        }
        
        return json_encode(
            simplexml_load_string(
                $input, 
                "SimpleXMLElement", 
                LIBXML_NOCDATA
            ), 
            JSON_PRETTY_PRINT
        );
    }
}

The second one, having a custom class for JsonString and XmlString, would look something like this:

final class JsonParser
{
    public function __invoke(JsonString $input): string
    {
        return json_encode(
            json_decode($input), 
            JSON_PRETTY_PRINT
        );
    }
}

final class XmlParser 
{
    public function __invoke(XmlString $input): string
    {
        return json_encode(
            simplexml_load_string(
                $input, 
                "SimpleXMLElement", 
                LIBXML_NOCDATA
            ), 
            JSON_PRETTY_PRINT
        );
    }
}

But don't forget that we'd also need to introduce a factory to convert a string to its proper type, which means quite a lot of overhead.

On a final note, callable has another advantage: users aren't bound to using invokable classes. Depending on their needs and how they test, they could get away with simply adding closures:

Container::singleton(
    Parser::class,
    fn () => (new Parser)->addParser(
        fn (Request $request) => json_encode([
            'method' => $request->method,
            'headers' => $request->headers,
            'body' => $request->body,
        ], JSON_PRETTY_PRINT)
    );
);

Are there downsides to this approach? Definitely. Just like there are downsides to the original solution where we had lots of code duplication. I personally think that, from a developer experience point of view; it's worth considering alternatives to the original way of how we implement dynamic strategies; and I can imagine some projects benefiting from it.

What do you think? Let me know via Twitter or email; don't hesitate to say I'm slowly going crazy if you think so!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-04-06T00:00:00+00:00
<![CDATA[ Goodbye ]]> https://www.stitcher.io/blog/goodbye

I've got some important news: today marked my last day working at Spatie and I will start working at JetBrains in the PhpStorm team on Monday. I want to talk about that.

You might be wondering what happened. I actually didn't plan on leaving Spatie originally; I wasn't looking for a new job. It was back in September last year, that I was told about the PhpStorm team looking for a developer advocate; and that felt like an opportunity I couldn't pass.

If you'd ask me to describe my dream job, I would probably tell you it would focus on the PHP community, content creation and education; and this is everything my new job at JetBrains is focussed on. So, I don't think this comes as a surprise to anyone, and I think you'll understand why I made this decision.

I'll of course miss my colleagues at Spatie — I spent almost 5 years with them — but I'm also ready and very excited about this new adventure; I'll keep you posted!

# What about my blog, videos and newsletter?

Nothing much will change: I'll keep working on my blog and other personal projects in my free time; like I have been doing for the past years. I do find more creative energy in making videos over blog posts at the moment though, but I don't have any long term plans. I'll just keep doing what I like at the moment.

# What about open source?

I've written quite a lot of open source code at Spatie, and I'm happy to help maintain it. It's actually an important part of my new job to keep programming, just to keep in touch with the language. So while I won't work any more on client projects like I used to, I will keep writing PHP.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-03-25T00:00:00+00:00
<![CDATA[ The case for PHP generics ]]> https://www.stitcher.io/blog/generics-in-php-4 I started this series by saying it’s not just about teaching you, it’s also about making my case for what I think is the most viable and the most logical path to adding generics in PHP.

It’s up to you to decide after this video whether you agree or not. So, your honor, I’d like to start my closing statements.

Adding monomorphized or reified generics won’t happen. At least not according to Nikita who did an extensive amount of research on the topic. Both options either pose performance problems or simply require too much core code refactoring of PHP’s runtime type checker to be achievable within a reasonable amount of time.

However, if we think about the true value that generics bring, it’s not about runtime type checks. By the time PHPs runtime type checker kicks in and possibly throws a type error, we’re already running our code. Our program will crash. And I’ve never heard any user of my programs say “oh, it’s a type error, it’s ok”. No. The program crashed, and that’s the end of story.

Runtime type checks in PHP are a very useful debugging tool, I give you that, and in some cases required for type juggling. But most of the value of PHP’s type system comes from static analysis.

So, if we want generics in PHP we need a mind shift:

First, developers need to embrace static analysis. The irony here is that developers who want generics and understand their value, also understand the value of static type checkers. So while there is a group of PHP developers who don’t care about static analysis, they also shouldn’t care about the value that generics bring. Because, these two: generics and static type checking, simply cannot be separated.

Second, if PHP internals decide that statically checked generics have their place in PHP; they should wonder whether static analysis should be left as a responsibility with the community, or whether they should play a role in it. Either by creating a specification that every static analyser should follow, or by shipping their own static type checker. The second one would definitely be preferable, but you can imagine what an undertaking that would be. I don’t think that relying on proven third part tools should be an issue.

Third, type juggling simply wouldn’t be possible anymore, at least not when using generics. You’d have to trust your static type checker. This is a way of programming that PHP developers aren’t really used to, but many other languages do exactly this, and it works fine. A static type checker is incredibly powerful and accurate. I can imagine it’s difficult for PHP developers to understand the power of statically typed languages without having used one before. It’s worth looking into a language like Rust, Java, or even TypeScript, just to appreciate the power of static type systems. Or you could start using one of PHP’s third party static analysers: Psalm or PHPStan.

To summarize: if we want generics in PHP, with all the benefits they bring to static analysis, we need to accept the fact that runtime erased generics are the only viable path.

In closing, a few more remarks I’d address.

First there’s the argument that what I’m describing is already possible with docblocks. If you go back to the second post in this series, you’ll find me explaining the differences in detail, but let me quickly summarize:

  • Docblocks don’t communicate the same importance to developers as built-in syntax does, which is also why we got attributes in PHP 8; built-in syntax has a value over docblocks
  • And, also, there’s no official specification of what generic annotations should look like when using doc blocks. That’s a big issue today, with all three major static analysers having slightly different implementations.

A second remark is that, even with type erasure, we could still expose generic type information via the reflection API. I’m not saying that the type information should be completely gone at runtime, my foremost concern is that PHP shouldn’t check generic types at runtime. I’m not sure what the impact would be on PHP’s core to have generic type information available via reflection; so I’m just putting it out there that I’m not against that idea.

And finally, there is of course another solution. One that anyone could pursue in theory. One that has proven itself in the past: TypeScript. It could be possible for a superset of PHP to exist that compiles to normal PHP, and while compiling doing lots of type checks and other cool stuff. TypeScript is immensely popular, and I think that if there’s room for a similar approach in serverside languages, PHP is probably a good candidate. However, TypeScript didn’t just magically appear overnight. It was created by experienced language designers, it’s magnitudes larger than adding runtime-ignored generics in PHP. But who knows, maybe one day.

With all of that being said, I hope that you found this series useful and educational; I said everything I wanted to about generics. I’d appreciate it if you shared this series with your colleagues and followers — I believe it’s an important topic and I want to see things change.

I rest my case.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-03-25T00:00:00+00:00
<![CDATA[ Why we can't have generics in PHP ]]> https://www.stitcher.io/blog/generics-in-php-3 We’re going to take a deep dive in what’s going on behind the scenes when it comes to generics and PHP. It’s super interesting, and very important to understand why generics aren’t supported yet as first-class citizens in PHP.

Let’s get started.

Generics aren’t coming to PHP. That was Nikita’s conclusion last year. It simply wasn’t doable.

To understand why Nikita said that, we need to look at how generics could be implemented. In general, there are three possible ways to do so; programming languages that do support generics mostly use one of these three methods.

The first one is called Monomorphized Generics. Let’s go back to the first post of this series where I showed this collection example:

class StringCollection extends Collection
{
    public function offsetGet(mixed $key): string 
    { /* … */ }
}

class UserCollection extends Collection
{
    public function offsetGet(mixed $key): User 
    { /* … */ }
}

I explained that we could manually create implementations of the collection class for each type that we needed a collection for. It would be lots of manual work, there’d be lots of code, but it would work.

Monomorphized generics do exactly this, but in an automated way, behind the scenes. At runtime, PHP would not know about the generic Collection class, but rather about two or more specific implementations:

$users = new Collection<User>();
// Collection_User

$slugs = new Collection<string>();
// Collection_string

Monomorphized generics are a totally valid approach. Rust, for example, uses them. One advantage is that there are a bunch of performance gains, because there are no more generic type checks at runtime, it’s all split apart before running the code.

But that immediately brings us to the problem with monomorphized generics in PHP. PHP doesn’t have an explicit compilation step like Rust to split one generic class into several specific implementations; and, on top of that: monomorphized generics do require quite a lot of memory, because you’re making several copies of the same class with a few differences. That might not be as big an issue for a compiled Rust binary, but it is a serious concern for PHP code being run from a central point, the server; maybe serving hundreds or thousands of requests per second.

The next option is Reified Generics. This is an implementation where the generic class is kept as-is, and type information is evaluated on the fly, at runtime. C# and Kotlin have reified generics, and it’s the closest to PHP’s current type system, because PHP does all its type checks at runtime. The problem here is that it would require an immense amount of core code refactoring for reified generics to work, and you can imagine some performance overhead creeping in, as we’re doing more and more type checks at runtime.

That brings us to the last option: completely ignore generics at runtime. Act like they are not there; after all, a generic implementation of, for example, a collection class would work with every kind of input anyway.

So if we ignore all generic type checks at runtime, there aren’t any problems.

Well, not so fast. Ignoring generic types at runtime — it’s called type erasure by the way, Java and Python do it — it poses some problems for PHP.

For one: PHP not only uses types for validation, it also uses type information to convert values on the fly from one type to another — that’s the type juggling I mentioned in the first post of this series:

function add(int $a, int $b): int 
{
    return $a + $b;
}

add('1', '2') // 3;

If PHP ignored the generic type of this “string” collection, and we’d accidentally add an integer to it, it wouldn’t be able to warn us about that, if the generic type was erased:

$slugs = new Collection<string>();

$slugs[] = 1; // 1 won't be cast to '1'

The second, and more important problem with type erasure — maybe you’re already yelling it at your screen by now — is that the types are gone. Why would we add generic types, if they are erased at runtime?

It makes sense in Java and Python, because all type definitions are checked before running the code using a static analyser. Java for example runs a built-in static analyser when compiling code; something that PHP simply doesn’t do: there is no compilation step, and there certainly isn’t a built-in static type checker.

On the other hand… all the advantages of type checking, the ones we discussed in the previous posts; they don’t come from PHP’s built-in runtime typechecker. By the time PHP’s type checker tells us something is wrong, we’re already running the code. A type error essentially crashes our program.

Instead, most of the added value of type checks comes from static analysers that don’t require us to run our code. They are pretty good at making sure there can be no runtime type errors, as long as you, the programmer, provide enough type information. That doesn’t mean there can’t be any bugs in your code, but it is possible to write PHP code that’s completely statically checked and doesn’t produce any type errors while running. And on top of that: there are all the static insights that we get while writing code; that’s by far the most valuable part of any type system, and has nothing to do with runtime type checks.

So do we actually need runtime type checks? Because that’s the main reason why generics can’t be added in PHP today: it’s either too complex or too resource intensive for PHP to validate generic types at runtime.

That’s next time, in the last post of this series.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-03-24T00:00:00+00:00
<![CDATA[ Generics in depth ]]> https://www.stitcher.io/blog/generics-in-php-2 I showed a very boring example of generics in the previous post, we’re going to do better in this one.

$users = new Collection<User>();

$slugs = new Collection<string>();

Collections; they are probably the easiest way to explain what generics are about, but they also are the example that everyone talks about when discussing generics. It’s actually not uncommon for people to think that “generics” and “collections with a type” are the same thing. That’s definitely not the case.

So let’s take a look at two more examples.

Here’s a function called app — if you work with a framework like Laravel, it might look familiar: this function takes a class name, and will resolve an instance of that class using the dependency container:

function app(string $className): mixed
{
    return Container::get($className);
}

Now, you don’t need to know how the container works, what’s important is that this function will give you an instance of the class that you request.

So, basically, it’s a generic function; one whose return type will depend on what kind of class name you gave it. And it would be cool if our IDE and other static analysers also understand that if I give the classname “UserRepository” to this function, I expect an instance of UserRepository to be returned, and nothing else:

function app(string $className): mixed
{ /* … */ }

app(UserRepository::class); // ?

Well, generics allow us to do that.

And I guess this is a good time to mention that I’ve been keeping a secret, kind of: I previously said that generics don’t exist in PHP; well, that’s not entirely true. All static analysers out there — the tools that read your code without running it, tools like your IDE — they have agreed to use doc block annotation for generics:

/**
 * @template Type
 * @param class-string<Type> $className
 * @return Type
 */
function app(string $className): mixed
{ /* … */ }

Granted: it’s not the most pretty syntax, and all static analysers are relying on a simple agreement that this is the syntax — there’s no official specification; but nevertheless: it works. PhpStorm, Psalm and PhpStan — those are the three largest static analysers in the PHP world — understand this syntax to some degree.

IDEs like PhpStorm use it to give the programmer feedback when they are writing code, and tools like Psalm and PhpStan use it to analyse your codebase in bulk and detect potential bugs, mostly based on type definitions.

So actually, we can build this app function in such a way that our tools aren’t operating in the dark anymore. Of course, there’s no guarantee by PHP itself that the return type will be the correct one — PHP won’t do any runtime type checks for this function; but if we can trust our static analysers to be right, there’s very little — or even no chance of this code breaking when running it.

This is the incredible power of static analysis: we can actually be sure that, without running our code; most of it will work as intended. All of that thanks to types — including generics.

Let’s look at an even more complex example:

Attributes::in(MyController::class)
    ->filter(RouteAttribute::class)
    ->newInstance()
    ->

Here we have a class that can “query” attributes and instantiate them on the fly. If you’ve worked with attributes before you know that their reflection API is rather verbose, so I find this kind of helper class very useful.

When we use the filter method, we give it an attribute’s class name; and afterwards calling the newInstance method, we know that the result will be an instance of our filtered class. And again: it would be nice if our IDE understood what we’re talking about.

You guessed it: generics allow us to do that:

/** @template AttributeType */
class Attributes
{
    /**
     * @template InputType
     * @param class-string<InputType> $className
     * @return self<InputType>
     */
    public function filter(string $className): self
    { /* … */ }
 
    /**
     * @return AttributeType 
     */   
    public function newInstance(): mixed
    { /* … */ }
    
    // …
}

I hope you start to see how powerful simple type information can be. A couple of years ago, I would have needed an IDE plugin for these kinds of insights to work, now I just need to add some type information.

This latest example doesn’t only rely on generics though, there’s another equally important part that’s in play. Type inference: the ability of a static analyser to “guess” — or reliably determine — a type without the user specifying it. That’s what’s happening with that class-string annotation over there. Our IDE is able to recognise the input we give this function as a class name, and infer that type as the generic type.

So, everything’s solved, right: generics are available in PHP and all major static analysers know how to work with them. Well… there’s a couple of caveats.

First of, there’s no official spec of what generics should look like, right now every static analyser could push its own syntax; they happen to have agreed on one, for now; but there are little future guarantees.

Second: doc blocks are, in my opinion, suboptimal. They feel like a less important part of our codebase. And granted: generic annotations only provide static insights and no runtime functionality, but we’ve seen how powerful static analysis can be, even without runtime type checks. I think it’s unfair to treat type information as “doc comments”, it doesn’t communicate the importance of those types within our code. That’s why we got attributes in PHP 8: all functionality that attributes provide, was already possible with docblock annotations, but that just didn’t feel good enough. The same goes for generics.

And finally: without a proper specification, all three major static analysers have differences between their generics implementations. PhpStorm being the one most lacking at the moment. Ideally, there would be an official specification coming from PHP’s internals. Right now, there isn’t.

These are the main reasons why I believe that it’s worth investing time in a more permanent and sustainable solution. So why doesn’t PHP have proper generics yet? Why do we rely on doc blocks without a clear specification?

That’s for the next post!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-03-23T00:00:00+00:00
<![CDATA[ Generics in PHP: The basics ]]> https://www.stitcher.io/blog/generics-in-php-1 Generics in PHP. I know I’d want them. And I know a lot of developers who agree. On the other hand, there is a group of PHP programmers, maybe even larger, that say they don’t know what generics are, or why they should care.

I’m going to do a series on this blog about generics and PHP. We’ll start from the beginning, but quickly work our way to the more complex topics. We’ll talk about what generics are, why PHP doesn’t support them, and what’s possible in the future.

Let’s get started.

Every programming language has some kind of type system. Some languages have a very strict implementation, while others — PHP falls in this category — are much more lenient.

Now, type systems are used for a variety of reasons; the most obvious one is type validation.

Let’s imagine we have a function that takes two numbers, two integers; and does some kind of maths operation on them:

function add($a, $b) 
{
    return $a + $b;
}

PHP will happily allow you to pass any kind of data to that function, numbers, strings, booleans, doesn’t matter. PHP will try its best to convert a variable whenever it makes sense, like for example adding them together.

add('1', '2');

But those conversions — type juggling — often lead to unexpected results, if not to say: bugs and crashes.

add([], true); // ?

Now, we could manually write code to check whether our maths addition will work with any given input:

function add($a, $b) 
{
    if (!is_int($a) || !is_int($b)) {
        return null;
    }
    
    return $a + $b;
}

Or we could make use of PHPs built-in type hints — built-in shorthands for what we’d otherwise do manually:

function add(int $a, int $b): int 
{
    return $a + $b;
}

Many developers in the PHP community say they don’t really care about these type hints because they know they should only pass integers to this function — they wrote it, after all.

However, that kind of reasoning quickly falls apart: you’re often not the only one working in that codebase, you’re also using code that you haven’t written yourself — think about how many packages you’re pulling in with composer. And so, while this example in isolation might not seem to be that big a deal, type checking does come in handy once your code starts to grow.

Besides that, adding type hints not only guards against invalid state, but they also clarify what kind of input is expected from us, programmers. Types often make it so that you don’t need to read external documentation, because much of what a function does is already encapsulated by its type definition.

IDEs make heavy use of this principle: they can tell the programmer what kind of input is expected by a function or what fields and methods are available on an object — because it belongs to a class. IDEs make our code writing so much more productive, in large part because they can statically analyse type hints across our codebase.

Keep that word in mind: static analysis — it’s going to be very important later in this series. It means that programs, IDEs or other kinds of “static analysers” can look at our code, and without running it tell us whether it will work or not — at least, to some degree. If we’re passing a string to our function that takes an integer, our IDE will tell us we’re doing something wrong — something that would lead to a crashing program at runtime; but our IDE is able to tell us without having to actually run the code.

On the other hand, type systems have their limitations. A common example is a “list of items”:

class Collection extends ArrayObject
{
    public function offsetGet(mixed $key): mixed 
    { /* … */ }
    
    public function filter(Closure $fn): self 
    { /* … */ }
    
    public function map(Closure $fn): self 
    { /* … */ }
}

A collection has a bunch of methods that work with any kind of input: looping, filtering, mapping, you name it; a collection implementation shouldn’t care about whether it’s dealing with strings or integers.

But let’s look at it from an outsider’s perspective. What happens if we want to be sure that one collection only contains strings, and another one only contains User objects. The collection itself doesn’t care when looping over its items, but we do. We want to know whether this item in a loop is a User or a string — that’s quite the difference. But without proper type information, our IDE is operating in the dark.

$users = new Collection();

// …

foreach ($users as $user) {
    $user-> // ?
}

Now, we could create separate implementations for each collection: one that only works with strings, and another that only works with User objects:

class StringCollection extends Collection
{
    public function offsetGet(mixed $key): string 
    { /* … */ }
}

class UserCollection extends Collection
{
    public function offsetGet(mixed $key): User 
    { /* … */ }
}

But what if we need a third implementation? A fourth? Maybe ten or twenty. It becomes quite painful to manage all that code.

That’s where generics come in.

Now, to be clear: PHP doesn’t have generics. That’s a bold statement cutting quite a lot of corners, and we’re coming back to that later in this series. But for now it’s sufficient to say that what I’m showing next isn’t possible in PHP. But it is in many other languages.

Instead of creating a separate implementation for every possible type, many programming languages allow developers to define a “generic” type on the collection class:

class Collection<Type> extends ArrayObject
{
    public function offsetGet(mixed $key): Type 
    { /* … */ }
    
    // …
}

Basically we’re saying that the implementation of the collection class will work for any kind of input, but when we create an instance of a collection, we should specify a type. It’s a generic implementation, but it’s made specific depending on the programmer’s needs:

$users = new Collection<User>();

$slugs = new Collection<string>();

It might seem like a small thing to do: adding a type. But that type alone opens a world of possibilities. Our IDE now knows what kind of data is in a collection, it can tell us whether we’re adding an item with the wrong type; it can tell us what we can do with items when iterating over a collection, it can tell us whether we’re passing the collection to a function that knows how to work with those specific items.

And while we could technically achieve the same by manually implementing a collection for every type we need; a generic implementation would be a significant improvement for you and me, developers who are writing and maintaining code.

So, why don’t we have generics in PHP? What other things can we do with them besides a boring collection? Can we add support for them? We’re going to answer all those questions in this mini series. And to be clear up front: my goal with this series is to teach you about generics, but equally important is that I want to create awareness about how we’re missing out with PHP. I want that to change.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-03-22T00:00:00+00:00
<![CDATA[ PHP in 2021 (video) ]]> https://www.stitcher.io/blog/php-in-2021-video Back in December, I made this video looking back on PHP in 2021. You can watch it (make sure to like and subscribe if you liked it), or you can read the transcript here if you don't like watching videos.


It’s the end of 2021 and I still hear some people say they think PHP is dead. They are wrong, let’s talk about that.

Let’s start with the language itself. It’s been actively developed for the past decade, with a new release every year. And just to name a few features that have been added recently:

  • Enums
  • The match operator
  • Named arguments
  • Typed and readonly properties
  • FFI which stands for foreign function interface and allows cross-language communication
  • Property promotion
  • Fibers — also known as green threads or coroutines
  • Null coalescing
  • Array spreading
  • The JIT — a just-in-time compiler
  • Preloading
  • And Attributes — also known as annotations for better meta programming

And these are just my favourites, there’s much more. All of that to say that PHP is a rich language these days. On top of that, static analysis has grown in popularity over the years, allowing for generics, closure definitions and quite a lot more; and we see more and more people and projects adopting static analysis tools like Psalm and PHPStan. It’s quite powerful.

Finally, when it comes to the language itself: just recently the PHP foundation was announced. A non-profit with the goal to support, promote, and advance PHP. They are backed by companies like

  • Jetbrains
  • Automattic (that’s the company behind wordpress)
  • Symfony
  • Laravel
  • Packgist
  • Zend
  • and Craft;

just to name a few. Their only focus is to be able to pay core developers so that they can work on PHP full time. The foundation was announced less than a month ago, and has raised Two hundred fourteen thousand dollars to date, with a yearly goal of slightly less than three hundred thousand.


Besides the language itself — which I can assure you is stable and very much alive — PHP has a large and active community. Packagist is the most popular package manager, and has a total of almost fifty billion downloads. With around one point five billion new downloads each month.

Next there are the immensely popular web frameworks live Symfony and Laravel, and CMSs like WordPress and Craft.

There’s also the async community — don’t be mistaken: PHP can perfectly run asynchronously like for example you’d do with node. There’s Swoole and RoadRunner which are low-level components that optimize PHP’s runtime for asynchronous programming, but there also are high-level packages that you can plug into any project: packages like Amp and ReactPHP.


Finally, PHP is just a fun language to work with. It’s mature, performant, has a huge community, and is actively developed. I like working with PHP. You might prefer other languages which is totally fine, but there’s absolutely no reason to say that PHP is dead. It’s very much alive.

]]>
2022-01-24T00:00:00+00:00
<![CDATA[ Dealing with dependencies ]]> https://www.stitcher.io/blog/dealing-with-dependencies This post was originally sent to my newsletter.

I've written a lot about PHP 8.1 these past months. Not just about the release itself, but also about how important it is to keep up-to-date with PHP versions.

I don't want to leave you with empty words though, so I want to share some actionable advice on how to deal with updating external dependencies. Because, let's face it, you want to update your project to PHP 8.1, but some of your dependencies don't support it. So, end of story, right?

Here's what I do in such cases.

First off, I don't wait for the official PHP release to start testing my code. This goes for client and private projects, as well as open source projects. You can safely start testing once PHP's release candidates arrive. You might even discover a bug or two in PHP's core, helping them along the way.

Next, if you encounter an external dependency that doesn't support the latest PHP version yet, try and send a pull request to fix it yourself. Open source maintainers will often be very thankful for your contribution. It might be best to check the open issues, pull requests and discussions though, maybe it's already being worked on.

Of course, it's not always possible to send a PR on your own. Either you don't know the codebase well enough or don't have the time. The next step is to reach out to the maintainers, politely ask if they can provide PHP X.X support and whether you can help in any way. Even when you're not able to actually code the required changes, you might be able to test those changes in your project, and provide early feedback.

The benefit of starting early, is that you're not as much pressured for time. Maybe it'll take maintainers a bit longer than you anticipated, so starting early is only beneficial.

Let's fast-forward in time. PHP 8.1 is released, and one of your dependencies still hasn't been updated. Either the maintainer seems to be inactive, or there's a roadblock that can't be solved in a reasonable amount of time. Start looking for alternatives. This is the reason why you shouldn't pull in any dependency into your project without doing your research first. You should look into who and how many people are maintaining it, whether their work is funded, whether there's recent activity in the repository or not, and how many open and stale issues there are. It's best to do this kind of research before, instead of having to deal with it right when want to upgrade your project.

It's impossible to predict the future though, so you'll have to deal with inactive projects one day or another.

If you can't find any alternative dependencies, or none that can be implemented in a reasonable amount of time; you can consider forking the package and pulling it into your project. This should always be a last-resort option, but it might be worth it. Realise that you're adding tons of technical debt to your project by doing so though, so carefully consider the pros and cons. Also avoid trying to maintain a public fork, unless you're really motivated to do so.

Now, this strategy seems to work fine in client projects; but what about open source projects that have dependencies themselves? Things get a little more difficult when your open source code turns out to be dependant on other code, code that doesn't support the latest PHP version. Our last resort — forking — isn't as trivial when hundreds or thousands of projects depend on that code as well.

If you're an open source maintainer, I'd say the rule about picking your dependencies is even more important for your open source code. Forking "in the open" comes with a lot of headaches that you want to avoid at all costs.

While it's still a valid strategy in some cases, it might be worth to look at it from a different angle.

Being active in the Laravel community, I actually think we're rather fortunate. There are companies that pay employees to work on open source code. Laravel itself is the best example. I spoke with Dries in preparation for this post, one of the maintainers of Laravel. I asked him about the most difficult things when it comes to dealing with external dependencies from Laravel's point of view. He said it's mostly a waiting game. On top of that there's lots of communication between them and external package maintainers to get everything working.

So if you're in open source, the best thing you can do is to carefully consider what packages you depend on. Keep a good relation with those maintainers and try to help where you can. If, on the other hand, you're an open source user; you or your company should consider supporting the people you depend on. They are doing incredibly important work; most of the time, for free.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

That's all I've got for you today. Though I'm wondering: are you running up-to-date PHP versions? If not, what's keeping you back? Are external dependencies the problem, or maybe you're dealing with server constraints? Maybe it has to do with time or budget issues or something else? Let me know via email or Twitter, I'm curious!

]]>
2022-01-19T00:00:00+00:00
<![CDATA[ "It's your fault" ]]> https://www.stitcher.io/blog/its-your-fault I read an intriguing blog post yesterday. It's an old one dating back to 2008, titled "Even if a function doesn’t do anything, you still have to call it if the documentation says so, because it might do something tomorrow". That's quite a mouthful, but the article itself is rather short; I can summarise it one paragraph for you.

There used to be a function in Windows Kernel that didn't do anything. Yet, the docs told programmers that this function had to be called after calling another one: if you called GetEnvironmentStrings, you also needed to call FreeEnvironmentStrings. However, many programmers didn't bother to do so because it was pointless: FreeEnvironmentStrings didn't do anything, it was literally an empty function. A couple of years later, that function actually got an implementation, and many applications started to break because their programmers never bothered to call it in the first place.

The article summarises it as follows:

If the documentation says that you have to call a function, then you have to call it. It may be that the function doesn’t do anything, but that doesn’t prevent it from doing something in the future.

Or, how I like to phrase it: some of the most crappy software design possible.

There was a whole debate in the comment section on whether Microsoft or the developers — the users of Microsoft's code — messed up. After all: the docs explicitly told their users they needed to call that function, so if they didn't follow the rules, they were on their own.

There were a couple of commentators calling out Microsoft though:

Why not instead design the API such that the programmer cannot fail to use it correctly and still have their program compile?

Source

And:

As a sometimes systems-programmer myself, I'm dismayed that it took until just a couple of comments ago before somebody pointed out that it was ALSO a dumb thing to stick in a do-nothing function call and then assume people would call it by contract.

Uh, did that REALLY seem like a good idea to anybody? By Windows NT 4, had we not all figured out a long time ago that many application developers are not going to do things exactly the way you tell them?

This is MS's error first; the app developer's second. Neither one is right, but MS was more wrong.

Source

In my opinion, these are some sensible arguments. Users in general can't be trusted to, one, read the docs and two, follow rules that don't seem to make sense at that point in time. That's just not how to world works.

But ok, this was 2008 and we've learned to do better now, haven't we? Well… it's actually still very common to write code that's going to be used by others, and assume those users will know how to use that code responsibly. I personally know plenty of people who follow this mindset. I respect them very much, and they also know I disagree with them on this opinion.

Looking at it from the user perspective though, this mindset is suboptimal as well: if the code itself isn't clear on how it should be used, there's a level of uncertainty introduced in the user's mind. "Am I missing something here?" "Should I read every docs page available before actually using this code?"

I think it's better software design, for vendors and users alike, to make our code as explicit and robust as possible, with as little room for interpretation and uncertainty as possible.

In my opinion, that means:

  • Using a proper type system (as strict as possible)
  • Not allowing classes to be extended, unless it's by design, final by default
  • Not allowing state to be writeable from the outside, unless it's by design, readonly by default
  • Only adding methods to your public API that actually should be publicly accessible, private by default
  • Using explicit, clear names everywhere
  • Programming to an interface instead of an implementation

It's better software design, for vendors and users alike, to make our code as explicit and robust as possible, with as little room for interpretation and uncertainty.

To me, it's not a matter of distrust, it's about writing code that's clear about what it does, without having to dig through a set of rules written in the docs somewhere far away from your IDE.

There's only very little code around these days that doesn't need extra explanation, and I think we can do better than that.

]]>
2022-01-15T00:00:00+00:00
<![CDATA[ Twitter home made me miserable ]]> https://www.stitcher.io/blog/twitter-home-made-me-miserable This post was originally sent to my newsletter in November, 2021. I wanted to share it here as well, just to hear some more thoughts.


Two months ago, I decided to give Twitter Home another chance: the timeline that Twitter fills using their algorithm instead of just chronologically showing tweets of people you follow. I wanted to discover interesting new people to follow and I figure Twitter would fill my feed with tweets related to my interests; based on what and who I liked, retweeted and followed over the years.

I was wrong.

After two months, I now only see tweets of:

  • self proclaimed "tech-gurus" who don't have much, if any, up-to-date experience with real-life projects and clients;
  • people trying to sell me their latest products;
  • people showing off their new MacBook pro;
  • people going out of their way trying to spread "positive vibes" to the point that it becomes a little cringey;
  • people who use threads, which I don't find a very efficient way of communicating longer and coherent thoughts.

And just to be clear, I'm not mad at any of those people; most of them actually tweet interesting stuff as well; but Twitter simply didn't show those more interesting tweets in my Home feed.

It turns out their algorithm is rather picky, especially when it comes to tweeting external sources:

On average, 51% of tweets in chronological timelines contained an external link, compared to just 18% in the algorithmic timelines

Unfortunately for me, I find external sources (blog posts, news articles, etc) often the most relevant and insightful; and Twitter deliberately filters them out when discovering new people.

So after using Twitter Home for two months, I felt genuinely miserable every time I opened my feed. I knew that almost no tweet would interest me, and I'm sad that the Twitter algorithm doesn't work better for my case. I'm sure Twitter is well aware of how their algorithm works, and I'm sure it yields the best results for the majority of their users, but I apparently don't belong in that group.

So, here's where I need your help: I really want to discover more interesting people online; people who write about PHP, webdev, and programming; people who dare to challenge ideas that we take for granted; content that makes us think outside our box.

But how do I find those people? Twitter clearly isn't the best platform and I find Reddit and HackerNews either too focussed or too broad. So what's left? Any ideas? I would very much appreciate your input!

]]>
2022-01-14T00:00:00+00:00
<![CDATA[ How I plan ]]> https://www.stitcher.io/blog/how-i-plan I love getting mail. If you're a regular reader of this blog, you probably know that by now. I received a mail today from Muhammad, asking me a question that's not directly related to programming, though maybe it might interest some programmers nevertheless.

Here's what he asked:

I struggle in one area and I would like your help in that regard. I wonder how you plan your week, month, year and 5 years? Developers like me can learn coding and programming techniques; but struggle to set a goal, set proper time and follow a plan.

It only rarely happens, but this time it did: this question actually made me think really hard about my own progress, and I think it's worth sharing it.


First off, I should make a distinction between doing client work, and what I like to call "creative" work. Client work pays the bills, but creative work is what really gives me energy. I have a very different approach in handling them both.

Let's start with client work (the most boring part of the answer): I've been working on the same project for over three years now. I work on it together with a team of wonderful colleagues. I'm the one who has been communicating directly with the client though: analyzing the problem space, setting goals and deadlines, etc.

There's only one thing I can think of that I've come to find incredibly important: clear communication. Check and double check that the work we're doing is what the client is expecting, and clearly communicate about how we're doing in terms of time and budget. We have regular meetings just to talk about how it's going. We both understand that things can change, and we both are adaptable. But we also know that change comes at a price, and we respect that.

So: honest communication and building a meaningful relation between us and our clients are what I consider to be the key. Everything planning related goes rather smoothly once there's a level of trust between both parties.

That's about all I wanted to say about client work, now I want to look at the exciting part — the part that really got me thinking when Muhammad asked his question. How do I plan what I write about on this blog? What kind of podcasts I make? What kind of videos or tweets or newsletters I post?

I don't.

Whenever I try to shove my creative energy into a some kind of system, I find that it utterly fails. That's why, on some weeks, I write 3 or 4 blog posts, and while sometimes I go a month without writing anything.

Sometimes I feel inspired to make a video or podcast. When I do I usually sit down whenever I find the time, write a script, record it and edit it as soon as possible. I've got no "long term media engagement strategy" like some people do. I tried it, and it simply doesn't work.

I used to plan everything to the minute though. Both my wife and I did.

I remember us sitting in the car visiting my wife's gynaecologist — she had been pregnant with our first child for about 3 months — and we were discussing how we wanted the delivery to go. Ideally my wife wanted to stay less then 24 hours in the hospital (you can do that in Belgium if your baby is healthy, it's called an "outpatient delivery" if Google Translate can be trusted).

Fast forward two months and we're sitting in that same car driving urgently to the hospital. It turns out there were some serious complications and my wife needed to be hospitalized. Eventually our son was born at around eight months with an emergency C-section — not what we had planned for. Almost one year later the pandemic hit and we suddenly were in lockdown for the next couple of months with a 9-month old baby.

We learned that no single plan can ever guarantee it'll succeed. So why bother with the stress, disappointment and frustration that comes with it when things turn out to be different than expected. Sure, we still plan short-term things, but there's definitely not a long-term plan we hold on to. There are dreams and hopes, but we don't expect all of them to become a reality.

I do the same with my creative work. Whether it's a blog post, newsletter, tweet, video or podcast; I don't make any long term plans. At most I keep a list of ideas that pop up into my head, but I'll have to admit I remove most of them after a couple of months if I didn't find the energy to work on them by then. Only a handful of ideas make it, and those are the ones that I simply start working on whenever I feel like it.

I'll see where this whole online adventure takes me step by step, and I'm ok with that. I actually find it very liberating. The same goes for learning (for me at least). When I'm truly passionate about something, I don't need a schedule to force me to spend time digging into it. On the contrary: I'd almost need a schedule to tell me when to pause because I need to take care of the kids or do house chores or whatever. After all, aren't only the things we're truly passionate about worth spending so much time on?


I'm not sure this was the answer Muhammad expected, but at least it's the most accurate one I could give. If you want to share your thoughts as well, just send me an email!

Thanks for reading! This post is part of my "Dev Diaries" series where I write about my own and personal experiences as a developer. Would you like to read some more?

If you want to stay up to date about what's happening on this blog, you can follow me on Twitter or subscribe to my newsletter:

]]>
2022-01-13T00:00:00+00:00
<![CDATA[ PHP in 2022 ]]> https://www.stitcher.io/blog/php-in-2022 It's the fourth time I'm writing a yearly "PHP in 20XX" post, and I must admit I've never been as excited about it as this year: we've seen awesome new features added to PHP, with stuff like attributes, enums, promoted properties and fibers; and on top of that, the static analysis community is making great progress, my personal favourite feature is PhpStorm now supporting generics when writing code.

Exciting times are ahead, let's take a look at modern day PHP!


# PHP 8.1

I can't help but start this list with the newest PHP version, released just a little more than one month ago. My main projects are already being prepared to run on PHP 8.1 in production, which I must admit I'm very excited about. You might not expect it from a minor release — there are no major breaking changes and only deprecation notices added — but PHP 8.1 brings some very cool features. here's my personal top three.

Enums are now built-into the language:

enum Status
{
    case draft;
    case published;
    case archived;
    
    public function color(): string
    {
        return match($this) 
        {
            Status::draft => 'grey',   
            Status::published => 'green',   
            Status::archived => 'red',   
        };
    }
}

We're able to use new in initializers:

class PostStateMachine
{
    public function __construct(
        private State $state = new Draft(),
    ) {
    }
}

And, of course, readonly properties:

class PostData
{
    public function __construct(
        public readonly string $title,
        public readonly PostState $state,
        public readonly DateTimeImmutable $publishedAt,
    ) {}
}

Which, combined with PHP 8.0's promoted properties, make for some very clean data classes. Just to visualise the difference, here's that same class, with the same functionality, written in PHP 5.6:

class PostData
{
    /** @var string */
    private $title;
    
    /** @var State */
    private $state;
    
    /** @var \DateTimeImmutable|null */
    private $publishedAt;
   
   /**
    * @param string $title 
    * @param State $state 
    * @param \DateTimeImmutable|null $publishedAt 
    */
    public function __construct(
        $title,
        $state,
        $publishedAt = null
    ) {
        $this->title = $title;
        $this->state = $state;
        $this->publishedAt = $publishedAt;
    }
    
    /**
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;    
    }
    
    /**
     * @return State 
     */
    public function getState() 
    {
        return $this->state;    
    }
    
    /**
     * @return \DateTimeImmutable|null 
     */
    public function getPublishedAt() 
    {
        return $this->publishedAt;    
    }
}

Can you see why I'm excited? PHP is getting these awesome syntax improvements with every release. It's only getting more and more fun to write it! Of course, there are a lot more features added in modern PHP, check out my 3-minute video if you want a quick rundown, or you can scroll to keep reading — there's much more exciting about PHP in 2022!

# Static Analysis

I've already briefly mentioned it: static analysis in PHP is growing significantly.

  • Frameworks like Laravel are embracing static typing more and more (writing code and docblocks with the sole purpose of helping static analysis);
  • PhpStorm added support for generic types, it's a pretty big deal if you can write generic code and have your IDE understand it while you're writing it;
  • PhpStan and psalm are only growing; and finally
  • my own newsletter series about static analysis has been doing pretty well with over 1500 participants, more and more people are getting interested in the topic.

If you just want a quick read about why static analysis matters in PHP, and why I'm so excited about it, you could check out this blog post: "We don't need runtime type checks".

# The PHP foundation

Two months ago, the PHP world got some pretty big news, maybe even the biggest news of 2021: Nikita, one of most active core maintainers is stepping down to work on LLVM, but at the same time there's also a new initiative backed by several big companies to finally make core development sustainable.

In short, there's the PHP Foundation, a non-profit with the only goal to fund PHP core development. The initiative is driven by JetBrains, who have already pledged $100,000 to the project. Alongside many others, they have now raised $329,920.75; a good start!

That money is used to fund core development, and opens doors for people to work on PHP who were previously unable. You can read more about the Foundation's mission and goals in JetBrains' blog post.

# The ecosystem

Just like every year, I can't go without mentioning Packagist, now with over 3 million registered versions and more than 300.000 packages. As you can see, the ecosystem just keeps growing and growing, 2022 won't be any different.

Oh, by the way, just recently Packagist passed the milestone of having handled over more than 50 billion installs. Congrats Packagist!

# Async PHP

One exciting development in the async community, is that the developers from both Amp and ReactPHP — the two major async players — have come together to make a fiber-compatible event loop implementation called Revolt PHP.

In comparison to the community at large, async PHP is only used by a small portion of it; but nevertheless it's still good to see the async community is going strong and embracing modern PHP.

# Serverless PHP

An interesting development I've been following along the sideline, is serverless PHP. My buddy Matthieu Napoli is making it his mission to educate PHP developers about this relatively new way of using PHP, and he seems to be doing pretty well. You can check out Bref, his open source project to make serverless PHP easy, or check out his course about serverless PHP in 2022.

I haven't had any use cases for it myself, but I know more and more people running serverless PHP in production, so it's definitely something worth keeping an eye on.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Looking forward

So what about this year? I'm looking forward to see where static analysis is going, and am especially curious about even better integration with PhpStorm. I find that realtime static analysis — static analysis in your code while you're writing it — offers much more value during the development phase itself.

I'm a little worried now that Nikita has stepped down. He's definitely not the only person capable of working on PHP's core, but he did a tremendous amount of work these past years with PHP 8.0 and 8.1. I hope the PHP Foundation will be up to speed soon and that there are enough core developers who have time to work on PHP next year. PHP 8.2 is already in development, although there aren't many RFCs drafted yet.

I don't think 2022 will be the most mind blowing year for PHP, but rather a year of adding stability. Nothing wrong with that.


These were the things that stood out the most to me personally in 2021, and that make me excited for PHP in 2022: awesome new syntax, stability for core development thanks to the PHP Foundation, static analysis is growing stronger and better, and lots of interesting developments across the community.

What are you most excited about? Let me know on Twitter or via email; I'd love to hear your thoughts!

]]>
2022-01-11T00:00:00+00:00
<![CDATA[ PHP version stats: January, 2022 ]]> https://www.stitcher.io/blog/php-version-stats-january-2022 It's that time again: my biyearly summary of which PHP versions are used across the community. It's been 6 months since my previous post and during that time PHP 8.1 was released. It'll be interesting to see some numbers on this newest version as well.

As always, it's important to note that I'm working with the data available to us. That means that these charts are no 100% accurate representation of the PHP community as a whole, but they are an accurate representation of one of the most prominent parts of PHP: the packagist ecosystem.

# Usage Statistics

Let's start with the raw numbers: the percentage of PHP versions being used, today and six months ago.

Version July, 2021 (%) January, 2022 (%)
8.1 0.1 9.1
8.0 14.7 23.9
7.4 46.8 43.9
7.3 19.2 12.0
7.2 10.4 6.6
7.1 3.8 2.4
7.0 1.3 0.8

Note that I've omitted all versions that didn't have more than 1% usage back in July, 2021. Visualizing this data looks something like this:

Evolution of version usage

It's good to see PHP 8.1 being used in almost 10% of all composer installs, only one month after its release. It makes sense that it's easier picked up on for projects already on PHP 8.0, since it's fairly easy to upgrade from PHP 8.0 to PHP 8.1.

I'm also happy to see PHP 8.0's growth, although PHP 8.0 and 8.1 combined only account for one third of all installs. That means that two out of three composer installs use a PHP versions that isn't actively supported any more.

Over the past years, I've been making it my goal to educate the people around me about the importance of keeping software up to date. I believe we — you and I — all have a responsibility to carry. If you want some hands-on tips on how to start, you can read this post about the why and how of managing PHP version ugrades.

two out of three composer installs use a PHP versions that isn't actively supported any more

Moving on the the all-time overview chart, here you can see the evolution of version usage across time.

All time evolution

Despite PHP 7.4 starting its downward trajectory, it's clear that it still has a way to go. Let's hope we'll see a steeper decline in six months.

# Required versions

Another interesting metric is the minimum required version across packages. I've used Nikita's popular package analyzer to download the 1000 most popular composer packages, and wrote a little script to get the lowest version they support from their composer.json. Here are the results:

Version July, 2021 (#) January, 2022 (#)
8.0 117 160
7.4 56 69
7.3 133 116
7.2 142 133
7.1 182 190
7.0 31 29
5.6 61 49
5.5 43 42
5.4 41 43
5.3 97 83
5.2 12 10
5.0 2 2

You'll notice there are a little less than 1000 packages included, that's because some of the 1000 most popular packages don't specifically require a PHP version.

For visual learners, here's the same data visualised in a chart:

Minimal PHP requirement over time

You might be surprised not to see PHP 8.1 here yet, but keep in mind that this data shows the minimum required version. It doesn't mean that no packages support PHP 8.1, it only means they also support PHP 8.0 or lower versions. That makes a lot of sense given that PHP 8.1 has only been released for a little more than a month.

By the way, I'm talking about PHP 8.0 and 8.1 as if you're all already familiar with these newer versions. If that's not the case, or if you want 3-minute refresher about all the cool things that are happening in the PHP world these days, check out my video about modern day PHP!


So, in summary: I see similar trends as previous years, with the majority of composer installs still running outdated versions. I know many have good reasons why they can't or won't upgrade, but I also believe it has never been easier to stay up-to-date with modern PHP than it is today. Investing a couple of hours or days per year to keep your codebase healthy and up-to-date shouldn't be an issue for anyone. I hope you want to help create awareness for this issue. You can start by sharing this blog post or my recap video.

What are your thoughts on these stats? Are you already using PHP 8.1? Let me know your thoughts on Twitter and subscribe to my newsletter if you want to be kept up-to-date about these posts!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2022-01-10T00:00:00+00:00
<![CDATA[ Upgrade to PHP 8.1 with Homebrew on Mac ]]> https://www.stitcher.io/blog/php-81-upgrade-mac

# Upgrading with Homebrew

Start by making sure brew is up-to-date:

brew update

Next, upgrade PHP. You can either use the built-in php recipe, but I recommend to use the shivammathur/homebrew-php tap.

# Normal upgrade

brew upgrade php

# Upgrade with shivammathur/homebrew-php

brew tap shivammathur/php
brew install shivammathur/php/php@8.1

To switch between versions, use the following command:

brew link --overwrite --force php@8.1

You can read more in the repository.

# Next steps

Check the current version by running php -v:

php -v

Restart Nginx or Apache, if you're using Laravel Valet you can skip to the next section; you need some extra steps in order for the web server to properly work.

sudo nginx -s reload
sudo apachectl restart

And make sure that your local web server also uses PHP 8.1 by visiting this script:

# index.php, accessible to your web server

phpinfo();

The version should show 8.1.x.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Valet

If you're using Laravel Valet, you should do the following steps to upgrade it:

composer global update

You can use valet use to switch between PHP versions:

valet use php@8.1
valet use php@8.0

# Extensions

PHP extensions are installed using pecl. I personally use Redis and Xdebug. They can be installed like so:

pecl install redis
pecl install xdebug

You can run pecl list to see which extensions are installed:

pecl list

# Installed packages, channel pecl.php.net:
# =========================================
# Package Version State
# redis   5.3.4   stable
# xdebug  3.1.1   stable

You can search for other extensions using pecl search:

pecl search pdf

# Retrieving data...0%
# ..
# Matched packages, channel pecl.php.net:
# =======================================
# Package Stable/(Latest) Local
# pdflib  4.1.4 (stable)        Creating PDF on the fly with the PDFlib library

Make sure to restart your web server after installing new packages:

sudo nginx -s reload
sudo apachectl restart
valet restart

Make sure all extensions are correctly installed and loaded by checking both your PHP webserver and CLI installs:

php -i | grep redis
var_dump(extension_loaded('redis'));

If extensions aren't properly loaded, there are two easy fixes.

First, make sure the extensions are added in the correct ini file. You can run php --ini to know which file is loaded:

Configuration File (php.ini) Path: /opt/homebrew/etc/php/8.1
Loaded Configuration File:         /opt/homebrew/etc/php/8.1/php.ini
Scan for additional .ini files in: /opt/homebrew/etc/php/8.1/conf.d
Additional .ini files parsed:      /opt/homebrew/etc/php/8.1/conf.d/error_log.ini,
/opt/homebrew/etc/php/8.1/conf.d/ext-opcache.ini,
/opt/homebrew/etc/php/8.1/conf.d/php-memory-limits.ini

Now check the ini file:

extension="redis.so"
zend_extension="xdebug.so"

Note that if you're testing installed extensions via the CLI, you don't need to restart nginx, apache or Valet when making changes to ini settings.

The second thing you can do, if you're updating from an older PHP version which also used pecl to install extension; is to reinstall every extension individually.

pecl uninstall redis
pecl install redis

# Last step

Finally you should test and upgrade your projects for PHP 8 compatibility.

]]>
2021-11-26T00:00:00+00:00
<![CDATA[ PHP 8.1: real-life performance benchmarks ]]> https://www.stitcher.io/blog/php-81-performance-in-real-life I did a very quick performance test because I wanted to know the impact of PHP 8.1 on my real-life projects.

These benchmarks were taken on my local machine, and only meant to measure the relative difference between PHP 8.0 and 8.1. I benchmarked a page that showed lots of data, with lots of classes loaded, as I expected that inheritance cache would have the largest performance impact.

Metric PHP 8.0 PHP 8.1
Requests per second (#/sec) 32.02 34.75
Time per request (ms) 31.232 28.777

The results seem to be in line what what Dmitry originally reported when he added this feature: a 5-8% performance increase.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-11-25T00:00:00+00:00
<![CDATA[ PHP 8.1 in 8 code blocks ]]> https://www.stitcher.io/blog/php-81-in-8-code-blocks

enum Status
{
    case draft;
    case published;
    case archived;
    
    public function color(): string
    {
        return match($this) 
        {
            Status::draft => 'grey',   
            Status::published => 'green',   
            Status::archived => 'red',   
        };
    }
}

Enums


class PostData
{
    public function __construct(
        public readonly string $title,
        public readonly string $author,
        public readonly string $body,
        public readonly DateTimeImmutable $createdAt,
        public readonly PostState $state,
    ) {}
}

Readonly properties


class PostStateMachine
{
    public function __construct(
        private State $state = new Draft(),
    ) {
    }
}

New in initializers


$fiber = new Fiber(function (): void {
    $valueAfterResuming = Fiber::suspend('after suspending');
    
    // … 
});
 
$valueAfterSuspending = $fiber->start();
 
$fiber->resume('after resuming');

Fibers, a.k.a. "green threads"

$array1 = ["a" => 1];

$array2 = ["b" => 2];

$array = ["a" => 0, ...$array1, ...$array2];

var_dump($array); // ["a" => 1, "b" => 2]

Array unpacking also supports string keys


function foo(int $a, int $b) { /* … */ }

$foo = foo(...);

$foo(a: 1, b: 2);

First class callables


function generateSlug(HasTitle&HasId $post) {
    return strtolower($post->getTitle()) . $post->getId();
}

Pure intersection types


$list = ["a", "b", "c"];

array_is_list($list); // true

$notAList = [1 => "a", 2 => "b", 3 => "c"];

array_is_list($notAList); // false

$alsoNotAList = ["a" => "a", "b" => "b", "c" => "c"];

array_is_list($alsoNotAList); // false

The new array_is_list function

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-11-07T00:00:00+00:00
<![CDATA[ Generics in PHP (video) ]]> https://www.stitcher.io/blog/generics-in-php-video I made this video about generics last week, you can watch it (make sure to like and subscribe if you liked it), or you can read the transcript here if you don't like watching videos. Also make sure to share your opinions on the topic via Twitter or email!


Generics. We all want them, they are probably not going to be built-into PHP any time soon. BUT there is proper support for generics using docblocks; both by static analysers like PHPStan and Psalm, but also — and this is a big one — by PhpStorm.

You see, only a few months ago, PhpStorm added basic support for generics using docblocks. And it does in fact work with quite a lot of cases, PhpStorm can tell us — in real time, while coding — what kind of generic type we're dealing with. It's even smart enough to infer generic types in some occasions.

If you have no clue what I'm talking about right now, I would suggest doing some reading on my blog — I've added some useful links in the description for you. But I also want to give an example.

Here we have a kind of "attribute query" class: a class that can filter and instantiate attributes:

$routeAttributes = Attributes::new(MyController::class)
    ->instanceOf(Route::class)
    ->first();

It provides a slightly cleaner API compared to straight up using PHP's built-in reflection classes.

Now, after running this query here, I want my IDE to know, in real time, that I have an instance of the Route attribute here. And this is exactly the kind of complex example that PhpStorm is now able to detect, and it's a huge time saver.

Let me show you the query itself, or at least: the interesting parts of it.

/**
 * @template AttributeType
 */
class Attributes
{
    /**
     * @return AttributeType
     */
    public function first(): mixed
    { /* … */ }
}

Here we have our attribute class, with a generic AttributeType, and that's the type that's returned by the first method, in this example. The problem here: how do we actually set that generic type? It's actually pretty straight forward when you know about generic type inference.

/**
 * @template AttributeType
 */
class Attributes
{
    /**
     * @template InstanceOfType
     *
     * @param class-string<InstanceOfType> $className
     *
     * @return self<InstanceOfType>
     */
    public function instanceOf(string $className): self
    { /* … */ }
}

Here we have our instanceOf method, and you can see it defines another generic type, it's called InstanceOfType. Now, PhpStorm, and other static analysers; are smart enough to detect — or infer the type of the input that's passed to this function — that's the name of the attribute we want to filter on — and use that type as the generic type for our attributes class, when we return it. This kind of generic type inference is an incredibly powerful tool.

You might need to read this example a few times before getting it, but it essentially allows us to create classes like this attribute query class where the end user still has lots of information available to them while using this package.

Now, this is actually not what I wanted to talk about today. It's all pretty cool, and I'm very excited about it; but if you know me, you know that I like to think about the details. And one of those details that we need to talk about, now that generics in PHP are much more accessible; is how to name them.

I used AttributeType and InstanceOfType as the generic type placeholders, but that's kind of not the convention in most programming languages.

I looked at quite a lot of them, and by far, the most popular convention — I think that's thanks to Java — is to use a single letter for generic types; so T, or E or V or K or U — those are some of the popular choices. And that are quite a lot of languages that follow this convention: Java, Kotlin, Rust, Go, also C# and Swift.

And I don't know about you, but I find that this makes my code so much harder to read. And the reasoning behind using these single letters, is to make it clear, by their name, that these are generic types and not real types. But as you can see in my screenshots, you could very well just use a different colour for generic types to differentiate them.

I need to mention though that PhpStorm doesn't support that yet, but I hope that they'll change it after seeing this video.

Another convention that I've seen around — especially in the Psalm and PHPStan's documentation is to prefix the generic name with an uppercase letter T. But is that really more readable than suffixing it with "Type"? TAttribute or AttributeType; TInstanceOf or InstanceOfType? The second one sounds much more like we'd say it in human language, no?

So yeah, these are the kinds of details I'm thinking about, because I feel like they genuinely affect the readability of my code, and code of others that I need to work in.

So, what I would like to know: what's your opinion?

I made a poll on Twitter, and was surprised that so many people — more than 50% — preferred the single letter approach. That's terrible for code readability — especially if you're working with multiple generic types within the same context.

Anyway; I'll probably stick with what feels best for me and what I think is the most readably; but do share your opinions in the comments or on reddit or twitter wherever you're watching this; maybe someone is able to change my mind; or maybe I just changed yours?

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-11-06T00:00:00+00:00
<![CDATA[ What's new in PHP 8.1 ]]> https://www.stitcher.io/blog/new-in-php-81 PHP 8.1 was released on November 25, 2021. It's currently the latest PHP version. In this post, we'll go through all features, performance improvements, changes and deprecations one by one.

# New features

As with every release, PHP 8.1 adds some nice new features. Keep in mind that this list will grow over the year.

# Enums RFC

Enums will be added in PHP 8.1! If you're unsure what they can be used for, you can read about them here.

Adding enums would be a significant improvement in PHP, so I for one am very much looking forward seeing enums arrive in PHP 8.1. To give you a quick preview of what they will look like, here's a code sample:

enum Status {
  case Pending;
  case Active;
  case Archived;
}

And this is how they will be used:

class Post
{
    public function __construct(
        private Status $status = Status::Pending;
    ) {}

    public function setStatus(Status $status): void
    {
        // …
    }
}

$post->setStatus(Status::Active);

You can find an in-depth analysis of how to use enums in this post.


# Fibers RFC

Fibers — aka "green threads" — are a low level mechanism to manage parallelism. You probably won't use them directly in your applications, but frameworks like Amphp and ReactPHP will make extensive use of them.

Here's a simple example of using fibers:

$fiber = new Fiber(function (): void {
    $valueAfterResuming = Fiber::suspend('after suspending');
    
    // … 
});
 
$valueAfterSuspending = $fiber->start();
 
$fiber->resume('after resuming');

If you want to read some more about fibers, what they can and can't do, you can read this post.

# Performance improvements PR

Dmitry Stogov has added some improvements to opcache, he calls it "inheritance cache". This feature allows links between classes to be cached, much like linked classes can be preloaded as of PHP 7.4.

Dmitry reports between a 5% and 8% performance increase thanks to this change, a nice little detail to look out for in PHP 8.1.


# Array unpacking with string keys RFC

Array unpacking was already allowed in PHP 7.4, but it only worked with numeric keys. The reason string keys weren't supported before is because there wasn't any consensus on how to merge array duplicates. The RFC cleanly solves this by following the semantics of array_merge:

$array1 = ["a" => 1];

$array2 = ["b" => 2];

$array = ["a" => 0, ...$array1, ...$array2];

var_dump($array); // ["a" => 1, "b" => 2]

# new in initializers RFC

This RFC allows you to use the new keyword in function definitions as a default parameter, as well as in attribute arguments and other places.

class MyController {
    public function __construct(
        private Logger $logger = new NullLogger(),
    ) {}
}

You can read all about this feature in this dedicated post.


# Readonly properties RFC

Class properties can be marked as readonly, meaning they can only be written once.

class PostData {
    public function __construct(
        public readonly string $title,
        public readonly DateTimeImmutable $date,
    ) {}
}

Trying to change a readonly property after it has been initialized will result in an error:

$post = new Post('Title', /* … */);

$post->title = 'Other';

Error: Cannot modify readonly property Post::$title

If you want to learn more about readonly properties in depth, you can read my followup post.


# First-class callable syntax RFC

You can now make a closure from a callable by calling that callable and passing it ... as its argument:

function foo(int $a, int $b) { /* … */ }

$foo = foo(...);

$foo(a: 1, b: 2);

# Pure intersection types RFC

You already know about union types in PHP 8.0, and intersection types are a similar feature. Where union types require the input to be one of the given types, intersection types require the input to be all of the specified types. Intersection types are especially useful when you're working with lots of interfaces:

function generateSlug(HasTitle&HasId $post) {
    return strtolower($post->getTitle()) . $post->getId();
}

If you like this style of programming, you'd need to create a new interface Sluggable and implement it in $post, intersection types get rid of that overhead.


# New never type RFC

The never type can be used to indicated that a function will actually stop the program flow. This can be done either by throwing an exception, calling exit or other similar functions.

function dd(mixed $input): never
{
    // dump
    
    exit;
}

never differs from void in that void still allows the program to continue. This might seem like a novelty feature but it's actually a quite useful one for static analysers.


# New array_is_list function RFC

You've probably had to deal with this once in a while: determine if an array's keys are in numerical order, starting from index 0. Just like json_encode decides whether an array should be encoded as an array or object.

PHP 8.1 adds a built-in function to determine whether an array is a list with those semantics, or not:

$list = ["a", "b", "c"];

array_is_list($list); // true

$notAList = [1 => "a", 2 => "b", 3 => "c"];

array_is_list($notAList); // false

$alsoNotAList = ["a" => "a", "b" => "b", "c" => "c"];

array_is_list($alsoNotAList); // false

Do you want to stay up-to-date about PHP 8.1's development? Subscribe to my newsletter and receive occasional updates:


# Final class constants RFC

Class constants in PHP can be overridden during inheritance:

class Foo
{
    public const X = "foo";
}
 
class Bar extends Foo
{
    public const X = "bar";
}

As of PHP 8.1, you can mark such constants as final in order to prevent this:

class Foo
{
    final public const X = "foo";
}
 
class Bar extends Foo
{
    public const X = "bar";
    Fatal error: Bar::X cannot override final constant Foo::X
}

# New fsync function RFC

PHP 8.1 adds the fsync and fdatasync functions to force synchronization of file changes to disk and ensure operating system write buffers have been flushed before returning.

$file = fopen("sample.txt", "w");

fwrite($file, "Some content");

if (fsync($file)) {
    echo "File has been successfully persisted to disk.";
}

fclose($file);

Because disk synchronization is a file system operation, the fsync function will only work on plain file streams. Attempting to sync non-file streams will emit a warning.


# Explicit octal integer literal notation RFC

You can now use 0o and 0O to denote octal numbers. The previous notation by prefixing a number with 0 still works as well.

016 === 0o16; // true
016 === 0O16; // true

# Breaking changes

While PHP 8.1 is a minor version, there will be some changes that might technically be a breaking change, and deprecations as well. Let's discuss them one by one.


# Internal method return types RFC

Chances are you might run into this deprecation notice when upgrading to PHP 8.1:

Return type should either be compatible with IteratorAggregate::getIterator(): Traversable, 
or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice

You might notice this error pop up when using phpunit/phpunit, symfony/finder and some other popular open source packages. What's happened is that internal functions are starting to use proper return types. If you're extending a class from the standard library (like IteratorAggregate), you'll need to add those return types as well.

The fix is simple: update your vendor code if the error occurs in a third-party package (most of those are already fixed with their newest releases). If the error occurs in your code you can either add the ReturnTypeWillChange attribute, suppressing the error until PHP 9.0. Here's an example of a class extending DateTime:

class MyDateTime extends DateTime
{
    /**
     * @return DateTime|false
     */
    #[ReturnTypeWillChange]
    public function modify(string $modifier) 
    { 
        return false; 
    }
}

Or you can simply add the return type:

class MyDateTime extends DateTime
{
    public function modify(string $modifier): DateTime|false 
    { 
        return false; 
    }
}

# Restrict $GLOBALS usage RFC

A small change to how $GLOBALS is used will have a significant impact on the performance of all array operations. Nikita does a fine job explaining the problem and solution in the RFC. The change means that some edge cases aren't possible to do any more with $GLOBALS. "What is no longer supported are writes to $GLOBALS taken as a whole. All the following will generate a compile-time error":

$GLOBALS = [];
$GLOBALS += [];
$GLOBALS =& $x;
$x =& $GLOBALS;
unset($GLOBALS);

On top of that, passing $GLOBALS by reference will generate a runtime error:

by_ref($GLOBALS); // Run-time error

Nikita analysed the top 2000 packages on Packagist, and only found 23 cases that will be affected by this change. We can conclude the impact of this — technically breaking — change will be low, which is why internals decided to add it in PHP 8.1. Remember that most of us will win by this change, given the positive performance impact it has everywhere in our code.


# Resource to object migrations

These changes are part of the long-term vision to convert all resources to dedicated objects. You can read more about it here.

Fileinfo functions with finfo objects

Functions like finfo_file and finfo_open used to accept and return resources. As of PHP 8.1, they work with finfo objects.

IMAP functions with IMAPConnection objects

Just like the fileinfo change, IMAP functions like imap_body and imap_open no longer work with resources


# Deprecate passing null to non-nullable arguments of internal functions RFC

This change is simple: internal functions currently accept null for arguments that are non-nullable, this RFC deprecates that behaviour. For example, this is currently possible:

str_contains("string", null);

In PHP 8.1, these kinds of errors will throw a deprecation warning, in PHP 9 they will be converted to type errors.


# Autovivification on false RFC

From the RFC:

PHP natively allows for autovivification (auto-creation of arrays from falsey values). This feature is very useful and used in a lot of PHP projects, especially if the variable is undefined. However, there is a little oddity that allows creating an array from a false and null value.

You can read the details on the RFC page. In summary, this behaviour is deprecated:

$array = false;

$array[] = 2;

Automatic conversion of false to array is deprecated

# Other small changes

With every release, there's a bunch of very minor changes to the language. All of them are listed in the UPGRADING guide on GitHub and the small deprecations RFC, make sure to check it out if you want to know every little detail.

Here's a summary of the most significant changes:

  • MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH no longer has an effect
  • MYSQLI_STORE_RESULT_COPY_DATA no longer has an effect
  • PDO::ATTR_STRINGIFY_FETCHES now also works with booleans
  • Integers and floats in PDO MySQL and Sqlite result sets will be returned using native PHP types instead of strings when using emulated prepared statements
  • Functions like htmlspecialchars and htmlentities now also escape ' by default to &#039;; malformed UTF-8 will also be replaced with a unicode character, instead of resulting in an empty string
  • The hash, hash_file and hash_init have an extra argument added to them called $options, it has a default value of [] so it won't affect your code
  • New support for MurmurHash3 and xxHash

That's it for now, keep in mind I'll regularly update this post during the year, so make sure to subscribe if you want to be kept in the loop. Are you excited for PHP 8.1? Let me know on Twitter!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-11-25T00:00:00+00:00
<![CDATA[ The case for route attributes ]]> https://www.stitcher.io/blog/route-attributes I've been thinking about route attributes lately. By doing so, I came to realise that I've got a somewhat strange relation with annotations a.k.a. attributes. Over the years, I've gone from loving them to hating them, to loving them again, to somewhere in between. I've seen them abused, both inside and outside of PHP and I've heard both advocates and opponents making compelling arguments for and against them.

Lately, I've done quite a lot of thinking about a specific use case for them. I've talked with a bunch of people about it and I've tried to approach the question as rationally as possible: are attributes used for routing, a good or bad idea?

After months of thoughts and discussions, I've come to (what I think to be as objectively as possible) a conclusion: they are worth giving a try, albeit with some side notes attached. In this post, I'll share my thought process, as well as address all counterarguments I've heard against route attributes over these past years.

Let's get started.


To make sure we're on the same page, route attributes in their most basic form would look something like this:

class PostAdminController
{
    #[Get('/posts')]
    public function index() {}
    
    #[Get('/posts/{post}')]
    public function show(Post $post) {}
    
    // …
    
    #[Post('/posts/{post}')]
    public function store(Post $post) {}
}

There are a lot of issues with such a simplified example, so let's go through them one by one.

# Duplication

First of all, there's the issue of duplication. It might not seem like a problem in this example, but most projects definitely have more than "a few routes". I've counted them in two of the larger projects I'm working on: 470 and 815 routes respectively.

Both Symfony and Laravel have a concept called "route groups" to deal with these kinds of scaling issues. And the same thinking can be applied when using route attributes.

I'm sure you can come up with quite a lot of different approaches to modeling such attribute route groups; I'm going to share two that I think are robust and qualitative solutions, but it's by no means a definitive list.

You could manage "shared route configuration", stuff like prefixes and middlewares, on the controller level:

#[Prefix('/posts')]
#[Middleware(AdminMiddleware::class)]
class PostController
{
    #[Get('/posts')]
    public function index() {}
    
    #[Get('/posts/{post}')]
    public function show(Post $post) {}
}

Or, taking it a step further; have a generic Route attribute that can be used like so:

#[Route(
    prefix: '/post',
    middleware: [AdminMiddleware::class]
)]
class PostController
{
    #[Get('/posts')]
    public function index() {}
    
    #[Get('/posts/{post}')]
    public function show(Post $post) {}
}

But also make it extensible:

#[Attribute]
class AdminRoute extends Route
{
    public function __construct(
        string $prefix,
        array $middleware,
    ) {
        parent::__construct(
            prefix: "/admin/{$prefix}",
            middleware: [
                AdminMiddleware::class,
                ...$middleware
            ],
        )
    }
}

And be used like so:

#[AdminRoute]
class PostController
{
    #[Get('/posts')]
    public function index() {}
    
    #[Get('/posts/{post}')]
    public function show(Post $post) {}
}

This last approach is definitely my favourite, but feel free to differ in that opinion. The main point here is: excessive duplication doesn't have to be a problem with route attributes.

# Discoverability

The second-biggest argument against route attributes comes from people who say that they prefer to keep their routes in a single file, so that they can easily search them, instead of spreading them across potentially hundreds of controller files.

Let's take a look at a real life example though. Here we have a contacts controller with an edit method:

class ContactsController
{
    public function edit(Contact $contact) 
    { /* … */ };
}

People arguing for "a central place to manage their routes", in other words: against route attributes; say that a central route file makes it easier to find what they are looking for. So, ok, let's click through to our routes file (in my case the Laravel IDEA plugin allows you to click the edit method and go straight to the route definition), and take a look at what's there:

Route::get('{contact}', [ContactsController::class, 'edit']);

So, what's the URI to visit this page? Is it /{contactId}? Of course not, this route is part of a route group:

Route::prefix('people')->group(function (): void {
    // …
    Route::get('{contact}', [ContactsController::class, 'edit']);
});

So, it's /people/{contactId}? Nope, because this group is part of another group:

Route::prefix('crm')
    // …
    ->group(function (): void {
        Route::prefix('people')->group(function (): void {
            // …
            Route::get('{contact}', [ContactsController::class, 'edit']);
        });
    }

Which is part of another group:

Route::middleware('can:admin,' . Tenant::class)
    ->group(function (): void {
        Route::prefix('crm')
        // …
        ->group(function (): void {
            Route::prefix('people')->group(function (): void {
                // …
                Route::get('{contact}', [ContactsController::class, 'edit']);
            });
        }

Which is part of another group, defined in Laravel's route service provider:

Route::middleware(['web', 'auth', /* … */])
    ->prefix('admin/{currentTenant}')
    ->group(base_path('routes/admin_tenant.php'));

So, in fact, the full URI to this controller is /admin/{tenantId}/crm/people/edit/{contactId}. And now remember our route file actually contains somewhere between 700 and 1500 lines of code, not just the snippets I shared here.

I'd argue that using dedicated route attributes like CrmRoute extending AdminRoute would be much easier to work with, since you can simply start from the controller and click your way one level up each time, without manually looking through group configurations.

Furthermore, adding a route to the right place in such a large route file poses the same issue: on what line exactly should my route be defined to fall in the right group? I'm not going to step through the same process again in reverse, I'm sure you can see the problem I'm pointing at.

Finally, some people mention splitting their route files into separate ones to partially prevent these problems. And I'd agree with them: that's exactly what route attributes allow you to do on a controller-based level.

In short, dedicated route files do not improve discoverability and route attributes definitely don't worsen the situation.

# Consistency

With the two main arguments against route attributes refuted, let's consider whether they have additional benefits compared to separated route files.

Spoiler: they do.

In the vast majority of cases, from my own experience and based on other's testimonies, controller methods and URIs almost always map one to one together. So why shouldn't they be kept together?

When I'm writing a new controller method, the last thing I want to be bothered about is to create the controller method, and then having to think about "ok, which route file should I now go to that has the correct middleware groups setup, and where in that file should I register this particular method". There's so much unnecessary cognitive overload introduced because of separate route files, because they pull apart two concepts they tightly belong together.

Let's just keep them together, so that we can focus on more important stuff.

Furthermore, any framework worth its salt will provide you with the tools necessary to generate any URI based on a controller method:

action([PostController::class, 'show'], $post);

If you're already working with controller methods as the "entry point" into your project's URI scheme, then why not keep relevant meta data right with them as well?

So yes, route attributes do add value compared to route files: they reduce cognitive load while programming.

# Route collisions

One of the only arguments against route attributes that I kind of agree with, is how we deal with collisions. You've probably dealt with a situation like this one, where two route definitions collide with each other:

Route::get('/contacts/{id}', …);
Route::get('/contacts/list', …);

Here we have a classic collision: when visiting /contacts/list, your router could detect it as matching /contacts/{id}, and in turn runs the wrong action for that route.

Such problems occur rarely, but I've had to deal with them myself on the odd occasion. The solution, when using a single route file, is to simply switch their order:

Route::get('/contacts/list', …);
Route::get('/contacts/{id}', …);

This makes it so that /contacts/list is the first hit, and thus prevents the route collision. However, you don't have any control over the route order when using attributes since they are directly coupled to controller methods and not grouped together; so what then?

First of all, there are a couple of ways to circumvent route collisions, using route files or attributes, all the same; that don't require you to rely on route ordering:

  • You could change your URI scheme, so that there are no potential collisions: /contacts/show/{id} or /contacts/{id}/show; or
  • you could use regex validation to only match numeric ids: /contacts/{id:\d+}.

However, there still might be some edge cases where collisions are unavoidable. How to handle those? The most obvious solution is to simply allow some kind of "order" key on route attributes, so that you can carefully control their order yourself:

#[Get('/contacts/list', order: 'contacts-1')]
public function index() {}

#[Get('/contacts/{id}', order: 'contacts-2')]
public function show(Contact $contact) {}

I agree that this approach isn't ideal, but I'd say that solving route collisions never is. On top of that, these kinds of collisions only rarely happen, so I only consider it a very minor problem that can be solved when needed.

# Performance

Finally a short one, but one that needs mentioning: some people are afraid of attributes because of performance issues.

First of all: reflection in PHP is pretty fast, all major frameworks use reflection extensively, and I bet you never noticed those parts being a performance bottleneck.

And, secondly: attribute discovery and route registration is something that is very easily cacheable in production: Laravel already does this with event listeners and blade components, just to name two examples.

In fact, the concept of "a route cache" is already present in both Symfony and Laravel, and Symfony even already supports route attributes.

So no, performance isn't a concern when using route attributes.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

So, what's left? The only argument I've heard that I didn't address here is that "people just don't like attributes".

There's very little to say against that. I think it mostly means that "people don't like change" in general. I've been guilty of this attitude myself. The only advice I can give, if you're in that situation, is to just try it out. Get out of your comfort zone, it's a liberating thing to do.

Now, maybe you want to tell me I'm wrong, or share your own thoughts on the matter. I'd love for my opinion to be challenged, so feel free to share your thoughts on Twitter or via email!

]]>
2021-10-27T00:00:00+00:00
<![CDATA[ PHP 8.1: new in initializers ]]> https://www.stitcher.io/blog/php-81-new-in-initializers PHP 8.1 adds a feature that might seem like a small detail, but one that I think will have a significant day-by-day impact on many people. So what's this "new in initializers RFC" about? Let's take a look at an example; we've all written code like this:

class MyStateMachine
{
    public function __construct(
        private ?State $state = null,
    ) {
        $this->state ??= new InitialState();
    }
}

In this state machine example, we'd like to construct our class in two ways: with and without an initial state. If we construct it without an initial state, we want a default one to be set. PHP of course supports setting initial values directly in the parameter list, but only for primitive types. For example, if our state machine used strings instead of objects internally, we'd be able to write its constructor like so:

class MyStateMachine
{
    public function __construct(
        private string $state = 'initial',
    ) {
    }
}

So with PHP 8.1 we're able to use that same "default value" syntax for objects as well. In other words: you can use new for default arguments (which are one example of "initializers"):

class MyStateMachine
{
    public function __construct(
        private State $state = new InitialState(),
    ) {
    }
}

"Initializers" are more than parameter default values though, here's a simple explanation from the RFC:

This RFC proposes to allow use of new expressions inside parameter default values, attribute arguments, static variable initializers and global constant initializers

You read it right: attributes are in this list as well! Imagine a simple validation library that uses attributes to validate input on properties. Maybe it should be able to validate array elements, something like this:

class CreateEmailsRequest extends FormRequestData
{
    #[ValidArray(
        email: [new Required, new ValidEmail],
        name: [new Required, new ValidString],
    )]
    public array $people;
}

Before PHP 8.1, you wouldn't be able to write this kind of code, because you weren't allowed to use new in attributes, due to the way they are evaluated, but now you can!

Let's take a look at some important details worth mentioning.

# Only constructed when needed

These kinds of "new values" will only be constructed when actually needed. That means that, in our first example, PHP will only create a new object of InitialState if no argument is given:

class MyStateMachine
{
    public function __construct(
        private State $state = new InitialState(),
    ) {
    }
}

new MyStateMachine(new DraftState()); // No InitialState is created
new MyStateMachine(); // But now it is

In case of attributes, for example, the objects will only be created when newInstance is called on the reflection attribute.

# Not in class properties

You should also know that you cannot use new as a default value in class properties. Supporting this functionality would introduce lots of unforeseen side effects when, for example, serializing and unserializing objects.

class MyStateMachine
{
    private State $state = new InitialState();
}

Luckily we have promoted properties which do allow a default value, since PHP will transpile the property promotion syntax, keeping the default value in the constructor argument, but not in the actual property.

Here's what the transpiled version looks like:

class MyStateMachine
{
    private State $state;
    
    public function __construct(
        State $state = new InitialState(),
    ) {
        $this->state = $state;
    }
}

# Limited input

You might have already guessed it, but you can only pass a limited set of input when constructing new objects in initializers. For example, you can't use variables, the spread operator, anonymous classes, etc. Still, it's a very welcome addition!


PHP is getting better and better with every update. Some people argue that these changes aren't strictly necessary since they don't add any new functionality to the language; but they do make our day-by-day developer lives just a little more easy, I really like that about PHP these days!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-10-09T00:00:00+00:00
<![CDATA[ The birth and death of a framework ]]> https://www.stitcher.io/blog/birth-and-death-of-a-framework

This post was first released on my newsletter. Feel free to subscribe if you want to be the first to see these kinds of posts, and want to talk about about them with me directly via email.

Every once in a while, maybe every couple of years, someone has an idea that revolutionises the tech industry. All popular frameworks that we use day by day once started out as such a small and insignificant idea: React, TypeScript, Tailwind, Electron, Laravel.

It's hard to imagine a world without those frameworks. And yet: if you told me 15 years ago that the JavaScript ecosystem would be where it is today, I'd have a hard time believing you.


Eric Evans once gave a talk about trying to imagine alternatives to frameworks and libraries that we're used to today. He looked at a popular Java time library — Joda Time — and wondered if it was the best solution out there. It certainly was the most used one, but did that also mean "the best"? What followed was a thought experiment about dealing with time, thinking outside the box and solving domain problems from scratch without being influenced by prior knowledge and legacy expectations.

What if we'd start from a blank slate, Eric wondered?

I think that's exactly the spot where potential revolutionary ideas are born. What if we don't take our current solutions for granted but start from scratch instead?

I find Laravel to be a good example. You could easily say that, 10 years ago, the problem of "MVC frameworks for PHP" had already been solved by several frameworks; why would we need another one? And yet, Laravel managed to outgrow every other framework in just a few years time. There's quite a few data sources confirming that:

It's true that there's no single data set with a 100% accurate representation of the real world, though I think these sources are the most accurate available, and they all confirm the same: the massive growth of a framework that didn't really solve any new problems compared to its competitors, and yet skyrocketed in popularity.

Ironically though, it's that same popularity, that will most likely mean its end in the long run.


What I like about Eric's thought experiment, is that there weren't any alternative goals: he simply wanted to take an honest look at the software he had at hand and wonder "could it be better?".

There's a hidden caveat with Eric's approach though: he can come up with the best solution in the world, all the while ignoring legacy code and backwards compatibility. If you don't have to worry about an existing user base then yes, you can come up with a better solution to almost any given problem.

Joda Time wasn't popular because it was the best, but because it grew with its users for many, many years. It was a trusted solution — sure it had its quirks, but it had proven itself more than enough.

The irony of managing popular software is that once it becomes popular, you can't promise the same quality you did at the start. You can't create the same disruption over and over again, because you need to accommodate your existing user base. In the end, you inevitably end up making compromises.

I feel like this is happening to Laravel today, 10 years after its birth. That's not a bad thing, by the way; it's a sign of maturity and stability, which are two key components in creating long lasting, valuable software.

On top of that, Laravel has solved most, if not all, of the problems there are when writing a web application. There aren't many more ground-breaking features that are missing. Every release brings some niceties, but nothing that's absolutely life critical. Meanwhile though, it takes a very long time to get up to speed with the latest PHP additions, because there's a legacy user base to keep in mind.

By satisfying their users and guaranteeing stability, any framework must stop being as disruptive as they were at the start. They must create a void that will be filled by another framework somewhere in the future.

That's exactly why Laravel grew so popular: it filled the void created by other popular frameworks. Ten years ago, that void in the PHP community was simplicity and a low-level entry barrier. That's the void that Laravel filled, and turned out to be extremely successful.

Laravel is in the same place today, where other frameworks were a decade ago. There's a subtle void being created, and it'll grow steadily for the years to come. I believe there will be a point in time where that void is large enough to spark the birth of a new framework. And while Laravel will keep being relevant for many more years — there's quite a lot of production code dependant on it — there will be another "best thing". Laravel, just like any other framework, will reach a tipping point; just like jQuery, Rails, Bootstrap, Symfony, Angular.

So the question at hand: who and what will fill that void?

# On the other hand…

I think it's possible for any framework to fill its own void, but it requires groundbreaking changes. In Laravel's case, I can come up with a few things I'd expect from a framework if it were created from scratch today:

  • Proper type support (this is already partially worked on, though there's still lots of room for improvement).
  • Getting rid of unnecessary technical debt. For example, Facades and other forms of magic: they served a purpose 10 years ago when its target audience wasn't using a proper IDE or relying on static analysis, but the PHP community is generally moving towards another type of programming.
  • Embrace modern PHP features like named arguments, enums and attributes.
  • Venture in new territories like async and serverless, Laravel is actually already doing that, and is on the forefront in this area within the PHP community.

It'll be interesting to see whether Laravel will be able to fill its own void the next decade or so. I wouldn't be surprised if it did, or at least partially.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-09-16T00:00:00+00:00
<![CDATA[ PHP 8.1: readonly properties ]]> https://www.stitcher.io/blog/php-81-readonly-properties

Important note: PHP 8.2 adds a way of making whole classes readonly at once: readonly classes.

Writing data transfer objects and value objects in PHP has become significantly easier over the years. Take for example a look at a DTO in PHP 5.6:

class BlogData
{
    /** @var string */
    private $title;
    
    /** @var Status */
    private $status;
    
    /** @var \DateTimeImmutable|null */
    private $publishedAt;
   
   /**
    * @param string $title 
    * @param Status $status 
    * @param \DateTimeImmutable|null $publishedAt 
    */
    public function __construct(
        $title,
        $status,
        $publishedAt = null
    ) {
        $this->title = $title;
        $this->status = $status;
        $this->publishedAt = $publishedAt;
    }
    
    /**
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;    
    }
    
    /**
     * @return Status 
     */
    public function getStatus() 
    {
        return $this->status;    
    }
    
    /**
     * @return \DateTimeImmutable|null 
     */
    public function getPublishedAt() 
    {
        return $this->publishedAt;    
    }
}

And compare it to its PHP 8.0's equivalent:

class BlogData
{
    public function __construct(
        private string $title,
        private Status $status,
        private ?DateTimeImmutable $publishedAt = null,
    ) {}
    
    public function getTitle(): string
    {
        return $this->title;    
    }
    
    public function getStatus(): Status 
    {
        return $this->status;    
    }
    
    public function getPublishedAt(): ?DateTimeImmutable
    {
        return $this->publishedAt;    
    }
}

That's already quite the difference, though I think there's still one big issue: all those getters. Personally, I don't use them anymore since PHP 8.0 with its promoted properties. I simply prefer to use public properties instead of adding getters:

class BlogData
{
    public function __construct(
        public string $title,
        public Status $status,
        public ?DateTimeImmutable $publishedAt = null,
    ) {}
}

Object oriented purists don't like this approach though: an object's internal status shouldn't be exposed directly, and definitely not be changeable from the outside.

In our projects at Spatie, we have an internal style guide rule that DTOs and VOs with public properties shouldn't be changed from the outside; a practice that seems to work fairly well, we've been doing it for quite some time now without running into any problems.

However, yes; I agree that it would be better if the language ensured that public properties couldn't be overwritten at all. Well, PHP 8.1 solves all these issues by introducing the readonly keyword:

class BlogData
{
    public function __construct(
        public readonly string $title,
        public readonly Status $status,
        public readonly ?DateTimeImmutable $publishedAt = null,
    ) {}
}

This keyword basically does what its name suggests: once a property is set, it cannot be overwritten anymore:

$blog = new BlogData(
    title: 'PHP 8.1: readonly properties', 
    status: Status::PUBLISHED, 
    publishedAt: now()
);

$blog->title = 'Another title';

Error: Cannot modify readonly property BlogData::$title

Knowing that, when an object is constructed, it won't change anymore, gives a level of certainty and peace when writing code: a whole range of unforeseen data changes simply can't happen anymore.

Of course, you still want to be able to copy data over to a new object, and maybe change some properties along the way. We'll discuss how to do that with readonly properties later in this post. First, let's look at them in depth.

# Only typed properties

Readonly properties can only be used in combination with typed properties:

class BlogData
{
    public readonly string $title;
    
    public readonly $mixed;
}

You can however use mixed as a type hint:

class BlogData
{
    public readonly string $title;
    
    public readonly mixed $mixed;
}

The reason for this restriction is that by omitting a property type, PHP will automatically set a property's value to null if no explicit value was supplied in the constructor. This behaviour, combined with readonly, would cause unnecessary confusion.

# Both normal and promoted properties

You've already seen examples of both: readonly can be added both on normal, as well as promoted properties:

class BlogData
{
    public readonly string $title;
    
    public function __construct(
        public readonly Status $status, 
    ) {}
}

# No default value

Readonly properties can not have a default value:

class BlogData
{
    public readonly string $title = 'Readonly properties';
}

That is, unless they are promoted properties:

class BlogData
{
    public function __construct(
        public readonly string $title = 'Readonly properties', 
    ) {}
}

The reason that it is allowed for promoted properties, is because the default value of a promoted property isn't used as the default value for the class property, but only for the constructor argument. Under the hood, the above code would transpile to this:

class BlogData
{
    public readonly string $title;
    
    public function __construct(
        string $title = 'Readonly properties', 
    ) {
        $this->title = $title;
    }
}

You can see how the actual property doesn't get assigned a default value. The reason for not allowing default values on readonly properties, by the way, is that they wouldn't be any different from constants in that form.

# Inheritance

You're not allowed to change the readonly flag during inheritance:

class Foo
{
    public readonly int $prop;
}

class Bar extends Foo
{
    public int $prop;
}

This rule goes in both directions: you're not allowed to add or remove the readonly flag during inheritance.

# Unset is not allowed

Once a readonly property is set, you cannot change it, not even unset it:

$foo = new Foo('value');

unset($foo->prop);

# Reflection

There's a new ReflectionProperty::isReadOnly() method, as well as a ReflectionProperty::IS_READONLY flag.

# Cloning

So, if you can't change readonly properties, and if you can't unset them, how can you create a copy of your DTOs or VOs and change some of its data? You can't clone them, because you wouldn't be able to overwrite its values. There's actually an idea to add a clone with construct in the future that allows this behaviour, but that doesn't solve our problem now.

Well, you can copy over objects with changed readonly properties, if you rely on a little bit of reflection magic. By creating an object without calling its constructor (which is possible using reflection), and then by manually copying each property over — sometimes overwriting its value — you can in fact "clone" an object and change its readonly properties.

I made a small package to do exactly that, here's what it looks like:

class BlogData
{
    use Cloneable;

    public function __construct(
        public readonly string $title,
    ) {}
}

$dataA = new BlogData('Title');

$dataB = $dataA->with(title: 'Another title');

I actually wrote a dedicated blogpost explaining the mechanics behind all of this, you can read it here.

# Readonly classes

Finally, I should also mention the addition of readonly classes in PHP 8.2. In cases where all properties of your class are readonly (which often happens with DTOs or VOs), you can mark the class itself as readonly. This means you won't have to declare every individual property as readonly — a nice shorthand!

readonly class BlogData
{
    public function __construct(
        public string $title,
        public Status $status,
        public ?DateTimeImmutable $publishedAt = null,
    ) {}
}

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

So, that's all there is to say about readonly properties. I think they are a great feature if you're working on projects that deal with lots of DTOs and VOs, and require you to carefully manage the data flow throughout your code. Immutable objects with readonly properties are a significant help in doing so.

I'm looking forward to using them, what about you? Let me know on Twitter or via e-mail!

]]>
2021-09-11T00:00:00+00:00
<![CDATA[ My IKEA clock, and software design ]]> https://www.stitcher.io/blog/my-ikea-clock This is my bedside clock:

I've had this IKEA clock for 14 years now — that's half my life. And it's one of those things I'll be really sad about the day it stops working.

Why? This clock is a beautiful example of perfect design. Not because it's pretty, but because it's simple, has a clear goal, combined with extremely good UX.

  • It has a very dim backlight, which only shows for a short period when you press the giant button on top. So there's no additional light in your room while sleeping.
  • It's wireless, it works with three AAA batteries.
  • I know for sure that I've changed its batteries once in the last 7 years, maybe once more before but I can't remember.
  • It has a rubber coating, protecting it from falls — I've actually thrown it at my brother once for waking me up too early, it kept working fine (sorry bro, we were young…). My oldest son has also chewed on it, the clock doesn't care.
  • It keeps its internal clock state for a minute or so after removing the batteries, so you can change batteries without having to reset the clock afterwards — is this magic?
  • It can change time both forwards and backwards, making it easy to set alarms without having to do the full 12-hour, 60-minute round trip.

We should design our software more like this clock. No nonsense, focussed on a clear goal and with the simplest UX possible. It just works.

Unfortunately, IKEA doesn't make this kind of clock any more (it's called "Slabang", by the way). I really hope mine will last a few more decades; I've already been looking for replacements but nothing seems to match the excellence of this clock.

]]>
2021-09-01T00:00:00+00:00
<![CDATA[ re: On using PSR abstractions ]]> https://www.stitcher.io/blog/re-on-using-psr-abstractions Yesterday, I read Matthias Noback's excellent blog post on PSR abstractions, and I'd like to share some thoughts on the topic as well. I'm going to quote the parts I want to answer, but make sure to read the full post if you want more information about Matthias' point of view.

I want to make clear up front that I mean no disrespect to any individual and I've tried very hard to convey that in this post. If something however still comes across as disrespectful or hurtful, please reach out to me to let me know and I'm happy to revisit it.


Let's start at the beginning.

Several years ago, when the PHP-FIG created its first PSRs they started some big changes in the PHP ecosystem

They definitely did. I'd say the FIG has done an amazing job in modernizing the PHP ecosystem.

PSRs for coding standards were defined, which I'm sure helped a lot of teams to leave coding standard discussions behind.

I think it's fair to say that most professional developers are using at least some PSR, with or without their knowledge.

Next up were the PSRs that aimed for the big goal: framework interoperability. […] The idea […] was that frameworks could provide implementation packages for the proposed interfaces. So you could eventually use the Symfony router, the Zend container, a Laravel security component, and so on.

While framework interoperability is useful to some extent, it's unrealistic trying to make everything work together: you'd end up with a single framework in the end. There are only so many ways to implement a router or container, if there's one common set of interfaces shared among different frameworks then there'll be very little differences to the users of those frameworks. Sure there might be some implementation differences — but the goal of a framework is for users not to care about those, and to be able to focus on building applications instead.

Matthias shares this concern:

One of the concerns I personally had about PSR abstractions is that once you have a good abstraction, you don't need multiple implementation packages

The beauty of the two or three (or four or five, depending how you count) major frameworks in our community is that they each have their unique way of tackling problems. Each framework has its own identity that attracts a different group of developers.

Aiming for "full framework interoperability" is not only an unrealistic goal, but also something we simply don't need.

But fair enough, maybe Matthias and the FIG aren't talking about full framework interoperability, just about some parts. So let's talk about abstractions. Matthias says there's value in PSRs because they are tried and tested abstractions and you can trust them.

Take, for example PSR-18, the HTTP client interface:

interface ClientInterface
{
    /**
     * @throws \Psr\Http\Client\ClientExceptionInterface
     */
    public function sendRequest(
        RequestInterface $request
    ): ResponseInterface;
}

Granted, this is as simple as an HTTP client can get. It should be able to send a request and return a response. Matthias says the following about it:

It is a great abstraction already: it does what you need, nothing more, nothing less. After all, what you need is to send an HTTP request and do something with the returned HTTP response

However, there's of course that infamous RequestInterface. Here's Matthias again:

The only problem about this interface is maybe: how can you create a RequestInterface instance?

I can resonate with that thought. Whenever I encounter a PSR-7 compliant library, I need to stop and think and search which package allows me to easily create — what I would think should be — a simple request object:

Every time I need an HTTP client I struggle with this again: what packages to install, and how to get a hold of these objects?

Matthias contemplates the idea that maybe your own, simpler abstraction is a better option?

interface HttpClient
{
    public function get(
        string $uri, 
        array $headers, 
        array $query
    ): string;

    public function post(
        string $uri, 
        array $headers, 
        string $body
    ): string;
}

"Unfortunately", he says:

[…] by creating my own abstraction I lose the benefits of using an established abstraction, being:

  1. You don't have to design a good abstraction yourself.
  2. You can use the interface and rely on an implementation package to provide a good implementation for it. If you find another package does a better job, it will be a very easy switch.

We're almost arriving at the core of my problem with the FIG these days. Sure, there's value in using tried and tested code, in not reinventing the wheel for every project. Matthias warns about the danger of doing that:

If you wrap PSR interfaces with your own classes you lose these benefits. You may end up creating an abstraction that just isn't right, or one that requires a heavy implementation that can't be easily replaced.

The benefit of using PSRs in comparison to running your own implementation, is that your own implementation raises tons of questions that have been answered by the FIG before:

  • What is the structure of the $headers array: header name as key, header value as value? All strings?
  • Same for $query; but does it support array-like query parameters? Shouldn't the query be part of the URI?
  • Should $uri contain the server hostname as well?
  • What if we want to use other request methods than get or post?
  • What if we want to make a POST request without a body?
  • What if we want to add query parameters to a POST request?
  • How can we deal with failure? Do we always get a string? What kind of exceptions do these methods throw?

So why spend time and money on creating another abstraction while we already have one?

Well, have you considered the fact that… maybe the FIG doesn't always come up with the best abstractions? That maybe the process that takes months of discussion by a small group of developers, to finally come up with an interface that contains a few methods, might not actually solve the problems that developers are dealing with in real life?

Sure the question of creating HTTP requests and responses has been answered by the FIG. It's an answer. Symfony and Laravel both have their own answer as well which are simpler if you ask me. Moreover, possibly better answers for my use cases.

We've been talking about HTTP abstractions, but Matthias gives another example: the container interface. He agrees that the FIG doesn't always come up with an abstraction that's relevant to the community's needs:

Without meaning to discredit the effort that went into it, nor anyone involved, there will always be standards that end up being outdated, like in my opinion PSR-11: Container interface.

Matthias carefully uses the word "outdated" here, though I want to say it's a plain irrelevant abstraction. The same way PSR-7 is irrelevant for most of the work I — and many others — are doing in Laravel or Symfony projects. I'm more than happy to ditch "interoperability" — what does that even mean when you're building a project closely tied to a framework and never intend to change it — and just use a simpler, straight forward, opinionated solution. It's also an abstraction, a good one, just not one that has an "official" name backed by the FIG.

Now some people tell me "you can't predict the future, maybe you do want interoperability somewhere in the next few years". I don't know about others but we actually outline the scope of projects in contracts with clients. They pay us to make an application specifically in one framework. There's no need for this level of interoperability.

Take a look, for example at both the container interface implementations of Symfony and Laravel, both implement \Psr\Container\ContainerInterface, and yet both add so many more methods. Implementing PSR-11 is merely a gimmick here for frameworks to be able to say "yes, we're PSR compliant"; because there's no real interoperability between these two.

Another example: PSR-7, the HTTP messages PSR. Both Symfony and Laravel don't implement PSR-7, because it simply doesn't solve their use cases. Oh and, just to be able to say "yes we're PSR compliant", there's the PSR-7 bridge, which basically applies the adapter pattern.

Do you realise that applying the adapter pattern is exactly the opposite of what the FIG is trying to achieve? The FIG wants a common abstraction to be used across frameworks, while the adapter pattern allows one interface to be used as another interface.


I want to end with making a slight change to Matthias' last sentence:

At the same time, we should also use PSR abstractions whenever it makes sense, since they will save us a lot of design work and will make our code less sensitive to changes in vendor packages.

I'd phrase it like this: "we should use abstractions whenever it makes sense". Whether it's the tried and tested Laravel or Symfony implementation of the container, HTTP client, caching, queuing, … These implementations work. They are valid abstractions, even if they don't carry the "PSR" name.

Yes, we should use abstractions; but no, we shouldn't use irrelevant and outdated abstractions. It's not because an abstraction carries the name "PSR" that it's suddenly better than others.

I figure there's a chance of some people getting angry by this post. You're allowed to, I’m open for that feedback. Please reach out to me via mail to tell me your thoughts. I don't question the sincerity and efforts of the FIG. I just genuinely believe they are trying to solve a problem that doesn't exist.

Let me end with how I started: the FIG has had a great impact on the PHP community, I'm very thankful for the early work they did as pioneers and the whole community needs to acknowledge that. I also think the FIG has reached its goal, and the project should be called complete.


As an addendum, I want to address one more point. I shared Matthias' original post on /r/php before publishing this one. There were some insightful discussions about it, and Matthieu Napoli, the author of PSR-11, pitched in.

I want to address one of the things he said, because I reckon it might be a counterargument that people bring up after reading this post as well. He said:

PSR-11 is great for libraries that want to interoperate with containers, for example:

  • Phinx to allow loading seed classes from your framework's container
  • Behat for doing dependency injection in feature contexts
  • Tactician command bus: load from your container (https://tactician.thephpleague.com/plugins/container/)
  • Faker for dealing with extensions
  • schmittjoh/serializer for lazy loading handlers from your container

In other words: I mainly look at PSRs from a framework's perspective, frameworks like Symfony or Laravel, while Matthieu is thinking about smaller, standalone, packages.

Here's one of the examples Matthieu gave in practice. Phinx optionally supports to set a container instance, which it'll use to resolve seed classes.

if ($this->container !== null) {
    $seed = $this->container->get($class);
}

And, fair enough: there seems to be some adoption at least in small, standalone packages. But what about the broader context? Are those features actually used by end users? Would it be worse if those package provided an adapter layer instead of relying on a third-party abstraction? There are a lot of ifs here, and I'd like to hear from users who actually have real-life experience with using these kinds of packages.

I mainly look at the FIG from a framework's point of view, I think that's relevant since it's the PHP framework interoperability group. I'm not entirely dismissing the merits of proper abstractions, I hope that was clear throughout this post.

]]>
2021-08-31T00:00:00+00:00
<![CDATA[ Named arguments and open source projects ]]> https://www.stitcher.io/blog/named-arguments-and-variadic-functions There are a few well known open source maintainers within the PHP community against the use of named arguments, citing maintenance overhead and backwards compatibility problems as reasons not wanting to use them.

I want to nuance those arguments a little bit.

# What's causing backwards compatibility problems?

The main fear is that supporting named arguments in, for example, a framework or open source package will increase the risk of breaking changes.

Imagine a package or framework exposing this class:

class QueryBuilder
{
    public function join(
        string $table, 
        string $leftColumn, 
        string $rightColumn, 
        string $type
    ) { /* … */ }
}

The problem with named arguments is that if users call this function with them, the framework now needs to treat parameter name changes as possible breaking ones:

$query->join(
    type: 'left',
    table: 'table_b',
    leftColumn: 'table_a.id',
    rightColumn: 'table_b.table_a_id',
)

If the framework wants to rename leftColumn and rightColumn to simply left and right, the above code — userland code — would break.

Here's the thing: no framework or package can prevent users from using named arguments, there simply isn't a way to disallow them. So either, as an open source maintainer, you:

  • treat argument name changes as breaking as soon as you support PHP 8;
  • ask users not to use named arguments; or
  • let users deal with those breaking changes themselves, and don't worry about it.

Being an open source maintainer myself: I choose option three. First of all: argument name changes only rarely happen; and second: I trust my users to be professional developers and know the consequences of using named arguments. They are smart grown ups, it's their responsibility.

So in summary for this first part: there's nothing the framework can do to prevent this kind of backwards compatibility issues besides making a note in the README on how they deal with argument name changes. Be consistent with whatever policy you choose, and you're fine.

# Named arguments as a cleaner syntax to deal with array data

The second way named arguments can be used, is in combination with variadic functions, essentially becoming a — in my opinion cleaner — shorthand for passing arrays of data:

$user = User::create(
    name: 'Brent',
    email: 'brendt@stitcher.io',
    company_id: 1,  
);

This is possible thanks to named arguments playing well together with variadic functions:

class User
{
    public function create(...$props) { /* … */ }
}

Passing a named argument list into this variadic create function will result in an array like this:

[
    'name' => 'Brent',
    'email' => 'brendt@stitcher.io',
    'company_id' => 1,  
]

Rewriting the above example without named arguments but arrays instead, would look like this:

$user = User::create([
    'name' => 'Brent',
    'email' => 'brendt@stitcher.io',
    'company_id' => 1,  
]);

I know which one of these two approaches I prefer. Disclaimer: it's the one that has better syntax highlighting and is shorter to write.

Here's the kicker: there isn't any possibility for breaking changes, because there aren't any hard coded argument names to begin with!


We really need to be more thoughtful about claiming that we cannot support named arguments in our open source packages because of backwards compatibility issues. In the first case there's nothing you can do either way, and the second case doesn't pose any danger of breaking changes.

Don't you agree? Send me an email or tweet and we can further discuss it. I'm open to be proven wrong.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-08-26T00:00:00+00:00
<![CDATA[ Rational thinking ]]> https://www.stitcher.io/blog/rational-thinking Now and then, I like to ask a simple coding style question on Twitter. It usually goes something like this: do you write FQCN's (fully qualified class names) in your doc blocks or not? In other words, do you write code like this:

/** @var \App\Models\Foo[] */
$arrayOfFoo = …

Or like this:

/** @var Foo[] */
$arrayOfFoo = …

Of course, the second example assumes you've imported the full class name at the top of your file.

use \App\Models\Foo;

// …

/** @var Foo[] */
$arrayOfFoo = …

I've asked this question maybe three or four times over the years, and I consistently get the same answer from a large group of respondents: they use the full class name, so that they don't have to scroll to the top of the file to know what they are dealing with.

My response, time and time again, has been: so what about real types? Property types, argument types, return types? Do you use the full class name in those cases as well?

class Bar
{
    public function baz(\App\Models\Foo $foo): \App\Models\Foo
    {
        // …
    }
}

I've actually had one person say "yes" to that question, and fair enough, they are consistent. But all the others say they don't. They write it like this:

use \App\Models\Foo;

class Bar
{
    public function baz(Foo $foo): Foo 
    {
        // …
    }
}

So what's the difference between doc block types (which are required in some cases because PHP's type system is limited), or real types? Why do you want to import one, but not the other; and how does "not scrolling to the top" make a good argument when it isn't consistent?


Programmers often take pride in their rational thinking. We look at problems from a slightly different angle than non-programmers do. It's probably this personality trait that got many people into programming to begin with.

I don't mind that people aren't consistent in how they write types in or out of doc blocks. But I am always surprised with how difficult it is to defend that opinion, once you start asking deeper questions, once people are forced to think about it a little more.

It makes me wonder, could it be that there are more such opinions that we think we're sure of; but that, in reality, we haven't actually thought through all that well? Tabs or spaces, light or dark colour schemes, active record or entity mapper, dependency injection or service location, …

How many "rational opinions" do we have that turn out to be irrational after all? Opinions that are habit-driven; that we think of as "the best option" — not because they are, but because they've worked in the past and we're comfortable using them.

Are we — the "rational thinkers" — in fact, just like everyone else, influenced by emotion, sometimes without even knowing it?


The real reason to always use full class names in doc blocks, by the way, is because PHP doesn't have a reflection API for import statements. So if you want to map a class name from a doc block to its FQCN, you'd need to manually parse that PHP file. Mind you: import statements are quite difficult to parse: there are aliases, partial namespace imports, grouped imports, function imports, and more.

Fortunately, the problem I describe here isn't really a problem anymore. There's a package called phpdocumentor/type-resolver that supports exactly this kind of parsing.

So the only real argument against importing doc block types, turns out to be not so relevant anymore.


How sure are we of our opinions? How fiercely and emotionally do we defend those opinions, even when we haven't thought them through all that well? And can we admit it when we're wrong, maybe apologise and move on?

I don't want to start a fight over tabs or spaces, importing types or not; but I do want to encourage you to critically look at your own opinions. Wonder whether you thought them through well enough; and if you'd be willing to change them, if they turn out to be more biased than you thought.


Are you angry right now? Do you want to tell me I'm wrong? Send me an email or a tweet and we can have a proper internet fight! No really, I would very much appreciate you challenging these thoughts if you don't agree, I'm looking forward to hearing from you!

Oh and if you want to stay up-to-date about my content and these kinds of posts, consider subscribing to my newsletter.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-08-25T00:00:00+00:00
<![CDATA[ Why do I write? ]]> https://www.stitcher.io/blog/why-do-i-write This post was originally sent to my newsletter on August 12, 2021. I originally didn't plan to blog about it, but given the reactions that I received, I decided it would be good to share it with a larger audience.


This is going to be a slightly different style of newsletter than you're used to. I'm not going to blog or tweet about this, I just want to share these thoughts with you, because you can hit the reply button and let me know your feedback, one on one.

So: why do I write? "Being a content creator" has been a hobby of mine for many years now (I actually made Minecraft videos before writing a blog, believe it or not) and I've always wanted to stay true to myself. My number one rule is that I only write about what I want, when I want to.

Yesterday though, my colleague Sebastian sent out a new edition of his newsletter that made me stop and think about where I am today. You should totally subscribe to Sebastian's newsletter by the way, I'm always happy to receive one of his mails in my inbox.

Anyway, Seb wrote about how we live in a culture where it seems that everyone and their grandma is working on a successful side-project, and how that affects him. He called it the the monetization trap — a must read!

I immediately told him I felt the same way he described in his post, and we had a small conversation about it. During our conversation, he shared an awesome blog post — another must read — and I must admit it struck a nerve with me. Here's a quote from that post:

I see a lot of ["fast food content"] on the web sadly. People who once had something to say that are now trapped in an endless cycle of recycled content, month after month, year after year, saying the same thing, over and over and over again.

I felt at least a little shame: I've done this myself. Maybe not to extremes, but I have written blog posts in the past, because I knew people like to read them, because I knew they have a high chance of going viral and giving me an adrenaline rush for a day, maybe two.

I was suddenly reminded of my number one rule, and came to realise that I might not always have followed it, even though I thought I did.

Now, I don't regret writing those kinds of posts, in fact I like to believe some of them have had a significant impact on people's professional developer's life (some people have told me it did). And yet, those aren't the posts I'm most proud of.

In fact, the ones I'm most proud of are actually the least popular ones.

Looking back, not following that one rule might have caused some levels of added stress these past few months, and I'm thankful for Seb's and Manu's writings to make me realise that.

Having named the problem and knowing it's there is actually very comforting, it means I can now work on it. It'll be good to focus on things that I want to, instead of things I must. We'll see what the future brings.

Having written all of this, I'm actually thinking of reposting it on my blog one day, because maybe there are more people out there experiencing the same pressure — consciously or not. I want to see your responses first though, so feel free hit the reply button if you have any thoughts! I'll try my best to answer soon.

Kind regards

Brent


I'd like to ask you — the blog reader — the same: if you have any thoughts to share about this topic, please send me an email (if the mailto link doesn't work: brendt@stitcher.io). I'll try my best to answer all mails, it might take a while!

If this post resonates with you, please consider sharing it with your audience. I think there's an important message here, but I'll let you be the judge of that.

Thanks for reading! This post is part of my "Dev Diaries" series where I write about my own and personal experiences as a developer. Would you like to read some more?

If you want to stay up to date about what's happening on this blog, you can follow me on Twitter or subscribe to my newsletter:

]]>
2021-08-13T00:00:00+00:00
<![CDATA[ The Road to PHP 8.1 ]]> https://www.stitcher.io/blog/the-road-to-php I'm happy to announce a fun little project today: The Road to PHP 8.1. It's a 10-day popup newsletter that'll teach you about all the shiny new things in PHP 8.1. The only thing you'll need to do is drop your name and email address, and you'll receive a daily email for the next 10 days.

Worried about spam or followup mails? You'll be automatically unsubscribed after those 10 days, I'm not keeping your email address!

You can subscribe here, and see whether it's your thing or not.

]]>
2021-08-06T00:00:00+00:00
<![CDATA[ We don't need runtime type checks ]]> https://www.stitcher.io/blog/we-dont-need-runtime-type-checks Do you want to make a guess about when I last encountered a TypeError in one of my projects? To be honest, I can't remember, so it's probably a few years. Coincidentally, I also started relying on static analysis around the same time.

I'm fairly certain that I could disable PHP's runtime type checking altogether — if that was a thing — and have a perfectly working codebase.

Because, here's the thing about runtime type checks: they are a debugging device, not so much a safety net. Runtime type errors make it easier to detect and fix bugs, but the reality is that whenever a type error is triggered, our code still crashed at runtime. When type errors occur in production, the end result is the program crashing, nothing you can do about it.

Now, I've written about type systems before, you'll find some references at the end of this post; so if you want more background information about them be sure to do some followup reading. Today, I want to discuss how static analysis has the power to revolutionize the way we write PHP code much more than it already does today, and how it can open doors to many new possibilities.

The tradeoff? We need a community-wide mind shift: there are still many PHP developers (including internal developers) who are taken aback by the idea of static type checking. My only goal today is to encourage you to think outside your box, to imagine what would be possible if PHP shifted towards a built-in, statically type-checked model.

Whether you're into static type systems or not, I promise it'll be interesting nevertheless. Let's dive in!


A while back, it became clear that generics in PHP are probably not coming any time soon. One of the main reasons being that there are two ways to implement them, and both have significant problems. Either there's too large an impact on runtime performance, or the implementation is just way too complex to get right.

Both approaches did assume a runtime type-checked implementation though. So I shared a thought experiment with internals: what if we only need to support the syntax for generics, and have static analysers do all the checks? I called them transpiled generics back then, but runtime-erased or runtime-ignored generics is probably a better name.

My thinking was that adding support for generic syntax shouldn't be all that hard, and without runtime type checks, there shouldn't be any performance impact. It makes sense if you think about it from a static analysis point of view: your code has already been analysed, and it's been proven to work correctly, so there's no more need for runtime type checks. On top of that, most developers only want generics as a way to get better code insights while coding; does the "I want to know what items are in an array" argument ring a bell?

Of course there could still be some type information exposed at runtime via reflection, but we can't deny that having types ignored at runtime is a major paradigm shift that most PHP developers aren't used to. Here's Sara's response on my runtime-ignored generics idea (which Hack already does) and how it requires a mind-shift:

Oh, I agree that there's real value in HackLang's approach. It's just that there is a mountain of inertia around "The PHP Way" and it's going to take an equal and opposite mountain to alter course. Entirely possible, even probable, but we won't see that level of shift in the next five years.

It'll take a few more years, but it's probably the way PHP will evolve anyway, according to Sara.

Now, before I get an angry mob chasing me: I'm not suggesting we bundle a static analyser in PHP that you're required to run (that would mean a "compilation" step in practice). What I am suggesting is that we can disable PHP's runtime type checks if we want to, and deal with the consequences ourselves. If you want to use generics in such a scenario then, yes, you'll have to use a static analyser. If you don't want to, that's fine, but you won't be able to use generics.

Of course, in an ideal world, PHP would ship with such a built-in, opt-in static analyser; instead of users having to rely on third party tools. Because the main problem with third party tools is consistency between them. Case in point: PhpStorm will support a basic form a generic-type doc blocks in their next release, years after Psalm and PHPStan added support for them.

If there was an official spec supported by internals, static analysis vendors wouldn't have any choice but to follow that spec. That's the major problem with doc block type checks at the moment: there are no rules, so every vendor does whatever they want.

The idea of a centralised static analyser isn't new, by the way, but you can imagine it's a massive undertaking to get right. Here's Rasmus on the matter a few years ago:

Now if the RFC was a plan for baking a compile-time static analysis engine into PHP itself, that would be interesting. But that is a massive project.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

When I asked Nikita about the idea of runtime-ignored types and generics, he described the main problem with generics that have a runtime implementation like so:

Complexity is a pretty big problem for us, and I think severely underestimated by non-contributors. Feature additions that seem simple on the surface tend to interact with other existing features in ways that balloon the complexity.

He also called runtime-erased generics "the cowards way out". The reason Nikita says that is because, if runtime-erased generics were supported, it would mean there's a huge inconsistency within PHP's type system where some parts are checked at runtime, and other parts are checked statically.

So to be clear: I don't think runtime-erased or runtime-ignored generics are where we should start. We first need PHP without runtime type checks whatsoever, and then we can think about building on top of that.

I asked Nikita if he thought such a version of PHP would have merit, he said this:

I think that would be a good thing... but then again, lots of things would be different in PHP if we'd do a clean-slate redesign now. We have to work within the constraints we have, somehow.

From a userland-developer point of view, I think we can work with the given constraints, as long as there's a large enough user base supporting these ideas.


So what if internals don't think of it as achievable to optionally step away from runtime type checks? Or what if such a mind shift won't happen within the next decade?

Well, there is another approach, one that actually has been tried and proven before in another language: TypeScript. The power and immense popularity of TypeScript comes from its static type checker. Sure, there's an extra compilation step to transform TypeScript code to regular JavaScript, but developers seem to manage that just fine — because they know how much they gain from proper static analysis.

The same has been tried in PHP before, by the way: there was Hack that at one point did compile to PHP, and there was preprocess, a project by Christopher Pitt. Unfortunately, Hack took another direction, and preprocess halted; not because of implementation problems, but because of lack of support in IDEs and the wider community.

If transpiling PHP gains more traction again, it'll definitely need proper IDE support if we ever want a chance for it to succeed. That's the benefit of an internals-backed implementation: when it's in PHP core, IDEs and other external tooling can't do anything but to follow along. A community-driven transpiler wouldn't have that benefit.


So, this is where we are today:

  • PHP's runtime type checker is reaching its limitations (generics being the most obvious example)
  • There are already runtime-ignored types (doc blocks), but there's no consensus on syntax and usage across static analysis communities
  • Runtime-ignored types require a mind-shift that many developers find difficult at this point
  • Transpiling PHP is possible, it's been done before, but it's a massive undertaking and likely to fail again if tried without proper support

I see much more potential for static analysis. It has made my code more stable and easier to write, and I couldn't do without it anymore. On the other hand, the community and toolset has still a long way to go, and we're all playing a part in that journey.

I'd love for internals to further explore the static analysis side of PHP: it's more than just a userland addon to the language, and will only continue to grow tighter to PHP in the future.

I'd want to see these changes to the language today, though I know that's an unrealistic expectation. I hope Sara's assessment is right in that this is the form PHP is evolving to, but unfortunately it'll take a few more years to get there. This blog post is just an attempt to give one more little push in the right direction.

What's your opinion? Let me know.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Sidenotes

I'll probably add some more information to this section when people read this post and share their feedback, though I could already think of a couple of things.

# Psalm + Rector as a "transpiler" ?

Someone mentioned the idea about using PHP without runtime type checks by using a combination of Psalm and Rector: Psalm first analysed the codebase, and Rector removed all type hints afterwards to generate a "compiled" production build.

It's an interesting step to further explore the problem space, and while it doesn't mean there's support for custom syntax, there might be potential in the Psalm + Rector combo.

# Why not use … ?

The classic question that gets asked by skeptics: why not use Java or C# or whatever other language if you want to rely on static analysis so much?

Well, the answer is simple: the ecosystem.

# What about the FIG?

The main problem with doc block types is one of consistency, which maybe the FIG could solve?

I'm not sure about the relevance of the FIG these days: I can't imagine the FIG having an influence over the development of, for example, PhpStorm; and there are very little significant frameworks still following PSRs to the rule.

So, yes, maybe? I'd love to be proven wrong.

# Python already does this

Python's whole type system is built on type-erasure. So it's interesting to note that what I'm proposing here isn't anything new.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-07-26T00:00:00+00:00
<![CDATA[ Would you like some optimism or realism with your estimate, sir? ]]> https://www.stitcher.io/blog/optimistic-or-realistic-estimates I've already made quite a lot of project estimates in my programming career. In these last three years, I've been estimating (and building) lots of features for one specific project.

Over time, some kind of dynamic has grown between our client and I. They ask me "Brent, would it take much work to build … ?", to which I always reply: "everything is possible, it just depends on how much you want to pay"; our client in turn always says "I know! But I need an accurate estimate first!".

Our client has told me several times that my estimates are actually pretty accurate, so I guess I must be doing something right, and I figure I'd share my process — based on nothing but my own experience — with you. Maybe it can help someone out there.


First things first, everything that's between 2 and 4 hours of work, I feel comfortable estimating with a buffer in mind. I usually multiply my original idea by a factor of 2, and that seems to result in accurate, small-scale estimates almost every time.

The problems start with features that take longer than a few hours. I don't think the human brain is capable of fully comprehending the scope of work at these scales, and this is a problem that's only growing more significant the larger the scope becomes.

Personally, I have a tendency to approach my estimates very optimistically: "if everything goes right, it'll probably take three weeks". And that's what I used to tell my clients. The problem with that approach is that they only hear "three weeks", and forget about the "if everything goes right" part.

I've now learned that nothing ever goes "just right"; and that there will always be unforeseen changes. I should account for those as well.

So I ask my client: would you like to have an optimistic or realistic estimate? In practice, my realistic estimate is simply the optimistic one, but multiplied by two or three, and represented as a range. For example: if my optimistic estimate is 2 weeks, I'll tell my client it'll take somewhere between 2 weeks and 4 or 5 weeks. If the optimistic estimate already spans months, I prefer to multiply by three, because the larger the estimate, the more things that can change.

I've been using this tactic for years now, and it seem to actually work pretty well.

What I think is key here, is that there's an open communication between my client and myself. There's a level of trust, and I feel safe to just tell them the truth, even though I know sometimes they might have liked a more optimistic version.

This level of trust and communication comes from working together for a couple of years though. It might be lacking at the start of a new project. That's why so many starting projects are under-estimated, and why there's almost always a point in the project where expectations need to be adjusted (with all the consequences that come with it, these usually aren't happy times in a project's lifespan).

So I try to ask clients up front: do you want an optimistic or realistic estimate? I tell them I know the realistic one will be better in the long run.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-07-23T00:00:00+00:00
<![CDATA[ PHP 8.1: before and after ]]> https://www.stitcher.io/blog/php-81-before-and-after The release of PHP 8.1 will be here within a few months, and once again there are many features that get me excited! In this post I want to share the real-life impact that PHP 8.1 will have on my own code.

# Enums

A long-awaited feature, enums are coming! There's little to be said about this one, besides that I'm looking forward to not having to use spatie/enum or myclabs/php-enum anymore. Thanks for all the years of enum support to those packages, but they are the first I'll ditch when PHP 8.1 arrives and when I change this:

/**
 * @method static self draft()
 * @method static self published()
 * @method static self archived()
 */
class StatusEnum extends Enum
{
}

PHP 8.0

To this:

enum Status
{
    case draft;
    case published;
    case archived;
}

PHP 8.1

# Array unpacking with string keys

This one might seem like a small one, but it has bothered me more than once: only list arrays could be unpacked before PHP 8.1:

$a = [1, 2, 3];
$b = [4, 5, 6];

// This is allowed
$new = [...$a, ...$b];

PHP 8.0

While arrays with string keys cannot:

$a = ['a' => 1, 'b' => 2, 'c' => 3];
$b = ['d' => 4, 'e' => 5, 'f' => 6];

$new = [...$a, ...$b]; 

// You'd need to use array_merge in this case
$new = array_merge($a, $b); 

PHP 8.0

And so, one of the great features of PHP 8.1 that will make my life easier, is that arrays with string keys can now be unpacked as well!

$a = ['a' => 1, 'b' => 2, 'c' => 3];
$b = ['d' => 4, 'e' => 5, 'f' => 6];

// :)
$new = [...$a, ...$b]; 

PHP 8.1

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Class properties: initializers and readonly

Another stunning new feature is once again such a quality-of-life improvement that I've struggled with for years: default arguments in function parameters. Imagine you'd want to set a default state class for a BlogData object. Before PHP 8.1 you'd have to make it nullable and set it in the constructor:

class BlogData
{
    public function __construct(
        public string $title,
        public ?BlogState $state = null,
    ) {
        $this->state ??= new Draft();
    }
}

PHP 8.0

PHP 8.1 allows that new call directly in the function definition. This will be huge:

class BlogData
{
    public function __construct(
        public string $title,
        public BlogState $state = new Draft(),
    ) {
    }
}

PHP 8.1

Speaking of huge, have I mentioned yet that readonly properties are a thing now?!?

class BlogData
{
    public function __construct(
        public readonly string $title,
        public readonly BlogState $state = new Draft(),
    ) {
    }
}

PHP 8.1

Oh and don't worry about cloning, by the way: I've got you covered.

# First-class callable syntax

As if that wasn't enough, there's now also the first-class callable syntax, which gives you a clean way of creating closures from callables.

Previously you'd had to write something like this:

$strlen = Closure::fromCallable('strlen');
$callback = Closure::fromCallable([$object, 'method']);

PHP 8.0

In PHP 8.1, you can do… this:

$strlen = strlen(...);
$callback = $object->method(...);

PHP 8.1


There are even more features in PHP 8.1, but these are the ones I'm most excited about. What's your favourite one? Let me know on Twitter!

]]>
2021-07-16T00:00:00+00:00
<![CDATA[ An event-driven mindset ]]> https://www.stitcher.io/blog/an-event-driven-mindset I'm working on a new feature for our Spatie website: I'm adding a lightweight form of gamification for our ±10,000 users.

Here's how it works: every time a user watches one of our course videos, every time they complete a video series or a pull request gets merged on GitHub, they'll receive experience points. On top of that, there are also some achievements to reward users for their efforts. For example, there's a "10 pull requests" achievement, as well as a "100 XP" achievement and a few others.

Getting an achievement will reward the user with a digital badge, and in some cases they'll get some kind of certificate as well.

As you can see, it's a small, contained system, with well-defined boundaries. And I'd like to discuss one aspect of it with you today.


Let's zoom in on one of the possible flows: the pull request reward system. There are a few steps to it:

  • Register a user's pull request
  • Award some XP for it
  • Check whether this pull request is eligible for an achievement
  • If that's the case, unlock a badge and notify the user

Writing this flow down in bullets makes it feel like the pull request reward system is a linear process. In fact, we could write it like so:

class RegisterPullRequest
{
    public function __invoke(PullRequestData $data): void
    {
        // Persist the pull request
        $pullRequest = PullRequest::create(...$data);
        
        $user = $pullRequest->user;
        
        // Award XP
        $user->award(10);
        
        $pullRequestCount = count($user->pullRequests);
        
        // Determine whether an achievement should be triggered
        if (in_array($pullRequestCount, [10, 50, 100])) {
            $achievement = new PullRequestAchievement($pullRequestCount);
            
            // Persist the achievement  
            $user->unlock($achievement);
            
            // Notify the user
            $user->notify(new AchievementNotification($achievement));
        }
    }
}

You might want to refactor this code to separate methods or to use injected actions or service classes if you prefer that style of programming; but I wanted to keep this example concise, to easily clarify a flaw with this approach.

This code clearly represents the ordered steps we listed at first, but there are some hidden costs that come with it; costs that might not be apparent at the time of writing it. I can see two problems hidden within this implementation:

  • We've hard-coded the flow of our program into a fixed order
  • We've mixed a bunch of responsibilities into one giant class

Let's consider "awarding XP" and "unlocking achievements" for a moment. These are two equally important parts of our system. In fact, there's also an achievement for XP being awarded, which means that our current implementation is either lacking, or that there's some added functionality in $user->award(10); that we don't know about. Let's assume the latter for now.

Even though these two parts are equally important and not directly dependent on each other, we've combined them into one process because it seems like they belong together. However, an unfortunate side effect of doing so, is that our RegisterPullRequest class is growing larger and more complex. Making a change to how pull request achievements are handled, will inevitably take us to the same place where XP rewards are handled.

While you might find it still relatively easy to reason about this isolated (simplified) example, I think most of us can agree that yes, in fact, we're mixing several processes together into one: we're creating some kind of "god-class" that manages and oversees a complex process. We've created a single point of failure. And the more complex our business becomes, this code has the potential to grow larger, more complex and more difficult to reason about.

Speaking for myself, I've written these kinds of classes more than I'd like to admit, and I've seen it applied in many other code bases as well. And from experience, I can tell you they grow much larger than the example we're working with today.

I understand why we get to this point: we'll always need some kind of entry-point in our code, no? A complex process will need to be tied together somehow; we can't avoid that, right?

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

When I first learned about event-driven systems, I was hesitant, maybe even skeptical about them. Events introduce an unavoidable layer of indirectness to our code that makes it more difficult to follow the "flow of our program". However, keeping everything tightly coupled together also makes it difficult to understand our program flow, just in another way.

The indirectness of event-driven systems is actually the solution to our problem. While event-driven architecture might feel overly complex at first glance, it offers exactly the kind of flexibility we need to model our processes in a clean way — and better yet: in a way that's scalable and maintainable for years to come, much more than our current solution.

In an event-driven system, both "XP rewards" and "achievement unlocks" are treated as two standalone systems. They don't need to know of each other. The only thing they need to know is when a pull request is merged — when an event happens.

Both our systems are now event listeners that will act whenever a PullRequestMerged event is dispatched:

class AchievementManager
{
    public function __invoke(PullRequestMerged $event): void
    {
        $pullRequestCount = User::find($event->userId)
            ->pullRequests
            ->count();
        
        if (! in_array($pullRequestCount, [10, 50, 100])) {
            return;
        }
        
        $achievement = new PullRequestAchievement($pullRequestCount);
        
        $user->unlock($achievement);
        
        $user->notify(new AchievementNotification($achievement));
    }
}
class ExperienceManager
{
    public function __invoke(PullRequestMerged $event): void
    {
        $user = User::find($event->userId);
        
        $user->award(10);
    }
}

Now that these two systems are separated, it's much easier to reason about them because they live in isolation.

It doesn't stop there, by the way. What about that "achievement for a given amount of XP" I mentioned at the beginning of this post? ExperienceEarned could be an event itself that our AchievementManager listens for as well:

class AchievementManager
{
    public function onPullRequestMerged(PullRequestMerged $event): void
    { /* … */ }
    
    public function onExperienceEarned(ExperienceEarned $event): void
    {
        $user = User::find($event->userId);
        
        $currentCount = $user->experience;
        
        $previousCount = $currentCount - $event->amount;
        
        if ($previousCount >= 100) {
            return;
        }
        
        if ($currentCount < 100) {
            return;
        }
        
        $achievement = new ExperienceAchievement('100 XP!');
        
        $user->unlock($achievement);
        
        $user->notify(new AchievementNotification($achievement));
    }
}

You might even begin to see some opportunities yourself: what about sending a mail after an achievement was unlocked? That could also be driven by an event, so that AchievementManager doesn't need to think about it — we could add a listener that handles mails. What about persisting the pull request to the database? That could be event-driven as well. Achievements that earn experience? The list goes on.

This is the beauty of event-driven systems: by removing tightly-coupled components, we allow room for much more flexibility, while keeping our individual components small and sustainable. Besides that, events are an excellent way of handling micro-service messaging, horizontal scaling and more — though, discussing all these benefits would be too much to cover in one blog post.

Of course, I'm also glossing over some important details: what about eventual consistency? Or what about persisting events themselves? There's much more to event-driven systems than what I showed today, but I did show you the power of thinking with events. That idea alone has revolutionized the way I look at code, and I hope you will give it some more thought as well.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

If you're really interested in the topic, I'd like to share my course on event sourcing in Laravel with you. It's an in-depth course that covers many topics related to event-driven systems, and you don't need any prior Laravel or PHP knowledge to learn tons of stuff from it. You can read a sample chapter or two if you'd like to know more.

With all of that being said, let me know your thoughts on Twitter and leave a like if you appreciated this post, thanks!

]]>
2021-07-14T00:00:00+00:00
<![CDATA[ PHP 8.1: cloning and changing readonly properties ]]> https://www.stitcher.io/blog/cloning-readonly-properties-in-php-81 Note: PHP 8.3 adds a built-in way of cloning readonly properties, although it's rather limited in its possibilities. Read more.

In PHP 8.1, readonly properties aren't allowed to be overridden as soon as they are initialized. That also means that cloning an object and changing one of its readonly properties isn't allowed. It's likely that PHP will get some kind of clone with functionality in the future, but for now we'll have to work around the issue.

Let's imagine a simple DTO class with readonly properties:

class Post
{
    public function __construct(
        public readonly string $title, 
        public readonly string $author,
    ) {}
}

PHP 8.1 would throw an error when you'd clone a post object and tried to override one of its readonly properties:

$postA = new Post(title: 'a', author: 'Brent');

$postB = clone $postA;
$postB->title = 'b';

Error: Cannot modify readonly property Post::$title

The reason why this happens is because the current readonly implementation will only allow a value to be set as long as it's uninitialized. Since we're cloning an object that already had a value assigned to its properties, we cannot override it.

It's very likely PHP will add some kind of mechanism to clone objects and override readonly properties in the future, but with the feature freeze for PHP 8.1 coming up, we can be certain this won't be included for now.

So, at least for PHP 8.1, we'll need a way around this issue. Which is exactly what I did, and why I created a package that you can use as well: https://github.com/spatie/php-cloneable.

Here's how it works. First you download the package using composer, and next use the Spatie\Cloneable\Cloneable trait in all classes you want to be cloneable:

use Spatie\Cloneable\Cloneable;

class Post
{
    use Cloneable;
    
    public function __construct(
        public readonly string $title, 
        public readonly string $author
    ) {}
}

Now our Post objects will have a with method that you can use to clone and override properties with:

$postA = new Post(title: 'a', author: 'Brent');

$postB = $postA->with(title: 'b');
$postC = $postA->with(title: 'c', author: 'Freek');

There are of course a few caveats:

  • this package will skip calling the constructor when cloning an object, meaning any logic in the constructor won't be executed; and
  • the with method will be a shallow clone, meaning that nested objects aren't cloned as well.

I imagine this package being useful for simple data-transfer and value objects; which are exactly the types of objects that readonly properties were designed for to start with.

For my use cases, this implementation will suffice. And since I believe in opinion-driven design, I'm also not interested in added more functionality to it: this package solves one specific problem, and that's good enough.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-07-06T00:00:00+00:00
<![CDATA[ What about typed request classes? ]]> https://www.stitcher.io/blog/what-about-request-classes In some of our larger Laravel projects, we prefer to map request data to data transfer objects. By doing so we gain static analysis insights in what kind of data we're actually dealing with.

Such a request/dto setup usually looks something like this. Here's the request class handling validation of raw incoming data:

class PostEditRequest extends Request
{
    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'unique:posts,title', 'max:255'],
            'status' => ['required', 'string'],
            'body' => ['required', 'string'],
            'date' => ['required', 'date_format:Y-m-d'],
            'author_id' => ['nullable', 'exists:authors,id'],
            'tags' => [new CollectionRule(Tag::class)],
        ];
    }
}

And here's the DTO that represents that data in a way so that PHP, our IDE and external static analysers can understand it (note that I'm using our data-transfer-object package here):

class PostEditData extends DataTransferObject
{
    public string $title;
    
    public PostStatus $status;
    
    public string $body;
    
    public Carbon $date;
    
    public ?string $authorId;
    
    #[CastWith(ArrayCaster::class, itemType: Tag::class)]
    public array $tags;
}

Finally, there's the controller in between that converts the validated request data to a DTO and passes it to an action class to be used in our business processes:

class PostEditController
{
    public function __invoke(
        UpdatePostAction $updatePost,
        Post $post, 
        PostEditRequest $request,
    ) {
        return $updatePost(
            post: $post,
            data: new PostEditData(...$request->validated()), 
        );
    }
}

I've been thinking about the overhead that's associated with this two-step request/dto transformation. In the end, we only really care about a valid, typed representation of the data that's sent to our server, we don't really care about working with an array of raw request data.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

So why not do exactly that: have a way for our request classes to be properly typed, without the overhead of having to transform it manually to a DTO?

I could build up some suspense here to get you all excited about it, but I trust my readers to be able to draw their own, informed conclusions, so I'm just going to show you what it would look like in the end:

class PostEditRequest extends Request
{
    #[Rule(UniquePostRule::class)]
    #[Max(255)]
    public string $title;
    
    public PostStatus $status;
    
    public string $body;
    
    #[Date('Y-m-d')]
    public Carbon $date;
    
    public ?string $authorId;
    
    #[Rule(CollectionRule::class, type: Tag::class)]
    public array $tags;
}

Some people might say we're combining two responsibilities in one class: validation and data representation. They are right, but I'd say the old approach wasn't any different. Take a look again at the rules method in our old request:

class PostEditRequest extends Request
{
    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'unique:posts,title', 'max:255'],
            'status' => ['required', 'string'],
            'body' => ['required', 'string'],
            'date' => ['required', 'date_format:Y-m-d'],
            'author_id' => ['nullable', 'exists:authors,id'],
            'tags' => [new CollectionRule(Tag::class)],
        ];
    }
}

We're also validating type information here, it's just more hidden and can't be interpreted by an IDE or other static analyser. The only thing I suggest we do different is to properly use PHP's built-in type system to its full extent, and fill the gaps for more complex validation rules with attributes.

Finally, our controller could be refactored like so:

class PostEditController
{
    public function __invoke(
        UpdatePostAction $updatePost,
        Post $post, 
        PostEditRequest $data,
    ) {
        return $updatePost(
            post: $post,            data: new PostEditData(...$request->validated()),            data: $data, 
        );
    }
}

I didn't just come up with this idea by the way, there are a number of modern web frameworks doing exactly this:

And, finally: I don't think implementing this in Laravel would be all that difficult. We could even create a standalone package for it. All we need to do is build the request rules dynamically based on the public properties of the request, and fill them whenever a request comes in. I reckon the biggest portion of work is in creating the attributes to support all of Laravel's validation rules.

Anyway, I'm just throwing the idea out there to see what people think of it. Feel free to share your thoughts on Twitter with me.

]]>
2021-06-29T00:00:00+00:00
<![CDATA[ PHP version stats: July, 2021 ]]> https://www.stitcher.io/blog/php-version-stats-july-2021 Last month, Jordi announced he'd be sunsetting his PHP Version Stats blog series, so that he can focus on other types of content. As a replacement, he made a dashboard with live version stats that you can check out any time you want.

I made sure to let Jordi know how much I appreciated all the work he put into this, but also told him I felt kind of sad the series would end. While real-time stats are definitely useful, I did enjoy the occasional post that popped up every 6 months or so. They were a good interpretation of the data, and always sparked interesting discussions.

So I asked Jordi if he'd be ok with me continuing the series, which he was. And so, here we are, with a brand new version stats update!

I will keep a slightly different schedule though: I'll post in July and January, because I'm interested in seeing early adaption of the newest PHP version a month or so after it's released (which is usually at the end of November). I admit I'm two days early with this one, that's because of some upcoming holiday plans. And one more disclaimer: the data used in this post only looks at the composer ecosystem, and while composer represents a large part of the PHP community, it doesn't represent the whole.

With all of that being said, let's dive in!


# Usage Statistics

Let's start with the raw numbers: the percentage of PHP versions being used:

Version %
8.0 14.7
7.4 46.8
7.3 19.2
7.2 10.4
7.1 3.8
7.0 1.3
5.6 1.1

Note that I've excluded versions with less than 1% usage, in practice this means everything older than 5.6, as well as 8.1 (which hasn't been released yet) is excluded. Let's visualise this year's data and compare it to last year's:

Evolution of version usage, now and in May, 2020

PHP 7.4 has seen significant growth compared to last year. In fact, it looks like many people went straight from older versions to 7.4. Let's hope we'll be able to observe the same trend next year for PHP 8. Major version updates tend to take a little longer though; I assume that's because developers are wary of the breaking changes associated with them. Good news though: the update from PHP 7.4 to PHP 8 is actually surprisingly easy, thanks to the deprecation work that has been done in the 7.x series. It might be worth considering updating soon, not only because active support for PHP 7.4 ends in less than half a year, but also because PHP 8 brings tons of new and useful features.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

Next, let's take a look at the all-time evolution chart, which also clearly visualizes the growth of PHP 7.4:

All time evolution

I want to emphasize once more that the update from PHP 7.4 to 8 isn't all that hard, and truly worth investing a little time in. There are tools like Rector and PHP CS Fixer that can help make the upgrade even more smooth.

# Required versions

I've used Nikita's popular package analyzer to download the 1000 most popular composer packages, and wrote a little script to get the lowest version they support from their composer.json. Here are the results:

Version #
8.0 117
7.4 56
7.3 133
7.2 142
7.1 182
7.0 31
5.6 61
5.5 43
5.4 41
5.3 97
5.2 12
5.0 2

You'll notice there are a little less than 1000 packages included, that's because some of the 1000 most popular packages don't specifically require a PHP version.

It's interesting how 7.1 is still the minimum required version for almost 20% of the analysed packages. 7.2 adds another 15%. In fact, only 18% of the most popular packages require an actively supported version as their minimum.

I'm an open source maintainer myself, and I believe we have a responsibility to the community to help keep their software stack safe and secure. This starts with only supporting active PHP versions. So I hope to see this number shift more towards PHP 8 in the near future because, remember, PHP 7.4 is already nearing its end!

That being said, it's clear that many users are moving towards a supported PHP version, despite lower package requirements. I am a little worried about the move to PHP 8 though, and I hope we'll see broader adoption soon.


What are your thoughts on these stats? Are you looking forward to PHP 8.1? Let me know your thoughts on Twitter and subscribe to my newsletter if you want to be kept up-to-date about these posts!

]]>
2021-06-28T00:00:00+00:00
<![CDATA[ Opinion-driven design ]]> https://www.stitcher.io/blog/opinion-driven-design

I once worked at a company that wrote and maintained an in-house framework. Over the course of ten years, they probably made around 250 websites and applications with it. Despite many shortcomings, the company kept using their framework for a simple reason: they were in control.

Thanks to that control, they were able to tailor their framework to their own needs without any overhead. And, while I would argue that using popular, community-backed frameworks is almost always the better choice over writing your own , I can appreciate some of their reasoning.

Except, in the end, they utterly failed in creating what they originally set out to do. Instead of a tool shaped specifically for their needs, the framework's core developers often wanted flexibility: they dreamt of open sourcing their framework and it growing popular, so it needed to handle as many cases as possible to reach as wide an audience as possible. And thus, flexibility and configuration were often prioritized, even though they rarely added much — if any — value to company projects. The core developers were always able to convince the not-so-technical managers; while in reality, the design and flexibility of this framework was often just a burden for me and my colleagues to deal with.

This mindset of "high configurability and flexibility" is, unfortunately, common in software design. I'm not sure why, but somehow programmers — myself included — often think they need to account for every possible outcome, even when those outcomes aren't relevant to their use cases. Many of us deal with some kind of fear of losing our audience if the code we're writing isn't able to handle the most specific of specific edge cases. A very counter-productive thought.


Lately I've come to appreciate an opinion-driven approach to software design. Especially in the open source world, where you're writing code for others to use. I used to tell myself I'd need to write more code for more flexibility "if I want this package to grow popular".

I don't believe that anymore.

These days, I prefer one way of solving a problem, instead of offering several options. As an open source maintainer, I realise that not everyone might like the solutions I come up with as much as I do; but in the end, if the job gets done, if my code is reliable, clear and useful; there rarely are any complaints. So I started to prefer opinion-driven design when I realised that flexibility comes with a price that is often not worth paying.

I'm not the only one benefiting by the way. When users of my open source code only get one way of doing something, they don't have to be worried about micro-decisions that wouldn't affect the end result. And that, for me, is good software design: allowing programmers to focus on decisions that really matter and offer value to their projects and clients; instead of wasting time on unnecessary details.

Thanks for reading! This post is part of my "Dev Diaries" series where I write about my own and personal experiences as a developer. Would you like to read some more?

If you want to stay up to date about what's happening on this blog, you can follow me on Twitter or subscribe to my newsletter:

]]>
2021-06-23T00:00:00+00:00
<![CDATA[ What about config builders? ]]> https://www.stitcher.io/blog/what-about-config-builders I've been tinkering with a hobby project lately: a small framework to get more familiar with PHP 8, and try out some random ideas floating in my head. It's nothing too serious, but it's a fun exercise.

Most of these ideas are born from my daily experience with Laravel, and more specifically from the little annoyances I have with it. Now, don't get me wrong: I think Laravel is one of the best frameworks out there when it comes to modern PHP development and it's only natural that it has a quirk here and there, after a decade of development.

So this definitely isn't a Laravel-rant, rather it's just a thought experiment in dealing with one of those little annoyances.

So, Laravel has these PHP config files, right. Here's one example (this one is auth.php, if you're wondering):

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session", "token"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | If you have multiple user tables or models you may configure multiple
    | sources which represent each model / table. These sources may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | You may specify multiple password reset configurations if you have more
    | than one user table or model in the application and you want to have
    | separate password reset settings based on the specific user types.
    |
    | The expire time is the number of minutes that the reset token should be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Password Confirmation Timeout
    |--------------------------------------------------------------------------
    |
    | Here you may define the amount of seconds before a password confirmation
    | times out and the user is prompted to re-enter their password via the
    | confirmation screen. By default, the timeout lasts for three hours.
    |
    */

    'password_timeout' => 10800,

];

While I think PHP configuration files are far superior to, for example, YAML or XML configuration; there's one thing that annoys me quite a lot with it: there are no IDE insights in what kind of config values are available in this array, let alone which type of values they require.

To counteract this problem, the official Laravel config files have these inline doc blocks explaining each config entry. Most serious third party packages also provide such doc blocks.

With PHP 8 and named arguments though, there's a better solution available: config objects that know exactly what kind of data they'd need.

Here's what I'd imagine the auth.php would look like with it:

return AuthConfig::make()
    ->defaults(
        guard: 'web',
        passwords: 'users',
    )
    ->guards(
        web: GuardConfig::make()
            ->driver('session')
            ->provider('users'),
        api: GuardConfig::make()
            ->driver('token')
            ->provider('users')
            ->hash(false),
    )
    ->providers(
        users: AuthProviderConfig::make()
            ->driver('eloquent')
            ->model(User::class),
    )
    ->passwords(
        users: PasswordConfig::make()
            ->provider('users')
            ->table('password_resets')
            ->expire(60)
            ->throttle(60)
    )
    ->passwordTimeout(10800);

Thanks to named arguments and their support for variadic functions, we end up with a conciser syntax, while still having all documentation available to us: it's added as property types and doc blocks in these config objects, instead of being hard coded in the config files as text.

To me that's the most important value: your IDE tells you what you need to do, instead of having to read documentation — inline or external:

The only thing needed for this to work is some kind of interface that requires these config builders, as I like to call them, to implement a toArray method. You could go one step further and turn things around by always using config objects instead of arrays, which would allow you to also make use of their built-in documentation when reading config, and not only when initializing it. That's a bit more of a aggressive change though.

Here's what a config builder implementation would look like:

class AuthConfig extends ConfigBuilder
{
    public function defaults(
        ?string $guard = null,
        ?string $password = null,
        // …
    ): self {
        $this->config['defaults']['guard'] = 
            $guard 
            ?? $this->config['defaults']['guard'] 
            ?? null;
            
        $this->config['defaults']['password'] = 
            $password 
            ?? $this->config['defaults']['password'] 
            ?? null;

        return $this;
    }

    public function guards(GuardConfig ...$guardConfigs): self
    {
        foreach ($guardConfigs as $name => $guardConfig) {
            $this->config['guards'][$name] = $guardConfig->toArray();
        }

        return $this;
    }

    // …

    public function passwordTimeout(int $timeout): self
    {
        $this->config['password_timeout'] = $timeout;
        
        return $this;
    }
}

Another improvement I can come up with is by using enums instead of string values. They will be natively available in PHP 8.1, but there are also alternatives out there for older PHP versions.

Let's just assume we're already running PHP 8.1, we could write parts of it like so:

->guards(
    web: GuardConfig::make()
        ->driver(Driver::Session)
        ->provider(Provider::Users),
    api: GuardConfig::make()
        ->driver(Driver::Token)
        ->provider(Provider::Users)
        ->hash(false),
)

These ideas aren't new, by the way. We've been using config objects in some of our packages at Spatie, but we always start from a simple array and convert it when booting the application. There's also PHP CS that uses the same approach. Also, Symfony just recently added support for a fluent interface to build their config files; great!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

There's one advantage to tinkering with your own framework: you're not constrained by backwards compatibility and legacy. I imagine it might not be trivial to properly support config builders in Laravel, at least not as the default approach. They also become much less useful if you cannot use named arguments, which require PHP 8.

But, who knows? Maybe something similar might get added in the future in Laravel? Or maybe some third-party packages start doing it on their own first. Anyway, I'm going to tinker some more with my custom framework, just for fun!

What's your opinion on config builders? Let me know your thoughts, via Twitter or by subscribing to my newsletter.

]]>
2021-06-18T00:00:00+00:00
<![CDATA[ A new major version of Laravel Event Sourcing ]]> https://www.stitcher.io/blog/a-new-major-version-of-laravel-event-sourcing Today, we released a new version of spatie/laravel-event-sourcing, version 5 is probably one of the largest releases since the beginning of the package, we've worked several months on it and have been testing it extensively already in our own projects.

Credit where credit is due, many new features were inspired by Axon,
a popular event sourcing framework in the Java world, and several people pitched in during the development process.

In this post, I'll walk you through all significant changes, but first I want to mention the course that we've built at Spatie over the last months about event sourcing. If you're working on an event sourced project or thinking about starting one, this course will be of great help. Check it out on https://event-sourcing-laravel.com/!

# Consistent event handling

If you've used previous versions of our package, you might have struggled with how event handlers were registered across classes. Aggregate roots required you to write applyEventName functions, while projectors and reactors had an explicit event mapping.

Whatever class you're writing will now register event handlers the same way: by looking at the type of the event. You don't need any more configuration or naming conventions anymore.

class CartAggregateRoot extends AggregateRoot
{
    // …
    
    public function onCartAdded(CartAdded $event): void
    {
        // Any `CartAdded` event will automatically be matched to this handler
    }
}
class CartProjector extends Projector
{
    public function onCartAdded(CartAdded $event): void
    {
        // The same goes for projectors and reactors.
    }
}

# Event Queries

Event queries are a new feature that allow you to easily query an event stream without building database projections. You can think of them as in-memory projections that are rebuilt every time you call them.

Here's an example of it in action:

class EarningsForProductAndPeriod extends EventQuery
{
    private int $totalPrice = 0;
    
    public function __construct(
        private Period $period,
        private Collection $products
    ) {
        EloquentStoredEvent::query()
            ->whereEvent(OrderCreated::class)
            ->whereDate('created_at', '>=', $this->period->getStart())
            ->whereDate('created_at', '<=', $this->period->getEnd())
            ->cursor()
            ->each(
                fn (EloquentStoredEvent $event) => $this->apply($event)
            );
    }

    protected function applyOrderCreated(OrderCreated $orderCreated): void 
    {
        $orderLines = collect($orderCreated->orderData->orderLineData);

        $totalPriceForOrder = $orderLines
            ->filter(function (OrderLineData $orderLineData) {
                return $this->products->first(
                    fn(Product $product) => $orderLineData->productEquals($product)
                ) !== null;
            })
            ->sum(
                fn(OrderLineData $orderLineData) => $orderLineData->totalPriceIncludingVat
            );

        $this->totalPrice += $totalPriceForOrder;
    }
}

Note that these examples come from the Event Sourcing in Laravel book.

# Aggregate Partials

Aggregate partials allow you to split large aggregate roots into separate classes, while still keeping everything contained within the same aggregate. Partials can record and apply events just like an aggregate root, and can share state between them and their associated aggregate root.

Here's an example of an aggregate partial that handles everything related to item management within a shopping cart:

class CartItems extends AggregatePartial
{
    // …
    
    public function addItem(
        string $cartItemUuid, 
        Product $product, 
        int $amount
    ): self {
        $this->recordThat(new CartItemAdded(
            cartItemUuid: $cartItemUuid,
            productUuid: $product->uuid,
            amount: $amount,
        ));

        return $this;
    }

    protected function applyCartItemAdded(
        CartItemAdded $cartItemAdded
    ): void {
        $this->cartItems[$cartItemAdded->cartItemUuid] = null;
    }
}

And this is how the cart aggregate root would use it:

class CartAggregateRoot extends AggregateRoot
{
    protected CartItems $cartItems;

    public function __construct()
    {
        $this->cartItems = new CartItems($this);
    }

    public function addItem(
        string $cartItemUuid,
        Product $product,
        int $amount
    ): self {
        if (! $this->state->changesAllowed()) {
            throw new CartCannotBeChanged();
        }

        $this->cartItems->addItem($cartItemUuid, $product, $amount);

        return $this;
    }

Aggregate partials come with the same testing capabilities as aggregate roots, and are a useful way of keeping aggregate-related code maintainable.

# Command bus

We've added a command bus that can automatically map commands to handlers on aggregate roots:

namespace Spatie\Shop\Cart\Commands;

use Spatie\Shop\Support\EventSourcing\Attributes\AggregateUuid;
use Spatie\Shop\Support\EventSourcing\Attributes\HandledBy;
use Spatie\Shop\Support\EventSourcing\Commands\Command;

#[HandledBy(CartAggregateRoot::class)]
class AddCartItem implements Command
{
    public function __construct(
        #[AggregateUuid] public string $cartUuid,
        public string $cartItemUuid,
        public Product $product,
        public int $amount,
    ) {
    }
}

Whenever this command is dispatched, it will automatically be captured and handled by the associated aggregate root. It even works with aggregate partials:

class CartItems extends AggregatePartial
{
    // …
    
    public function addItem(AddCartItem $addCartItem): self
    {
        // …
    }
    
    public function removeItem(RemoveCartItem $removeCartItem): self
    {
        // …
    }
}    

Besides these new features, there are also some quality-of-life changes across the board:


All in all, I'm very exited for this new release. All the new features are also used in our real-life projects, so we know from experience how useful they are in complex applications. Of course, a blog post can't discuss all the details and the thought process behind this new version, so make sure to read the book if you want in-depth knowledge about all of these features, and more.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-06-15T00:00:00+00:00
<![CDATA[ Why we need multi-line short closures in PHP ]]> https://www.stitcher.io/blog/why-we-need-multi-line-short-closures-in-php PHP 8.1 is already taking shape quite well, yet there's one feature I'd love to see added, that's still being discussed: multi-line short closures. The RFC calls them "Auto-capturing multi-statement closures" but I hope you don't mind me using the somewhat shorter name.

If you're on an actively supported PHP version, you already know about short closures — a.k.a. "arrow functions". And, most importantly, one of their biggest drawbacks: they only support one-line expressions, which are also used as the return value.

The multi-line short closures RFC by Nuno and Larry aims to solve that problem, in — what I think is — an elegant way.

Some people are skeptical about this RFC though, and I want to address their arguments and share why I think it's still a worthwhile addition. Though first, I'll show you what the RFC is about. Keep in mind this is a proposal, and not added to PHP yet!

A quick refresher, this is what arrow functions in PHP look like today:

$getTitle = fn (Post $post) => $post->title;

And this is what the RFC proposes:

$date = now();

$getSlug = fn (Post $post) {
    $slug = Str::slug($post->title);
    
    return $date . $slug;
}

There are two important things to note about multi-line short closures:

  • they have access to the variables in the upper scope without needing use; and
  • they don't automatically return the last expression, which makes sense since there can be several expressions in a multi-line closure.

You might have already noticed it, but the RFC introduces an elegant kind of symmetry in how you can create closures:

  • on the one hand there's function and fn, the first one doesn't auto-capture variables from the outside, the second one does; and
  • on the other hand there's { … } or =>, using curly brackets allow you to write multiple lines, while => only accepts one expression, but also returns it.

Because of this symmetry, all of the following code samples are possible and all of them behaving a little differently.

Here's a closure that doesn't auto-capture outer scope, with multiple lines:

$date = now();

$getSlug = function (Post $post) use ($date) {
    $slug = Str::slug($post->title);
    
    return $date . $slug;
}

Next, a closure that does capture outer scope and immediately returns the result:

$date = now();

$getSlug = fn (Post $post) => $date . Str::slug($post->title);

And finally — proposed by the RFC — a closure that does capture outer scope, but still allows multiple lines:

$date = now();

$getSlug = fn (Post $post) {
    $slug = Str::slug($post->title);
    
    return $date . $slug;
}

If you're counting, you know that one option is still missing: a closure that doesn't auto-capture outer scope and immediately returns a value:

$date = now();

$getSlug = function (Post $post) use ($date) 
    => $date . Str::slug($post->title);

The RFC lists this last one as a possible future addition, but doesn't cover it right now.


With the background info out of the way, let's look at some counter arguments as to why some people don't like this change.

# Auto-capturing outer scope can lead to bugs

Some people voice their fear about auto-capturing variables from the outer scope, especially that it can lead to unclear code in the long run and thus, bugs.

I've got a few arguments against that fear.

First, auto-capturing is already supported by PHP in the current short closures, there's nothing about this RFC that changes that. The arrow function RFC passed with 51 votes for, and 8 against after a thorough discussion, and has been used extensively in lots of projects since — take a look at some popular OSS frameworks if you want some examples. Clear signs that this particular behaviour is wanted by the majority of people, whether you're afraid of it or not.

Nikita also shares this opinion:

I'm generally in favor of supporting auto-capture for multi-line closures. I think that extensive experience in other programming languages has shown that auto-capture does not hinder readability of code, and I don't see any particular reason why the effects in PHP would be different. — https://externals.io/message/113740#114239

Auto-capturing outer scope might not be your preferred way of coding, but that doesn't mean the idea should be dismissed, as there are many people who do prefer this style.

Fear should never be the driving force in a programming language's design, we should look at facts instead.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# By-value or by-reference passing

I believe the RFC gets it right when it says that outer-scope variables are always captured by-value, and not by-reference. This means that you won't be able to change the value of an outer-scope variable from within your closure — a good thing.

People might argue that this will cause confusion, because what happens if you do want to change the outer-scope variable? We could discuss about whether this would ever be a good idea or not, but it doesn't even matter since PHP already has the answer — remember that symmetry I talked about earlier?

If you want by-reference passing, you'll need to specifically say which variables you want access to — which is exactly what function allows:

$date = now();

$getSlug = function (Post $post) use (&$date) {
    $date = now()->addDay();
    
    // Please don't do this…
}

I'd argue that this RFC creates clear boundaries between what's possible with function and fn. It doesn't cause confusion; on the contrary: it creates consistency and clarity within PHP's syntax.

# There's little space to be gained

Some people argue that there's no need for adding multi-line short closures because there's little to be gained when it comes to the amount of characters you're writing. That might be true if you're not relying on outer-scope values, but to be honest it's not about how many characters you write.

This RFC makes the language's syntax more consistent, and a consistent language allows for easier programming.

When writing code, I don't want to be bothered with having to change fn to function when refactoring a closure that suddenly does need to be written on two lines instead of the prior one.

It might seem like such a small detail, but it's those details that impact our day-by-day developer life. And isn't that what a maturing language should be about? Look at features like property promotion, named arguments, enums, attributes, the match operator, etc. You could argue that none of those feature are "necessary" to write working PHP code, but still they do improve my day-by-day life significantly — and I hope yours too.

# Holding on to the past

Finally, some people might find it difficult to deal with change, and you really need to ask yourself that question if you're voting on RFCs. Yes, you might not see a need for a given feature but you're not only voting for yourself, you have a responsibility to the PHP community; there's more to this than just your projects and your team.

Do you know why closures right now don't auto-import variables from the outer scope? You might have gotten used to it, but do you know why they were designed this way 12 years ago? Larry did some digging in the mailing list archives, and discovered there were three reasons why use was introduced in the first place:

  • there were performance concerns if variables of the outer-scope were auto-captured — concerns that are no longer relevant today;
  • it was used to avoid surprise by-reference value passing — which also isn't a problem since we're always using by-value passing; and
  • it allowed users to explicitly capture variables by-value or by-reference, which is now cleanly solved because of the distinction between function and fn.

You might have gotten used to closures not auto-importing variables for the past decade, but keep in mind this behaviour was only added as a necessity back in the day. All arguments for only using explicit capture have been nullified by time, a great sign that PHP is maturing even more.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

With all of that being said, I'm looking forward to Nuno and Larry opening the vote on their RFC. PHP 8.1's feature freeze is planned for the 20th of July, 2021; so there's still some time to finalize the details. If you're voting on RFCs, I truly hope you can see the big picture, as this is one of the RFCs that will have a significant impact on many people's developer life.

]]>
2021-06-03T00:00:00+00:00
<![CDATA[ Running PHP code in parallel, the easy way ]]> https://www.stitcher.io/blog/parallel-php Less is more. You've heard that before, right? Keep it in mind, I'm going to show you something.

There are a few good and robust solutions to run PHP code in parallel already; and yet, we've made our own implementation. I want to explain why. First, let's set the scene: I want to run PHP code in parallel. Here are some of my use cases:

  • to test race conditions for our command-bus, when running PHPUnit tests;
  • to do a bunch of HTTP requests in parallel; and also
  • to generate this blog faster by allowing my static generator to work on multiple processes.

My use cases have two requirements in common: run a arbitrary amount of functions in parallel, and wait until all of them are finished. Let's look at the solutions available today.

AmpPHP has a package called parallel-functions. It looks like this:

use Amp\Promise;
use function Amp\ParallelFunctions\parallelMap;

$values = Promise\wait(
    parallelMap([1, 2, 3], function ($time) {
        \sleep($time);
    
        return $time * $time;
    })
);

For my use cases, I've got a few problems with this implementation:

  • it uses promises, which are very good for more complex async work, but are only overhead for me;
  • Amp's API and its use of functions feels very clunky to me, but that's rather subjective, I realise that; and finally
  • if you need a framework in your child processes, you'll need to boot it manually.

Moving on to ReactPHP, they don't have an out-of-the-box solution like Amp, but they do offer the low-level components:

$loop = React\EventLoop\Factory::create();

$process = new React\ChildProcess\Process('php child-process.php');

$process->start($loop);

$process->stdout->on('data', function ($chunk) {
    echo $chunk;
});

$process->on('exit', function($exitCode, $termSignal) {
    echo 'Process exited with code ' . $exitCode . PHP_EOL;
});

$loop->run();

A few caveats with this implementation:

  • ReactPHP always requires you to manually create an event loop which, again, is overhead for me;
  • they also work with promises; and finally
  • they only offer the bare infrastructure to run processes in parallel, there's lots of manual setup work.

Finally, there's Guzzle with its concurrent requests:

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$client = new Client(['base_uri' => 'http://httpbin.org/']);

$promises = [
    'image' => $client->getAsync('/image'),
    'png'   => $client->getAsync('/image/png'),
    'jpeg'  => $client->getAsync('/image/jpeg'),
    'webp'  => $client->getAsync('/image/webp')
];

$responses = Promise\Utils::unwrap($promises);
  • Again, there's the overhead of promises; but more importantly
  • Guzzle only works with HTTP requests, which only solves part of my problem.

Of all of the above, Amp's approach would have my preference, were it not that it still has quite a lot of overhead for my simple use cases. Honestly, all I wanted to do was to run some functions in parallel and wait until all of them are finished. I don't want to be bothered by looking up documentation about the particular API a framework is using. Did I have to import a function here? How to unwrap promises? How to wait for everything to finish?

All of the above examples are great solutions for the 10% cases that require people to have lots of control, but what about the 90% of cases where you just want to do one thing as simply as possible?

Less is more. We often forget that in software design. We overcomplicate our solution "just in case" someone might need it, and forget about the 90% use case. It leads to frustration because developers have to look up documentation in order to understand how to use a framework, or they have to write lots of boilerplate to get their generic case to work.

So with all of that being said, you now know why I decided to make another library that has one simple goal: run functions in parallel and wait for the result. Here's what it looks like:

$rssFeeds = Fork::new()
    ->run(
        fn () => file_get_contents('https://stitcher.io/rss'),
        fn () => file_get_contents('https://freek.dev/rss'),
        fn () => file_get_contents('https://spatie.be/rss'),
    );

And that's it. It does one job, and does it well. And don't be mistaken: it's not because there's a simple API that it only offers simple functionality! Let me share a few more examples.

Parallel functions are able to return anything, including objects:

$dates = Fork::new()
    ->run(
        fn () => new DateTime('2021-01-01'),
        fn () => new DateTime('2021-01-02'),
    );

They use process forks instead of fresh processes, meaning you don't need to manually boot your framework in every child process:

[$users, $posts, $news] = Fork::new()
    ->run(
        fn () => User::all(),
        fn () => Post::all(),
        fn () => News::all(),
    );

They allow before and after bindings, just in case you need to do a little more setup work. In the previous example, Laravel actually needs to reconnect to the database in the child processes before it would work:

[$users, $posts, $news] = Fork::new()
    ->before(fn () => DB::connection('mysql')->reconnect())
    ->run(
        fn () => User::all(),
        fn () => Post::all(),
        fn () => News::all(),
    );

And finally, before and after bindings can be run both in the child process and parent process; and also notice how individual function output can be passed as a parameter to these after callbacks:

Fork::new()
    ->after(
        child: fn () => DB::connection('mysql')->close(),
        parent: fn (int $amountOfPages) => 
            $this->progressBar->advance($amountOfPages),
    )
    ->run(
        fn () => Pages::generate('1-20'),
        fn () => Pages::generate('21-40'),
        fn () => Pages::generate('41-60'),
    );

There are of course a few things this package doesn't do:

  • there's no pool managing the amount of concurrent processes, you're in charge if you need to;
  • there are no promises;
  • pcntl doesn't work on Windows and doesn't run in web requests;
  • there's no behind the scenes exception handling, if a child fails it'll throw an exception and stop the process flow.

In other words: it's the perfect solution for the 90% case where you just want to run some functions in parallel and be done with it. If you need anything more than that, then the solutions listed above are a great start. There's also another package of ours called spatie/async that doesn't work with promises but does offer pool configuration and extensive exception handling.

If you want to know more or want to try the package yourself, you can check it out on GitHub: spatie/fork.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

Less is more. That's one of my core principles when coding. I prefer code that forces me to do something one way but always works, instead of a highly configurable framework that makes me wonder how to use it every time I look at it. I feel that many developers often get lost in a maze of high configurability and extensibility and forget their original end goal by doing so.

I hope this package can be of help for that group of people who fall in the 90% category.

]]>
2021-04-28T00:00:00+00:00
<![CDATA[ PHP in 2021 ]]> https://www.stitcher.io/blog/php-in-2021 Once a year, I look back at the recent developments in the PHP world, and also look forward to what's to come. And just like in 2020 and 2019, we're at it again!

Like I said time and time again: PHP isn't the same language it was ten years ago, and we're very thankful for that. It's a fast and reliable language, used to build large applications at scale. So let's discuss some of the most notable changes of PHP in the last year, to the language and the community.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# PHP 8 and beyond

The new major version, PHP 8, arrived late last year. I've written an extensive amount on the topic, and I won't copy/paste all of that here. As always, performance is only improving, as shown by the benchmarks done by Kinsta.

There's also the JIT that does seem to improve performance of some projects here and there, as well as preloading that has an overall positive impact if you're not using shared hosting.

I think that features like attributes (a.k.a. "annotations"), named arguments and promoted properties also deserve a mention, as they have definitely contributed towards PHP 8 being such a great release.

Meanwhile, the core team is already working on the next version, PHP 8.1, which will be released somewhere by the end of 2021. For now, the most significant features are enums and fibers, I'll mention both of them again later in this post.

Year after year, the core team succeeds in bringing a new stable release to the community, packed with a bunch of features and quality-of-life improvements. The upgrade path also isn't all that difficult anymore. I upgraded some of my own projects from PHP 7.4 to PHP 8, and it took only an hour or so per project. There really isn't any good reason to stay behind!

# PHP's type system

There's actually some very exciting news when it comes to types: enums will be added in PHP 8.1. On top of that we've also seen some of the maintainers of static analysis tools contributing to PHP's source code by landing their first RFC. This one adds the never type, a useful addition for static analysis.

Speaking of static analysis tools, PhpStorm has added built-in support for Psalm and PhpStan, which is a great step towards broader adaptation; our favourite IDE now also supports generics as docblocks, which makes them a lot more useful.

Still no support for first-class generics, unfortunately. There are some major road blockers, especially since we're still dealing with a dynamically typed language. Nikita has outlined the problems here. Personally, my approach would be the easy way out: only support runtime-erased generics and rely on static analysis. This requires more than a technical solution though, it also requires a mind shift in the PHP community as a whole. Maybe one day it becomes a viable option, but not as of yet.

# Async PHP

There was some big news recently: PHP is getting coroutines — aka. green threads — in PHP 8.1! Although, fibers — that's what they are called — might not be as big a game-changer as you might think.

Even though fibers themselves might only be a small cog in what is the large async machine, the RFC has spiked interest in the async community again, which we can only be happy about. Async frameworks like Amphp and ReactPHP are growing in popularity, and recently Laravel announced built-in support for Swoole.

# The community

I can't go on without mentioning Composer, the de-facto standard package manager. It has had a new major release in October, 2020: Composer 2.0. This version comes with several UX improvements, but most importantly sees extreme performance improvements, sometimes even tripling its speed on clean installs.

Speaking of composer, I like to measure the current state of PHP's ecosystem by looking at the available packages over time. Last year I spoke about ±25 million downloads a day, today that number has more than doubled and we're looking at ±60 million daily downloads.

Finally, take a look at this graph, listing the amount of packages and versions over time. It can also be found on their website. You can clearly see a healthy ecosystem growing, and there's no end in sight.

# The language

Let's end with a reminder of everything that's been added to PHP these recent years. If you haven't kept up with its development, you really want to check this list out. I think it shows the growth of the community and core development team within recent years, and I'm confident there's more to come.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

All of that to say: PHP is alive and doing very well. Every year I feel happier with the direction the language is going, and am looking forward to using it for many years to come!

If you're excited as well, you probably want to subscribe to my newsletter to stay up-to-date about PHP's development, as well as follow me on Twitter. Let me know your thoughts via Twitter or email, and share this post with your audience if you found it useful, thanks!

]]>
2021-04-15T00:00:00+00:00
<![CDATA[ Fibers with a grain of salt ]]> https://www.stitcher.io/blog/fibers-with-a-grain-of-salt

Do you prefer to listen?

I've created an audio version of this post. You can listen on YouTube or by subscribing to my podcast feed on Apple Podcasts, Stitcher or Spotify

So I was going to write an in-depth blogpost about using fibers in PHP 8.1. We were going to start with a basic example to explain them from the ground up. The idea was to send async HTTP requests and process them in parallel using fibers.

But playing around with them, I learned that the RFC wasn't kidding when it said "The Fiber API is not expected to be used directly in application-level code. Fibers provide a basic, low-level flow-control API to create higher-level abstractions that are then used in application code".

So instead of going down this path and make things way too complicated, we'll discuss what fibers are conceptually, why they are barely usable in application code, and how you can make use of async PHP after all.

First, a little bit of theory.


Imagine you want to send three HTTP requests and process their combined result. The synchronous way of doing so is by sending the first one, waiting for the response, then sending the second one, waiting, etc.

Let's represent such a program flow with as easy a chart as possible. You need to read this chart from the top down, and time progresses the further down you go. Each colour represents one HTTP request. The coloured pieces of each request represent PHP code actually running, where the CPU on your server is doing work, the transparent blocks represent waiting times: the request needs to be sent over the wire, the other server needs to process it and send it back. It's only when the response arrives that we can work again.

This is a synchronous execution flow: send, wait, process, repeat.

In the world of parallel processing, we send the request but don't wait. Then we send the next request, followed by another. Only then do we wait for all requests. And while waiting we periodically check whether one of our requests is already finished. If that's the case we can process it immediately.

You can see how such an approach reduces execution time because we're using the waiting time more optimally.

Fibers are a new mechanism in PHP 8.1 that allow you to manage those parallel execution paths more efficiently. It was already possible by using generators and yield, but fibers are a significant improvement, since they are specifically designed for this use case.

You would create one fiber for each request, and pause the fiber after the request is sent. After you've created all three fibers, you'd loop over them, and resume them one by one. By doing so, the fiber checks whether the request is already finished, if not it pauses again, otherwise it can process the response and eventually finish.

You see, fibers are a mechanism to start, pause and resume the execution flow of an isolated part of your program. Fibers are also called "green threads": threads that actually live in the same process. Those threads aren't managed by the operating system, but rather the runtime — the PHP runtime in our case. They are a cost efficient way of managing some forms of parallel programming.

But note how they don't add anything truly asynchronous: all fibers live in the same PHP process, and only one can run at a time. It's the main process that loops over them and checks them while waiting, and that loop is often called the "event loop".

The difficult part about parallelism isn't about how you loop over fibers or generators or whatever mechanism you want to use; it's about being able to start an operation, hand it over to an external service and only check the result when you want to, in a non-blocking way.

See, in the previous examples, we assumed that we could just send off a request and check its response later when we want to, but that actually isn't as easy as it sounds.

That's right: most of PHP's functions that deal with I/O don't have this non-blocking functionality built-in. In fact, there's only a handful of functions that do, and using them is quite cumbersome.

There's the example of sockets, which can be set to be non-blocking, like so:

[$read, $write] = stream_socket_pair(
    STREAM_PF_UNIX,
    STREAM_SOCK_STREAM,
    STREAM_IPPROTO_IP
);
 
stream_set_blocking($read, false);
stream_set_blocking($write, false);

By using stream_socket_pair(), two sockets are created that can be used for bidirectional communication. And as you can see, they can be set to be non-blocking using stream_set_blocking().

Say we'd want to implement our example, sending three requests. We could use sockets to do so, but we'd need to implement the HTTP protocol ourselves on top of it. That's exactly what nox7 did, a user who shared a small proof of concept on Reddit to show how to send HTTP GET requests using fibers and sockets. Do you really want to be concerned with doing so in your application code?

The answer, for me at least, is "no". Which is exactly what the RFC warned about; I'm not mad about that. Instead, we're encouraged to use one of the existing async frameworks: Amp or ReactPHP.

With ReactPHP, for example, we could write something like this:

$loop = React\EventLoop\Factory::create();

$browser = new Clue\React\Buzz\Browser($loop);

$promises = [
    $browser->get('https://example.com/1'),
    $browser->get('https://example.com/2'),
    $browser->get('https://example.com/3'),
];

$responses = Block\awaitAll($promises, $loop);

That's a better example compared to manually creating socket connections. And that's what the RFC means: application developers shouldn't need to worry about fibers, it's an implementation detail for frameworks like Amp or ReactPHP.

That brings us to the question though: what are the benefits of fibers compared to what we already could do with generators? Well the RFC explains it this way:

Unlike stack-less Generators, each Fiber has its own call stack, allowing them to be paused within deeply nested function calls. A function declaring an interruption point (i.e., calling Fiber::suspend()) need not change its return type, unlike a function using yield which must return a Generator instance.

Fibers can be suspended in any function call, including those called from within the PHP VM, such as functions provided to array_map or methods called by foreach on an Iterator object.

It's clear that fibers are a significant improvement, both syntax-wise and in flexibility. But they are nothing yet compared to, for example, Go, with its "goroutines".

There's still lots of functionality missing for async PHP to become mainstream without the overhead of frameworks, and fibers are a good step in the right direction, but we're not there yet.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

So there's that. There actually isn't much to say about fibers if you're not a maintainer of either Amp, ReactPHP or a smaller async PHP framework. Maybe even more frameworks or libraries will start incorporating them?

Meanwhile, there's also Swoole — a PHP extension that actually modifies several core functions to be non-blocking. Swoole itself is a Chinese project and often not very well documented when it comes to English, but recently Laravel announced first party-integration with it. Maybe this is the better strategy when it comes to moving PHP towards a more async model: optionally integrate Swoole or other extensions with frameworks like Laravel and Symfony?

It'll be interesting to see what the future will bring!

]]>
2021-04-12T00:00:00+00:00
<![CDATA[ Starting with event sourcing ]]> https://www.stitcher.io/blog/what-event-sourcing-is-not-about For many developers, event sourcing is a magical beast that's used to build extremely complex, often distributed projects. And there's good reason for that: event sourcing is a pattern that forces code to be built in a way that fits those complex projects exceptionally well.

Words like "modularized", "distributed", "scalable" and "versatile" come to mind to describe it; characteristics you can't do without if you're building applications at scale. Think about a popular webshop or a bank, handling maybe thousands if not millions of transactions per second. Those are the kinds of projects event sourcing most often is associated with.

And thus, event sourcing is rarely every used in smaller projects, the ones many developers deal with, the ones many of my regular blog readers work on.

I think the reason for this, is because of a fundamental mistake of what we think event sourcing is.

When I discuss event sourcing, people often assume that it comes with significant overhead, and that that overhead isn't justified in smaller projects. What they say is that there's some kind of pivot point, determined by the scope of the project, where event sourcing actually reduces costs, while in smaller projects it would introduce a cost overhead.

Something like this:

And, of course, this is an oversimplification, but it visualizes the argument very well: we should only use event sourcing in projects where we're sure it'll be worth it.

The problem with this statement is that it doesn't talk about just event sourcing, it talks about event sourcing with all its associated patterns as well: aggregates, sagas, snapshots, serialization, versioning, commands, …

Which is why I'd say our graph should look something more like this:

But does it even make sense to use event sourcing without the patterns that build on top it? There's a good reason why those patterns exist. That are the questions I want to answer today.


Here's Martin Fowler's vision on what event sourcing is:

"We can query an application's state to find out the current state of the world, and this answers many questions. However there are times when we don't just want to see where we are, we also want to know how we got there.

Event Sourcing ensures that all changes to application state are stored as a sequence of events. Not just can we query these events, we can also use the event log to reconstruct past states, and as a foundation to automatically adjust the state to cope with retroactive changes.

The fundamental idea of Event Sourcing is that of ensuring every change to the state of an application is captured in an event object, and that these event objects are themselves stored in the sequence they were applied for the same lifetime as the application state itself."

In other words: event sourcing is about storing changes, instead of their result. It's those changes that make up the final state of a project.

With event sourcing, the question of "is a cart checked out" should be answered by looking at the events that happened related to that cart, and not by looking at a cart's status.

That sounds like overhead indeed, so what are the benefits of such an approach? Fowler lists three:

  • Complete Rebuild: the application's state can be thrown away and rebuilt only by looking at events. This gives lots of flexibility when you know changes to the program flow or data structure will happen in the future — I'll give an example of this later in this post, don't worry if it sounds a little abstract for now.
  • Temporal Query: you can actually query events themselves to see what happened in the past. There's not just the end result of what happened, there's also the log of events themselves.
  • Event Replay: if you want to, you can make changes to the event log to correct mistakes, and replay events from that point on to rebuild a correct application state.

There's one important thing missing in Fowler's list though: events model time extremely well. In fact, they are much closer to how we humans perceive the world than CRUD is.

Think of some kind of process of your daily life. It could be your morning routine, it could be you doing groceries, maybe you attended a class or had a meeting at work; anything goes, as long as there were several steps involved.

Now try to explain that process in as much detail as possible. I'll take the morning routine as an example:

  • I got up at 5 AM
  • Brushed my teeth — dental hygiene is important — and got dressed
  • Went downstairs to make a coffee
  • Went to my home office (with my coffee)
  • Read up on mails
  • Started writing this post

Thinking with events comes natural to us, much more natural than having a table containing the state of what's happening right now, as with a CRUD approach.

If there's already a "flow of time" to be discovered in something as mundane as my morning routine, what about any kind of process for our client projects? Making bookings, sending invoices, managing inventories, you name it; "time" is very often a crucial aspect, and CRUD isn't all that good in managing it since it only shows the current state.


I'm going to give you an example of how extremely simple event sourcing can be, without the need for any kind of framework or infrastructure, and where there's no overhead compared to CRUD, in fact, there's less.

I have a blog, this one; and I use Google Analytics to anonymously track visitors, page views, etc. Of course I know Google isn't the most privacy-focussed so I was tinkering with alternatives. One day I wondered if, instead of relying on client side tracking, I could just rely on my server logs to determine how many pages and which were visited.

So, I wrote a little script that monitors my nginx access log; it filters out traffic like bots, crawlers etc, and stores each visit as a line in a database table. Such a visit has some data associated with it: the URL, the timestamp, the user agent, etc.

And that's it.

Oh were you expecting more? Well, I did end up writing a little more code to make my life easier, but in essence, what we have here already is event sourcing: I keep a chronological log of everything that happened in my application and I can use SQL queries to aggregate the data, for example, to show visits per day.

Now, of course, with millions of visits over time, running raw SQL queries can become tedious, so I added one pattern that builds on event sourcing: projections; also known as the "read model" in CQRS.

Every time I store a visit in the table, I also dispatch it as an event. There are several subscribers that handle them, for example there's a subscriber that groups visits per day, it keeps track of them in a table with two columns: day and count. It's literally only a few lines of code:

class VisitsPerDay
{
    public function __invoke(PageVisited $event): void
    {
        DB::insert(
            'INSERT INTO visits_per_day (`day`, `count`) 
            VALUES (?, ?) 
            ON DUPLICATE KEY UPDATE `count` = `count` + 1',
            [
                $event->date->format('Y-m-d'), 
                1,
            ]
        );
    }
}

Do you want visits per month? Per URL? I'll just make new subscribers. Here's the kicker though: I can add them after my application is deployed, and replay all previously stored events on them. So even when I make a change to my projectors or add new ones, I can always replay them from the point I started storing events, and not just from when I deployed a new feature.

This was especially useful in the beginning: there was lots of data coming in that were bots or traffic that weren't real users. I let the script run for a few days, observed the results, added some extra filtering, threw away all projection data and simply replayed all visits again. This way I wouldn't need to start all over again every time I made a change to the data.

And can make a guess how long it took to set up this event sourced project? 2 hours, from start to a working production version. Of course I used a framework for a DBAL and an event bus, but nothing specifically event sourcing related. Over the next days I did some fine tuning, added some charts based on my projection tables etc; but the event sourcing setup was absolutely easy to build.

And so, here's the point I'm trying to make: the event sourcing mindset is extremely powerful in many kinds of projects. Not just the ones that require a team of 20 developers to work on for five years. There are many problems where "time" plays a significant role, and most of those problems can be solved using a very simple form of event sourcing, without any overhead at all.

In fact, a CRUD approach would have cost me way more time to build this analytics project. Every time I made a change I would have to wait a few days to ensure this change was effective with real life visits. Event sourcing allowed me, a single developer, to actually be much more productive; which is the opposite of what many people believe event sourcing can achieve.


Now, don't get me wrong. I'm not saying event sourcing will simplify complex domain problems. Complex projects will take lots of time and resources, with or without event sourcing. Event sourcing simplifies some problems within those projects, and makes others a little harder.

But remember I'm not trying to make any conclusions about the absolute cost of using event sourcing or not. I only want people to realise that event sourcing in itself doesn't have to be extremely complex, and might indeed be a better solution for some projects, even when they are relatively small ones.

Greg Young, the one who came up with the term "event sourcing" more than 10 years ago; said that if your starting point for event sourcing is a framework, you're doing it wrong. It's a state of mind first, without needing any infrastructure. I actually hope that more developers can see it this way, that they remove the mental layer of complexity at first, and only add it back when actually needed. You can start with event sourcing today, without any special framework, and it might actually improve your workflow.


If you've made it this far, I assume you're intrigued by what I wrote — or very angry because of it, that's fine too. If you want to stay up-to-date about my content, consider subscribing to my newsletter, I occasionally send a mail about stuff I'm working on.

Finally, if you're interested in more, you can check out the footnotes!

]]>
2021-04-09T00:00:00+00:00
<![CDATA[ Thoughts on Event Sourcing ]]> https://www.stitcher.io/blog/thoughts-on-event-sourcing I was flattered being asked to write a guest post for Event Store — the company originally founded by Greg Young, who pioneered event sourcing more than ten years ago.

I wrote about my experience with Event Sourcing, and how it's a shift in "mental framework" for all of us who were taught proper MVC and CRUD to build web applications with.

I hope you like it: https://www.eventstore.com/blog/php-and-event-sourcing.

If you want to listen to the audio version, you can head over here: https://www.youtube.com/watch?v=LXDnjtgfl2k.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-04-08T00:00:00+00:00
<![CDATA[ Honesty ]]> https://www.stitcher.io/blog/honesty

You can listen to this post

If you prefer listening over reading, you can listen to this post on YouTube or by subscribing to my podcast feed on Apple Podcasts, Stitcher or Spotify

Do you sometimes lie?

Perhaps that's a too confrontational question. Do you sometimes bend the truth a little for the benefit of others or yourself? Do you still consider it a lie if all parties involved benefit from it?

Studies show that all humans lie to a certain extent. It's a natural part of how we interact with each other. Still there's a category of lies that's not accepted by society: lies that intentionally deceive others for your own benefit, sometimes even causing harm.

This is a story about those kinds of lies. Not the ones you and I tell, but the ones people close to us might. The ones that, when witnessing them makes you feel uncomfortable, makes you want to step in and tell the truth. But you don't, because if you do, it might cause you harm instead.

I was a junior developer, straight out of school. I was working at a company that made websites for small to medium sized businesses. Think your average marketing site with some added complexity like CRM or lead management, stuff like that.

I'd been working with a team of developers on one of the larger projects for half a year, maybe eight months. The project was already in production by this time, but there still was lots of new functionality to build.

One day, the client makes a somewhat angry call to my project manager — the one who's directly above me, who I work with every day. They tell her there's a bug in a recently deployed feature and they ask her to get it fixed immediately. My project manager tells them she'll review the matter internally and get back to them within the hour.

Fast forward an hour; I can see her nervously picking up the phone. By the way, we're in an open office, so everyone's able to hear whatever others say.

She tells our client that the colleague who built that feature is sick for the rest of the week but we'll put another developer on the issue. It might take a bit longer and probably won't get done until tomorrow, maybe even the day after that.

I gave a puzzled look to the guy sitting next to me, that's the colleague our PM is talking about and he's just fine, not sick at all.


Let me paint you the broader picture for a moment. The relation with this client, remember: our largest up until then; it had been a little… tense. Ever since the application was deployed to production — or maybe ever since it became clear that the project estimates weren't as accurate as the client had hoped; they were noticeably frustrated.

We did those estimates together as a team, with our PM and boss. The boss was the one who actually made the sales pitch and had contact with the client before the project started.

We roughly estimated — if memory serves me right — that we'd need a year to deliver the project. Of course, it's a rough estimate on this timescale, no one is able to accurately estimate a project this size. So naturally we told our boss that we'd need to consider enough margins.

That answer wasn't satisfying though. Our boss knew the expectations of the client, they were half of what we proposed. They would surely go with another party if we kept to this estimate. So our boss promised what we, developers and project manager said was impossible: he cut the estimate in half, and gave it to the client. Same features, just half the available time.

We knew from day one this wasn't anywhere near achievable. But "we'll see", our boss said when we voiced our concerns. "Right now it's most important to win this pitch, we'll worry about the practical details later."

Naturally, our client wasn't happy when we told them it would take a little longer than "anticipated" to get an initial version running on production. I'd say we did a pretty good job after all: only 2 months over time instead of the 6 we expected.

But you can imagine the toll these things take on the team. There was a constant atmosphere of stress, and we — as a company — were piling lie upon lie upon lie.


I talked with our PM in private after that phone call, actually I stumbled upon her crying in the hallway. After the client initially called, she went to discuss the situation with our boss.

The colleague in question was currently working on another project (which was also under-estimated by the way) and it would have a significant impact if he'd need to spend an extra day back on the other project again. The boss told our PM to simply say the colleague is out sick, and that it would take more time than strictly required to fix the issue because of this. In his mind it was a win: the colleague can continue working on the other project, we get a few more days to fix the issue and we could invoice more work than actually needed — making up for the losses we made because of our wrong estimation in the beginning.

This situation, together with other reasons, is why I quit that job shortly after. The PM handed in her resignation as well, a month after I left.

These days, I'm fortunate to have found a workplace that does value honesty between them and their clients. I now realise this is not the case for every company. In the end, that company I used to work for did take a turn for the better; I believe they realised there's little to no value in relationships based on lies. Even though a lie might serve you right now, it's never a long lasting solution.

Thanks for reading! This post is part of my "Dev Diaries" series where I write about my own and personal experiences as a developer. Would you like to read some more?

If you want to stay up to date about what's happening on this blog, you can follow me on Twitter or subscribe to my newsletter:

]]>
2021-03-31T00:00:00+00:00
<![CDATA[ Don't write your own framework ]]> https://www.stitcher.io/blog/dont-write-your-own-framework

You can listen to this post

If you prefer listening over reading, you can listen to this post on YouTube or by subscribing to my podcast feed on Apple Podcasts, Stitcher or Spotify

We were sitting with 5 or 6 backend developers around the large meeting table. It was 10 in the morning on a Monday, and we were all silently working on our laptops. There was a hasty atmosphere, and everyone tried to concentrate on the task ahead.

Less than 2 hours before, I walked into the office, not yet aware of any harm. I was immediately called to the meeting room at the back, there was no time to sit at my desk. Still I quickly grabbed a coffee, and went to the back where a few other colleagues already gathered.

With them was our boss, a nice guy; there wasn't any "upper management" culture or anything, we were just colleagues. The other people in the room already knew what was going on, so he explained to me personally.

There was a bug, in our in-house framework.

"It sure isn't the first one" — I remember thinking.

At this point we'd used this custom-built framework for several years; 200 websites were affected, more or less.

The bug was a stupid mistake — after all, which bug isn't?

Our framework router would take a URL and filter out repeated slashes, so //admin would become /admin. This is, I believe, part of some HTTP spec; at least I was told so, I never double checked. The problem however, was in the authorisation layer: /admin was a protected URL, but //admin was not. So the router would resolve //admin and all its underlying pages to the admin section, and the authoriser wouldn't recognise it as a location you'd need admin privileges for.

In other words: the admin section of all our websites could be entered without any login, by simply replacing /admin with //admin.

I don't remember drinking my coffee after that.

So we did the only thing we could do: manually update all of our websites, some running a very outdated version of our framework. It took us 3 days to do this with 5 or 6 developers. You can do the math on how much it cost.

In the end we never actually got to know whether the bug had been exploited: it was discovered by accident by one of our colleagues over the weekend, and we didn't keep access logs longer than a few days. So nobody could tell whether someone had unauthorised access to one of our sites over the past years; let alone know if, and which data had been leaked.

Don't write your own framework, at least not when you're building websites for paying clients; who trust your work to be professional and secure. Whatever framework you use, make sure it's backed by a large community.

Want to share your thoughts? Let's discuss them on HN.

Thanks for reading! This post is part of my "Dev Diaries" series where I write about my own and personal experiences as a developer. Would you like to read some more?

If you want to stay up to date about what's happening on this blog, you can follow me on Twitter or subscribe to my newsletter:

]]>
2021-03-25T00:00:00+00:00
<![CDATA[ PHP 8.1: Enums ]]> https://www.stitcher.io/blog/php-enums They are finally coming — built-in support for enums will be added in PHP 8.1! Some might consider them long overdue, but you don't hear me complain; I'm glad they made it! This post is dedicated to looking at the newly added feature in-depth. If you want to stay up to date about these kinds of changes and new features in PHP, make sure to subscribe to my newsletter.

As usual with my PHP feature posts, we start with a high level overview of what enums look like:

enum Status
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;
}

The benefit of enums is that they represent a collection of constant values, but most importantly those values can be typed, like so:

class BlogPost
{
    public function __construct(
        public Status $status, 
    ) {}
}

In this example, creating an enum and passing it to a BlogPost looks like this:

$post = new BlogPost(Status::DRAFT);

That's the basics out of the way, as you can see there's nothing complex at all about them. There are lots of side notes to be made though, let's look at enums in depth!

# Enum methods

Enums can define methods, just like classes. This is a very powerful feature, especially in combination with the match operator:

enum Status
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;
    
    public function color(): string
    {
        return match($this) 
        {
            Status::DRAFT => 'grey',   
            Status::PUBLISHED => 'green',   
            Status::ARCHIVED => 'red',   
        };
    }
}

Methods can be used like so:

$status = Status::ARCHIVED;

$status->color(); // 'red'

Static methods are allowed as well:

enum Status
{
    // …
    
    public static function make(): Status
    {
        // …
    }
}

And you can also use self within an enum:

enum Status
{
    // …
    
    public function color(): string
    {
        return match($this) 
        {
            self::DRAFT => 'grey',   
            self::PUBLISHED => 'green',   
            self::ARCHIVED => 'red',   
        };
    }
}

# Enum interfaces

Enums can implement interfaces, just like normal classes:

interface HasColor
{
    public function color(): string;
}
enum Status implements HasColor
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;
    
    public function color(): string { /* … */ }
}

# Enum values — aka "Backed enums"

Enum values are represented by objects internally, but you can assign a value to them if you want to; this is useful for eg. serializing them into a database.

enum Status: string
{
    case DRAFT = 'draft';
    case PUBLISHED = 'published';
    case ARCHIVED = 'archived';
}

Note the type declaration in the enum definition. It indicates that all enum values are of a given type. You could also make it an int. Take note that only int and string are allowed as enum values.

enum Status: int
{
    case DRAFT = 1;
    case PUBLISHED = 2;
    case ARCHIVED = 3;
}

The technical term for typed enums is called "backed enums" since they are "backed" by a simpler value. If you decide to assign enum values, all cases should have a value. You cannot mix and match them. Enums that aren't "backed" are called "pure enums".

# Backed enums with interfaces

If you're combining backed enums and interface, the enum type must come directly after the enum name, before the implements keyword.

enum Status: string implements HasColor
{
    case DRAFT = 'draft';
    case PUBLISHED = 'published';
    case ARCHIVED = 'archived';
    
    // …
}

# Serializing backed enums

If you're assigning values to enum cases, you probably want a way to serialize and deserialize them. Serializing them means you need a way to access the enum's value. That's done with a readonly public property:

$value = Status::PUBLISHED->value; // 2

Restoring an enum from a value can be done by using Enum::from:

$status = Status::from(2); // Status::PUBLISHED

There's also a tryFrom that returns null if an unknown value is passed. If you'd use from there would be an exception.

$status = Status::from('unknown'); // ValueError
$status = Status::tryFrom('unknown'); // null

Note that you can also use the built-in serialize and unserialize functions on enums. Furthermore, you can use json_encode in combination with backed enums, its result will be the enum value. This behaviour can be overridden by implementing JsonSerializable.

# Listing enum values

You can use the static Enum::cases() method to get a list of all available cases within an enum:

Status::cases();

/* [
    Status::DRAFT, 
    Status::PUBLISHED, 
    Status::ARCHIVED
] */

Note that this array contains the actual enum objects:

array_map(
    fn(Status $status) => $status->color(), 
    Status::cases()
);

# Enums are objects

I already mentioned that enums values are represented as objects, in fact those are singleton objects. That means that you can do comparisons with them like so:

$statusA = Status::PENDING;
$statusB = Status::PENDING;
$statusC = Status::ARCHIVED;

$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof Status; // true

# Enums as array keys

Because enums values are actually objects, it's currently not possible to use them as array keys. The following will result in an error:

$list = [
    Status::DRAFT => 'draft',
    // …
];

There is an RFC to change this behaviour, but it hasn't been voted yet.

This means you can only use enums as keys in SplObjectStorage and WeakMaps.

# Traits

Enums can use traits just like classes, but with some more restrictions. You're not allowed to override built-in enum methods, and they can't contain class properties — those are prohibited on enums.

# Reflection and attributes

As expected, there are a few reflection classes added for dealing with enums: ReflectionEnum, ReflectionEnumUnitCase and ReflectionEnumBackedCase. There's also a new enum_exists function which does what its name suggests.

Just like normal classes and properties, enums and their cases can be annotated using attributes. Note that TARGET_CLASS filter will also include enums.

One last thing: enums also have a read only property $enum->name, which the RFC mentions is an implementation detail and should probably only be used for debugging purposes. It's still worth mentioning though.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

That's about all there is to say about enums, I'm very much looking forward to using them as soon as PHP 8.1 arrives, and also to sunset my own userland implementation.

]]>
2021-02-17T00:00:00+00:00
<![CDATA[ PHP Enums before PHP 8.1 ]]> https://www.stitcher.io/blog/php-enums-before-php-81

# Native support in PHP 8.1!

You can read all about built-in enums in PHP 8.1 in this post. If you're looking for more information on userland implementations of enums before PHP 8.1, you can continue to read this post.

If you came here looking for our enum implementation in PHP, that's this way. If you're interested in the design philosophy behind it, read on!


An enumeration type, "enum" for short, is a data type to categorise named values. Enums can be used instead of hard coded strings to represent, for example, the status of a blog post in a structured and typed way.

PHP doesn't have a native enum type. It offers a very basic SPL implementation, but this really doesn't cut the mustard.

There's a popular package written by Matthieu Napoli called myclabs/php-enum. It's a package I and many others have been using in countless projects. It's really awesome.

Today I want to explore some of the difficulties we encounter when solving problems like enums in userland. I'll talk about my personal take on enums, and we'll ponder on core support.

One last note: I will assume that you know what enums are, and that you know on how to use them in real life projects.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Imagine if:

We could write something like this in PHP…

class Post
{
    public function setStatus(PostStatus $status): void
    {
        $this->status = $status;
    }
}

… and be sure that the value of Post::$status is always one of three strings: draft, published or archived.

Say we'd save this Post in a database, its status would automatically be represented as a string.

The myclabs/php-enum package allows us to write this:

class PostStatus extends Enum
{
    const DRAFT = 'draft';
    const PUBLISHED = 'published';
    const ARCHIVED = 'archived';
}

We could use the constant values directly like so:

class Post
{
    public function setStatus(string $status): void
    {
        $this->status = $status;
    }
}

// …

$post->setStatus(PostStatus::DRAFT);

But this prevents us to do proper type checking, as every string could be passed to Post::setStatus().

A better approach is to use a little magic introduced by the library:

class PostStatus extends Enum
{
    private const DRAFT = 'draft';
    private const PUBLISHED = 'published';
    private const ARCHIVED = 'archived';
}

$post->setStatus(PostStatus::DRAFT());

Using the magic method __callStatic() underneath, an object of the class PostStatus is constructed, with the 'draft' value in it.

Now we can type check for PostStatus and ensure the input is one of the three things defined by the "enum".

Here's the problem with the myclabs/php-enum package though: by relying on __callStatic(), we lose static analysis benefits like auto completion and refactoring:

As you can see in this case, your IDE is unaware of the PostsStatus::DRAFT() method.

Luckily, this problem is solvable with docblock type hints:

/**
 * @method static self DRAFT()
 * @method static self PUBLISHED()
 * @method static self ARCHIVED()
 */
class PostStatus extends Enum
{
    private const DRAFT = 'draft';
    private const PUBLISHED = 'published';
    private const ARCHIVED = 'archived';
}

$post->setStatus(PostStatus::DRAFT());

But now we're in trouble when refactoring an enum's value. Say we want to rename DRAFT to NEW:

Also we're maintaining duplicate code: there's the constant values, and the doc blocks.

At this point it's time to stop and think. In an ideal world, we'd have built-in enums in PHP:

enum PostStatus {
    DRAFT, PUBLISHED, ARCHIVED;
}

Since that's not the case right now, we're stuck with userland implementations.

Extending PHP's type system in userland most likely means two things: magic and reflection.

If we're already relying on these two elements, why not go full-out and make our lives as simple as possible?

Here's how I write enums today:

/**
 * @method static self DRAFT()
 * @method static self PUBLISHED()
 * @method static self ARCHIVED()
 */
class PostStatus extends Enum
{
}

Opinionated, right? It's less code to maintain though, with more benefits.

I know this is far from an ideal situation. It would be amazing to see built-in support for enums in PHP one day. But until then, this has to do.

If you want to, you can try out my implementation here.

So, what's your take on enums? Do you want them in core PHP? Let's talk about it on Twitter!

]]>
2021-02-17T00:00:00+00:00
<![CDATA[ A storm in a glass of water ]]> https://www.stitcher.io/blog/a-storm-in-a-glass-of-water I just read a post about PHP 8 that I couldn't just silently ignore. If you want to read it first, go check it out.

With a fitting title "A Perfect Storm", the author voices their concerns about how upgrading to PHP 8 isn't an easy path, and how open source maintainers have to struggle to be able to support PHP 8 on top of their existing codebase.

There's no denying that PHP 8 introduces some breaking changes. Especially the addition of union types, named arguments and consequently the changes made to the reflection API might feel like a pain.

What the author seems to forget when calling PHP 8 "a nightmare" and claiming it'll take years before being able to use it, is that PHP 8 is a major version, and things are allowed to break. Better yet: it's the entire purpose of a major release.

I don't blame the author for this sentiment, it lives in lots of people's mind. I think it's a side effect from a long and stable period of releases during the 7.x era. We've got used to easy updates, and we find it difficult when our code breaks because of one.

The problem however isn't with PHP growing and maturing, it's with developers and companies not being able to adapt quickly enough. Here's the brutal truth: PHP 7.4 has less than one year of active support to go. By the end of 2021, PHP 8 will be the only version (together with the future PHP 8.1) that's still actively worked on. You need to get on board, or you risk becoming stuck in legacy land. Believe it or not, but PHP 7.4 will one day be what we perceive PHP 5.4 or PHP 5.6 to be: completely outdated, insecure and slow.

Instead of shifting the blame on the perfectly healthy and stable release cycle of PHP, we should look at ourselves and our companies.


If you're still here and wanting to ride along, let's discuss a few things that you can do to prevent ending up in legacy land.

First: learn and use the proper tools. A craftsman is nothing without proper knowledge of his tools. These tools include static analysers, code formatters, IDEs, test frameworks and automatic updaters. Most problems you face during upgrades are actually solved automatically if only you'd use the proper tools.

While you're at it, consider buying the people maintaining those projects a drink, they are going to save you hours upon hours of manual and boring labour; they are worth a few bucks. We recently upgraded a large project to PHP 8. It took a few hours of preparation and research, and only 2 hours to do the actual upgrade; it's doable.

Next: if you're an open source maintainer. Don't bother supporting all of PHP 7 and 8 in the same version. Embrace PHP 8 as your main target and support 7.4 if it doesn't cause any problems. If you really need to actively support older versions, you'll have to support separate branches.

What baffles me about the "open source argument" made by many against PHP 8, is that they seem to forget that their old tags will keep working just fine. It's not because code isn't actively maintained any more that you're prohibited from using it. If your users really need to support an older PHP version, have them use an older version of your packages. If there's a crucial feature missing from those older versions, they are free to fork the package and do whatever they want with it. There shouldn't be anything holding you back from only supporting the active PHP versions. If anything, you're encouraging the majority of your users to upgrade faster, you're doing the PHP community a favour.

Finally, if you're in management or running a company: sticking with older PHP versions will always make you lose money in the end. Every year you hold off on updating your codebase, it becomes more difficult and time-consuming to catch up. Can you imagine what will happen when a critical security issue is discovered in your old version? On top of that: employees really worth their money won't stick with your legacy project forever. One day they'll seize a new opportunity if you don't keep them happy. If you're not keeping up to date, you're loosing in the end.


Part of being a professional developer is to be able to deal with these kinds of situations. Sure, I'd rather spend those hours spent updating on something else, but I know it's a small investment for a lot of joy in the long run.

Don't get dragged along the negativity, embrace the maturing language that is PHP and follow along. You won't regret it.

]]>
2021-01-20T00:00:00+00:00
<![CDATA[ PHP reimagined ]]> https://www.stitcher.io/blog/php-reimagined If it were up to me, I'd change a thing or two about PHP. Of course, I don't have anything to say about PHP's development, and that's ok. I still find it an interesting thought experiment to discover what changes I'd like to make to the language I use on a day-by-day basis and I'd love to hear your thoughts as well. To be clear: it's a very subjective list and in no way a critique on the amazing work the core team is doing.

This is an updated version of an older post of mine, since PHP is growing and evolving year by year. Let's dive in!

# Final by default

A common misconception about OO programming is that it's all about inheritance. Inheritance and polymorphism have their place, but OO is way more than that.

Because these principles are more often than not abused by programmers who claim they write "OO" code, I think the language should help prevent us making these mistakes.

That's why I would make all classes final by default:

final class Foo
{
}
class Bar extends Foo
{
}

Going even one step further: classes are only allowed to extend from abstract classes or implement interfaces. This way we can prevent deep inheritance chains of concrete classes.

# Void by default

Void is a strange thing when you think about it: it a "type", indicating the lack of a type. Why not go with the obvious way: no return type, means nothing is returned.

class Foo
{
    public function bar(): void
    {
        // …
    }
}
class Foo
{
    public function bar()
    {
        return false;
    }
}

Now you might be thinking: what if a function wants to return two types, that's the next point.

# No mixed type

The mixed type basically means: "you've got no idea what this function needs or will return, figure it out on your own".

Such a loose type system can be the source of many bugs. If you feel the need to use two different types in the same function, you should either make two implementations — this is where polymorphism has its place; or you should program to an interface.

Either way, there's always a better solution then relying on mixed. In my version of PHP, the language would ensure we always choose the better solution.

# All parameters must by typed

We already established that my version of PHP would make return types required. It's no surprise that the same goes for function parameters.

public function handle($bar)
{
}
public function handle(Bar $bar)
{
}

# Class properties must be typed

The same rules apply to class properties. Luckily for us, PHP 7.4 will introduce typed properties. I'd make them required though.

class Foo
{
    public $bar;
}
class Foo
{
    public Bar $bar;
}

# Visibility modifiers are required

Explicitness eliminates room for confusion. That's why all methods and class variables must have a visibility modifier.

class Foo
{
    function bar()
    {
        // …
    }
} 
class Foo
{
    public function bar()
    {
        // …
    }
} 

# Final on variables

I started this list by saying I'd drop the final keyword, that is on classes and methods. final would be a valid keyword to mark class variables as "read only".

A final variable may be set on construct, and not be changed afterwards.

class Foo
{
    public final Bar $bar;
    
    public __construct(Bar $bar)
    {
        $this->bar = $bar;
    }
}
$foo = new Foo($bar);

$foo->bar = new Bar();

# No more uninitialized state

Right now, Typed properties can be initialized after construction. This is valid PHP

class Foo
{
    public string $bar;
    
    public function __construct() {
        // Don't initialize bar
    }
}

$foo = new Foo();

$foo->bar = 'abc';

PHP only throws an error when the property is accessed before it's initialised.

$foo = new Foo();

echo $foo->bar; // Error

I'd say to get rid rid of this behaviour. If a typed property isn't initialised after the object was constructed, you get an error.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Named parameters

Named parameters were originally on this list, but fortunately are possible as of PHP 8!

# Better closures

I originally listed "multiline short closures" here, but I think it's a little bit more complex. What I'd like to see is a combination of function/fn and =>/{. I'd make all combinations possible:

function a() {
    return /* … */;
}

function b() => 1;

fn c() {
    return /* … */;
}

fn d() => 1;

Here's the difference: when using the function keyword, there's no automatic access to the outer scope, in other words you'll have to use use to access variables outside the closure. Using fn doesn't have this restriction.

If you're using the bracket notation for the closure's body {}, you'll be allowed to write multiline functions, but there's no magic return statement. => on the other hand only allows a single expression, which is immediately returned.

# Scalar types are also objects

One of the few things I think that we're all in agreement about: the current PHP function names and definitions are inconsistent and kind of sucky.

Let's treat all scalar types as objects, allowing them to contain what otherwise would be standalone functions.

public function handle(): string
{
    return "a, b, c";
}

$this->handle()->explode(',');

# Improved variance

You may have noticed a trend in the above changes. Most of them relate to PHP's type system. If all them were added, we'd also need to make the current type system more flexible.

Luckily again, PHP 7.4 already introduces improvements regarding type variance.

class Bar extends Foo { /* … */ }
interface A
{
    public function handle(Bar $bar): Foo;
}

class B implements A
{
    public function handle(Foo $bar): Bar
    {
        // …
    }
}

# Always strict type checking

Strict type checking is done by default, you should never declare(strict_types=1); anymore.

# Generics

After several improvements to the type system, I'd add some more improved ways to actually use it.

First a feature that probably most of the PHP world is waiting for: generics.

class List<T>
{
    public function current(): T
    {
        // …
    }
}

# Enums

Next up: built-in enums. Based on the several userland implementations it's clear that the community would benefit from a built-in enum type.

enum Status 
{
    DRAFT, STATUS, PUBLISHED;
}
class Bar
{
    public Status $status;
}
$bar->status = Status::DRAFT;

It's interesting to note that a new RFC popped up that might add enums in PHP 8.1. It's still being discussed though, so nothing concrete yet.

# Structs

To end this list: structs. One of my own packages I use all the time is the data transfer object package. It allows us to define strongly typed objects. In essence, they are a userland implementation of what structs are meant to solve.

struct Point {
    int $x;
    int $y;
}
$point = Point {1, 2}

# What would you like to change?

Let me know what's on your PHP wishlist! If you want to be kept in the loop, feel free to subscribe to my newsletter.

]]>
2021-01-13T00:00:00+00:00
<![CDATA[ Websites should be more like Star Wars ]]> https://www.stitcher.io/blog/websites-like-star-wars I have to admit I'm not the biggest Star Wars fan, but I can enjoy one of the movies from time to time. Whenever I watch one there is one thing that always stands out: the movement in everything Star Wars related feels… odd.

Here's what I mean with that: whenever a door slides open or a non-human character speaks, whenever a droid walks or moves; those movements feel a little unnatural to me. It makes the characters feel like puppets, and it makes objects like sliding doors feel like they have no actual weight.

Here's an example:

Baby Yoda feels like it's a hand played puppet, its movements don't seem natural at all. There is, of course, a good reason that Star Wars uses this approach: it's a style they continued to use ever since the oldest movies which, back in the day, had to deal with much more technical limitations and where the puppet-like feeling was unavoidable. They decided to continue to use the same style of movement, not because it was necessary, but because of the charm and nostalgia feeling.

So what does that have to do with websites?

I was a toddler when the internet began to grow, so I'm too young to have witnessed its rise first hand. Still I got into web development at an early age, starting with HTML when I was 12. These were times when the web was still shaping itself to what we know today. Just like Star Wars, the web had to deal with lots of technical limitations but with those limitations came creativity.

When I browse the web today, there's little creativity left to find. Sure, most websites are as polished as can be, but compare their design to ten others in the same niche, and you'll quickly start to see pattern emerging. While so many websites these days are pixel-perfect, they lack the soul and personality the web used to have.

Test it yourself: think about a topic and do a simple google search. Whether it's email campaigns, travel blogs, social media or news sites; there's little distinction between them.

I wish more websites aimed to be more like Star Wars: not sticking to the modern day rules and instead hold on to the things that give character, that make your website unique. We're allowed to break free from best practices, we're allowed to experiment.

The web still allows you to do so, the only thing holding us back is us.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2021-01-10T00:00:00+00:00
<![CDATA[ How to be right on the internet ]]> https://www.stitcher.io/blog/how-to-be-right-on-the-internet The internet is a wonderful place. It's full of knowledge, it enables us to stay close to family and friends, and sometimes there are people who get under your skin and cause an immense amount of frustration. I've been on both sides: the one dealing with troublemakers, but also the troublemaker himself.

Arguments and fights on the internet can make you feel exhausted, often having an effect on your mood in real life. So today I'd like to share a few guidelines with you on managing such situations. You can apply these rules to every situation: whether you've found yourself unwillingly dragged into a conversation that's quickly spiralling out of control, or whether you're the one who's getting a little heated yourself.

Finally, before diving in, I want to mention a very popular book on the topic of "how to deal with people": The 7 Habits of Highly Effective People by Stephen Covey. It's one of the most popular resources on people management, and part of this post was inspired by Covey's principles.

# The meaning of conflict

First things first: I use the term "conflict" and I want to make sure we're all on the same page on what it means. Let's look at the Oxford Dictionary's definition:

a situation in which people, groups or countries disagree strongly or are involved in a serious argument

Note how it doesn't say anything about the feelings of those people, groups or countries. This is the definition I'll assume when I use the term "conflict" throughout this post. It's a situation where a tension between people exists, but it doesn't mean that that situation needs to deteriorate into one where feelings are hurt and frustration arises.

When I say "conflict", it means there is a situation where people don't agree, but it doesn't mean they are fighting over it.

# Five rules

Instead of the seven habits described by Covey (which cover more than conflict resolving), we'll stick with five rules.

I've managed several online communities in the past years and most recently I've been moderating /r/php on Reddit. I wouldn't say I have to deal with conflicts every day, but they are very real and very abundant indeed. That's why I always try to follow these five rules when having a conversation, and I find they work very well.

The first four rules focus on conflict resolving in a constructive way. They have in common that they focus on a positive outcome. They assume a level of empathy and understanding. The fifth rule is for dealing with conflicts when there's no other way out. It's the emergency break when you notice the conversation is derailing. It's the most drastic one, but in some cases it's better to prevent further escalation.

Also note that you can use these rules, even when the other party doesn't agree to them. That's truly the power of these rules: you can apply them yourself even when the other side doesn't have the same constructive intentions. You'll notice that in most cases they'll even start copying your way of communication throughout a conflict, and start being constructive themselves, without even knowing.

Let's look at these rules one by one.

# Right or Wrong

Rule number one: there seldom is one absolute truth. Most times there's only our personal interpretation of any given situation. All of us have a background and context; things like education, programming languages, frameworks, projects and workplace have a tangible impact on our opinion, our view of the world.

There's a lot of power in realizing that both you and the other party are not neutral. I even think this offers a significant learning opportunity: a conflict where both parties are open to the context of the other one can result in valuable lessons for both.

We're only into rule one, and we've already identified a secondary goal: we can not only prevent conflicts from devolving into chaos, but we can seize the given opportunities to learn and grow. Even when the other party isn't interested in your context, it's still possible for you to grow.

# Perspective

You're on the internet, written text can — and will — be interpreted in other ways than intended. I'd say that written text is one of the worst mediums to resolve conflicts with, since there's very little nuance you can make with dry text.

This is why people on the internet often come across as more rude than they'd be in real life. While many simply forget about the downsides of text, some know it very well and abuse that "freedom" the internet gives. The way some people act online is mind-boggling to me: surely they can't be like this in real life? Realizing this reality will help you put heated conflicts into perspective.

But there's also something to say the other way around: you have to make sure the other party doesn't misinterpret what you wrote. That's why I try to explain my perspective clearly enough when dealing with a conflict. It often looks something like this:

"I want to make sure this doesn't come across as rude. I genuinely believe my point of view is the correct one, but I don't want to insult you by phrasing it in a rude or hurtful way."

Addressing your perspective and acknowledging that there can be differences is often enough to defuse tensions entirely.

# Expectations instead of assumptions

When reading someone's comments, you might be inclined to assume an undertone, an emotion, maybe even an unspoken opinion. These assumptions might trigger you in writing a more offensive response. You should be aware that all of this can happen unconsciously, and protect yourself from it.

The most effective way of doing so is by describing how you interpreted any given kind of text. Next you should allow the other party to correct your assumptions.

"I interpreted what you've written here as such and such, and want to make it clear that my answer is based on that interpretation. If you didn't mean it the way I interpreted it, please let me know."

From your point of view, you can be one step ahead and clarify your emotions and possible underlying feelings within the text. Furthermore, by reviewing what you've written, you can often identify parts that might be misconstrued and rewrite them. In some cases it even helps identify that you as well were driven by emotions without knowing and help prevent further escalation by rephrasing before posting.

# Conflict resolving

One important thing to keep in mind at all times is that resolving a conflict doesn't mean all parties have to agree. It's perfectly fine to step away without agreement. There are conflicts that can't be resolved, and you'll have to find a way of dealing with such situations. Sometimes this means that you both agree to disagree, but other times it can also result in one of the parties stepping away from a project or community. Conflict resolving can be drastic indeed, but it should always be done in a respectful and civilized manner.

If you keep the goal of resolving a conflict in mind, you can steer the conversation in the right way.

# The power of silence

Like I said before, the fifth rule is the emergency break. There are conflicts that you don't want to engage in or where the other party doesn't seem to be open to any kind of other opinion. You'll always have the power to simply step away from those conversations.

In such cases, it would be most polite to let the other party know you won't be engaging this conversation any further, but there might be cases where even such a message wouldn't be welcome. Just step away. You don't have to reply, you don't have to engage. Decide what's worth your time and what's not.

You shouldn't consider "stepping away" as failing: in some cases it's actually the better and smarter thing to do.

# In summary

  1. There is rarely one absolute truth, more often there are our interpretations, influenced by our personal context.
  2. Written text is one of the worst mediums to accurately describe your feelings. Use text to your advantage and clearly describe what your intentions are.
  3. Voice your assumptions instead of letting them guide you unintentionally.
  4. Resolving a conflict doesn't mean you have to agree with each other.
  5. If all else fails, you have the power to step away.

Thanks for reading! This post is part of my "Dev Diaries" series where I write about my own and personal experiences as a developer. Would you like to read some more?

If you want to stay up to date about what's happening on this blog, you can follow me on Twitter or subscribe to my newsletter:

]]>
2020-12-31T00:00:00+00:00
<![CDATA[ When I lost a few hundred leads ]]> https://www.stitcher.io/blog/when-i-lost-a-few-hundred-leads

Before you start…

Some readers have reached out to me and talked about solutions to the technical problems described in this post. I'd like to make clear that this post isn't about how to deal with or prevent bugs. It's about how I felt during a rather bad and stressful time, to talk about emotions and how to self-reflect and improve. With that out of the way, I hope you find value in this post.

I went home that evening anxious and stressed out. I'm not ashamed to admit I had to fight back a few tears on the train during my commute back home. Those weren't tears of sadness, mind you, rather tears of fear.

It had only been a few hours since my manager came to me saying that one of our clients complained that someone called them, saying they expected to have gotten a call after filling in the contact form and they never did. They asked me to look into it.

See, this client sold a particular service to people, and they could fill in a form on the website to ask for a quote. Depending on their residence the closest office would call them back after they'd filled in that form. This wasn't a super complex form, it had three steps, and asked some information about people's project, some check boxes, radio buttons and drop downs — you've seen such forms before. Every request submitted is called "a lead" (a potential client), hence the name we internally gave the form: "the lead form".

When one of those leads called our client's office to complain about never receiving a phone call, the office manager was clever enough to ask for the lead's email address, making my job easier. All I had to do was go in the database, find the form submission for that lead's email address, and look at what went wrong.

As a junior developer, my first thought was to look for external problems. Every time a lead submitted the form, an email would be sent to the corresponding office with all information, my guess was that either that email ended up in spam for some reason, or that maybe there had been a problem with our mailing service.

I couldn't find an entry in our database for that email address though.

"The address is probably wrong" — I remember thinking. See, the first thing that happens when the form is submitted is that its contents are saved into the database. That way we always have a copy of the data, in case something went wrong with mailing the office. So I told my manager to contact our client, telling them the email address they gave me is probably wrong, and went on with my day.

A few minutes later and my manager tells me that the email address is absolutely correct.

Ok, that means that the form was never submitted then? I sigh out loud: "it probably has to do with all third party javascript trackers and marketing tools we're using on the site". Team marketing was quite into using A/B testing libraries or tools like Mouseflow to record a user's screen so that they can analyse how people interact with the website. I wouldn't be surprised if one of those tools messed up the form submission.

So I call their marketing team and ask if they know anything more about this lead. They tell me they'll look into it and I, again, went on with my day. An hour goes by and I've already forgotten about the whole affair thanks to my "not my problem" mentality. Suddenly my manager's back again: team marketing wants to speak to me and he hands me the phone.

They have found the screen recording for that particular lead. Around 50% of all sessions were recorded, so we were pretty lucky finding it. It turns out the lead actually submitted the form, they'll send me the recording now.

This is the point where I start to consider that it might be my problem after all, but let's not jump to conclusions. I watch the recording, and yes indeed: the lead fills in step one, step two, step three, submits it and… that's not the right page. He should see a "thank you" page after submitting the form. Instead he sees a page with the header and footer of the website, but without any content on it.

That's not good.

I go into the test environment and submit the form with exactly the same data. I end up on the same blank page, and sure enough my submission isn't saved into the database. is this page? I've never seen it before. So I look at the logs. An error: null passed instead of boolean. That's a type error deep down in my code.


Before moving on, let me tell you about the error, why it happened and why no one knew of it.

So this form had a bunch of input fields, including check boxes. Whenever a lead request was submitted, we captured the data and prepared to save it into the database. It looked something like this:

$formSubmission = new FormSubmission();

$formSubmission->name = $form->get('name');
$formSubmission->email = $form->get('email');
// …

$entityManager->save($formSubmission);

The bug, simple as it may sound, was a simple typo:

$formSubmission->more_feedback_required = 
    $form->get('more_feedback_requierd');

Can you spot the requierd when calling $form->get()?

As it turns out, our form library — written by us for our in-house framework — wouldn't give an error when you're trying to get an unknown property, it simply returns null.

The database however expected a boolean value, and thus an error was thrown.

This isn't that big of a problem, albeit not that no one knew of it. The second part of the bug was the empty page: this one was served directly from nginx whenever an error would occur — we didn't want visitors to be bothered by error pages. It turned out that one of my colleagues had set it up a few months ago, and I didn't know of it. When a lead saw the empty page, they probably thought the form was submitted just fine, they didn't know there should be a "thank you" page instead.

Next, we never saw the error happening during testing, because the erroneous code only ran in specific cases, and no one thought about testing those. The form itself was unit tested, but not all edge cases were.

Finally we weren't automatically notified of errors in production, which meant no one knew about it until the client complained. At the time, the company didn't do any error monitoring on production, that might seem completely bonkers, but yes: it happens.


I deployed a fix for the typo (which I wrote to begin with), I told my manager about the problem and that it's fixed. In turn he asks me how many people were affected by it. I tell him to check up on this. I go back to my desk and check when this bug was committed and deployed to production. A month ago.

I remember a sickening feeling starting to rise from this point on.

I check the log files, and scan for the error. I'm stunned for a few moments. It's not just a few errors. It's hundreds. Hundreds of leads that we lost over the past month. I started to shake a little. After a few minutes I reported back to my manager. I again explained the bug itself, why it had been undetected for a month, and finally gave him the hard numbers. There was nothing we could do: those leads were lost.

# The aftermath

The following days were probably the most stressful in my career up until today. I'm thankful that no one blamed me directly: we all agreed that this was the team's responsibility and not my own. Still I was the one who wrote the typo, I was the one tasked with testing and deploying the changes to the form a month earlier.

I can honestly tell you that I had a hard time dealing with this feeling of guilt, even though my manager and colleagues were understanding.

I could of course point fingers to others: we shouldn't have used our own framework with its quirky form validation; devops should have installed some kind of error tracking; the empty error page should have been more clear. I admit I thought all these thoughts the days after, I even felt anger towards some of my colleagues for a while. But I learned that anger didn't get me anywhere. I better spent my time looking at how I could learn from all of this, instead of pointing fingers at others.

So, after a few weeks of processing, what felt to me like a trauma; I found the courage to look at what I could have done differently. What I could learn from this.

First of: ask my colleagues for reviews. It doesn't matter if I'm a junior or senior; a fresh look on my code is always valuable. I've learned that most people are busy themselves and won't help you unless you specifically ask them to. To this day I often ask my colleagues to review a PR. Even when I might have more years of experience, I highly value my colleagues' input. No matter how long I've been doing this, I still make mistakes, and I shouldn't be too proud to admit that.

Next: I don't use tools I don't know or trust. This wasn't the first time I stumbled upon the limitations of a custom, in-house framework. I've learned that it's better to use tools that are supported by a large open source community. It wasn't my decision which framework to use on that particular project, but it was a requirement for my next job: I only want to use well-known, community supported frameworks when it comes to client projects in production.

Third: I should test better myself, I shouldn't assume that a checkbox is just a checkbox. I should be skeptical towards every line of code I write. This incident is one of the reasons I came to love strongly typed systems more and more. I don't want a language that tries to be smart and juggle types here and there. I want a language that crashes hard when something happens that shouldn't happen, the stronger the type system, the better.

Next: I tend to code more defensively. I constantly ask myself: are null values possible? Anything unexpected that could happen? I prefer to add an extra check rather than assuming it'll work fine.

And finally: I learned not to blame others first. I think it's a trait of many junior developers to think the problem happened somewhere else. It was an important and humbling lesson to learn, and I tend to be more cautious when blaming an external party these days.


It's strange writing this blogpost. I was a young developer when this happened to me, and it's something that I think has had a lasting impact on my programming career. I also think we don't talk about this often enough. I lay awake at night thinking about how angry the client would be, what the impact would be on the company I worked for. Even though I realise most of my feelings were exaggerated, they still were there. I wasn't able to just turn them off. I felt fear and shame, and had little people to talk about it.

We write about our success stories, but what about our mistakes? What about the impact of these kinds of experiences on our mental health? Many of us in the software industry are introverts, or feel like we can't talk about our deepest emotions.

Let's keep an eye out for each other instead.

Thanks for reading! This post is part of my "Dev Diaries" series where I write about my own and personal experiences as a developer. Would you like to read some more?

If you want to stay up to date about what's happening on this blog, you can follow me on Twitter or subscribe to my newsletter:

]]>
2020-11-29T00:00:00+00:00
<![CDATA[ Announcing Blogs for Devs ]]> https://www.stitcher.io/blog/blogs-for-devs

When I started my blog in 2017 I never imagined it'd be as successful as now. I never seriously invested in SEO or did any aggressive marketing campaigns, and yet here we are: closing in to 2 million sessions, and the end is no where near.

Cumulated amount of sessions per weekCumulated amount of sessions per week

I did of course do something to put my content out there. Over the years I found ways to reach people who might be interested in reading what I wrote. But still, there wasn't much effort involved: I'd share a link from time to time; I'd listen to feedback (a thick skin is an asset if you're writing a blog, people on the internet love to tell you you're wrong); I tried to improve my writing.

I guess the reason it feels like little effort is because it's something I actually really enjoy doing. Even though I'm making some money with my blog now, I still consider it a hobby project. There has never been any pressure to write when I didn't want to. I ignored most of the "blogging best practices" because they asked way too much of my free time. I managed to find my own way of doing things, and it turns out it works.

Throughout this series I'll share everything I learned during my blogging journey with you: how to start, how to grow, how to analyse, how to engage with your audience, how to make money by writing on the internet. I'm a developer so some topics will be technical, but even if you've got no programming experience, you'll be able to learn lots — I'll keep things as simple as possible.

So, are you a blogger? Has it been your long-time dream of starting a blog? I'm happy to share my experience with you!

Do you enjoy this series?

I write about much more technical and blog related topics, if you want to you can subscribe to stay up to date about my content.

You can also subscribe via RSS.

]]>
2020-11-28T00:00:00+00:00
<![CDATA[ Upgrade to PHP 8 with Homebrew on Mac ]]> https://www.stitcher.io/blog/php-8-upgrade-mac

# Upgrading with Homebrew

Start by making sure brew is up-to-date:

brew update

Next, upgrade PHP. You can either use the built-in php recipe, use tap shivammathur/homebrew-php. I'd recommend the second approach, since it allows you to easily install several PHP versions and switch between them.

# Normal upgrade

brew upgrade php

# Upgrade with shivammathur/homebrew-php

brew tap shivammathur/php
brew install shivammathur/php/php@8.0

To switch between versions, use the following command:

brew link --overwrite --force php@8.0

You can read more in the repository.

# Next steps

Check the current version by running php -v:

php -v

Restart Nginx or Apache:

sudo nginx -s reload
sudo apachectl restart

And make sure that your local web server also uses PHP 8 by visiting this script:

# index.php, accessible to your web server

phpinfo();

The version should show 8.0.x.

Note: if you're using Laravel Valet, please keep on reading, you need some extra steps in order for the web server to properly work.

# Valet

If you're using Laravel Valet, you should do the following steps to upgrade it:

composer global update

You can use valet use to switch between PHP versions:

valet use php@8.0
valet use php@7.4

Note that if you're using an older Valet version (prior to v2.13.18), when switching from PHP 8 to PHP 7.4 there was a bug that didn't properly update the changes. This was fixed in Valet 2.13.18 so that it now automatically removes the valet socket after having run valet use php@7.4. If you need to do this manually, you can run:

cd ~/.config/valet
rm valet.sock
valet restart

# Extensions

PHP extensions are installed using pecl. I personally use Imagick, Redis and Xdebug. They can be installed like so:

pecl install imagick
pecl install redis
pecl install xdebug

You can run pecl list to see which extensions are installed:

pecl list

# Installed packages, channel pecl.php.net:
# =========================================
# Package Version State
# imagick 3.4.4   stable
# redis   5.1.1   stable
# xdebug  2.8.0   stable

You can search for other extensions using pecl search:

pecl search pdf

# Retrieving data...0%
# ..
# Matched packages, channel pecl.php.net:
# =======================================
# Package Stable/(Latest) Local
# pdflib  4.1.2 (stable)        Creating PDF on the fly with the PDFlib library

Make sure to restart your web server after installing new packages:

sudo nginx -s reload
sudo apachectl restart

If you're using Laravel Valet, you should restart it as well.

valet restart

Make sure all extensions are correctly installed and loaded by checking both your PHP webserver and CLI installs:

php -i | grep redis
var_dump(extension_loaded('redis'));

If extensions aren't properly loaded, there are two easy fixes.

First, make sure the extensions are added in the correct ini file. You can run php --ini to know which file is loaded:

Configuration File (php.ini) Path: /usr/local/etc/php/7.4</hljs>
Loaded Configuration File:         /usr/local/etc/php/7.4/php.ini
Scan for additional .ini files in: /usr/local/etc/php/7.4/conf.d
Additional .ini files parsed:      /usr/local/etc/php/7.4/conf.d/ext-opcache.ini,
/usr/local/etc/php/7.4/conf.d/php-memory-limits.ini

Now check the ini file:

extension="redis.so"
extension="imagick.so"
zend_extension="xdebug.so"

Note that if you're testing installed extensions via the CLI, you don't need to restart nginx, apache or Valet when making changes to ini settings.

The second thing you can do, if you're updating from an older PHP version which also used pecl to install extension; is to reinstall every extension individually.

pecl uninstall imagick
pecl install imagick

# Last step

Finally you should test and upgrade your projects for PHP 8 compatibility.

]]>
2020-11-26T00:00:00+00:00
<![CDATA[ What's new in PHP 8 ]]> https://www.stitcher.io/blog/new-in-php-8 PHP 8 was released on November 26, 2020. You can download it here. It's a new major version, which means that there are some breaking changes, as well as lots of new features and performance improvements.

Because of the breaking changes, there's a higher chance you'll need to make some changes in your code to get it running on PHP 8. If you've kept up to date with the latest releases though, the upgrade shouldn't be too hard, since most breaking changes were deprecated before in the 7.* versions. And don't worry, all these deprecations are listed in this post.

Besides breaking changes, PHP 8 also brings a nice set of new features such as the JIT compiler, union types, attributes, and more.

# New features

Let's start with all new features, it's quite a list!


# Union types RFC

Given the dynamically typed nature of PHP, there are lots of cases where union types can be useful. Union types are a collection of two or more types which indicate that either one of those can be used.

public function foo(Foo|Bar $input): int|float;

Note that void can never be part of a union type, since it indicates "no return value at all". Furthermore, nullable unions can be written using |null, or by using the existing ? notation:

public function foo(Foo|null $foo): void;

public function bar(?Bar $bar): void;

# JIT RFC

The JIT — just in time — compiler promises significant performance improvements, albeit not always within the context of web requests. I've done my own benchmarks on real-life web applications, and it seems like the JIT doesn't make that much of a difference, if any, on those kinds of PHP projects.

If you want to know more about what the JIT can do for PHP, you can read another post I wrote about it here.


# The nullsafe operator RFC

If you're familiar with the null coalescing operator you're already familiar with its shortcomings: it doesn't work on method calls. Instead you need intermediate checks, or rely on optional helpers provided by some frameworks:

$startDate = $booking->getStartDate();

$dateAsString = $startDate ? $startDate->asDateTimeString() : null;

With the addition of the nullsafe operator, we can now have null coalescing-like behaviour on methods!

$dateAsString = $booking->getStartDate()?->asDateTimeString();

You can read all about the nullsafe operator here.


# Named arguments RFC

Named arguments allow you to pass in values to a function, by specifying the value name, so that you don't have to take their order into consideration, and you can also skip optional parameters!

function foo(string $a, string $b, ?string $c = null, ?string $d = null) 
{ /* … */ }

foo(
    b: 'value b', 
    a: 'value a', 
    d: 'value d',
);

You can read about them in-depth in this post.


# Attributes RFC

Attributes, commonly known as annotations in other languages, offers a way to add meta data to classes, without having to parse docblocks.

As for a quick look, here's an example of what attributes look like, from the RFC:

use App\Attributes\ExampleAttribute;

#[ExampleAttribute]
class Foo
{
    #[ExampleAttribute]
    public const FOO = 'foo';
 
    #[ExampleAttribute]
    public $x;
 
    #[ExampleAttribute]
    public function foo(#[ExampleAttribute] $bar) { }
}
#[Attribute]
class ExampleAttribute
{
    public $value;
 
    public function __construct($value)
    {
        $this->value = $value;
    }
}

Note that this base Attribute used to be called PhpAttribute in the original RFC, but was changed with another RFC afterwards. If you want to take a deep dive in how attributes work, and how you can build your own; you can read about attributes in depth on this blog.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Match expression RFC

You could call it the big brother of the switch expression: match can return values, doesn't require break statements, can combine conditions, uses strict type comparisons and doesn't do any type coercion.

It looks like this:

$result = match($input) {
    0 => "hello",
    '1', '2', '3' => "world",
};

You can read up on the match expression in detail, over here.


# Constructor property promotion RFC

This RFC adds syntactic sugar to create value objects or data transfer objects. Instead of specifying class properties and a constructor for them, PHP can now combine them into one.

Instead of doing this:

class Money 
{
    public Currency $currency;
 
    public int $amount;
 
    public function __construct(
        Currency $currency,
        int $amount,
    ) {
        $this->currency = $currency;
        $this->amount = $amount;
    }
}

You can now do this:

class Money 
{
    public function __construct(
        public Currency $currency,
        public int $amount,
    ) {}
}

There's a lot more to tell about property promotion, you can read about them in this dedicated post.


# New static return type RFC

While it was already possible to return self, static wasn't a valid return type until PHP 8. Given PHP's dynamically typed nature, it's a feature that will be useful to many developers.

class Foo
{
    public function test(): static
    {
        return new static();
    }
}

# New mixed type RFC

Some might call it a necessary evil: the mixed type causes many to have mixed feelings. There's a very good argument to make for it though: a missing type can mean lots of things in PHP:

  • A function returns nothing or null
  • We're expecting one of several types
  • We're expecting a type that can't be type hinted in PHP

Because of the reasons above, it's a good thing the mixed type is added. mixed itself means one of these types:

  • array
  • bool
  • callable
  • int
  • float
  • null
  • object
  • resource
  • string

Note that mixed can also be used as a parameter or property type, not just as a return type.

Also note that since mixed already includes null, it's not allowed to make it nullable. The following will trigger an error:

// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.
function bar(): ?mixed {}

# Throw expression RFC

This RFC changes throw from being a statement to being an expression, which makes it possible to throw exception in many new places:

$triggerError = fn () => throw new MyError();

$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');

# Inheritance with private methods RFC

Previously, PHP used to apply the same inheritance checks on public, protected and private methods. In other words: private methods should follow the same method signature rules as protected and public methods. This doesn't make sense, since private methods won't be accessible by child classes.

This RFC changed that behaviour, so that these inheritance checks are not performed on private methods anymore. Furthermore, the use of final private function also didn't make sense, so doing so will now trigger a warning:

Warning: Private methods cannot be final as they are never overridden by other classes

# Weak maps RFC

Built upon the weakrefs RFC that was added in PHP 7.4, a WeakMap implementation is added in PHP 8. WeakMap holds references to objects, which don't prevent those objects from being garbage collected.

Take the example of ORMs, they often implement caches which hold references to entity classes to improve the performance of relations between entities. These entity objects can not be garbage collected, as long as this cache has a reference to them, even if the cache is the only thing referencing them.

If this caching layer uses weak references and maps instead, PHP will garbage collect these objects when nothing else references them anymore. Especially in the case of ORMs, which can manage several hundreds, if not thousands of entities within a request; weak maps can offer a better, more resource friendly way of dealing with these objects.

Here's what weak maps look like, an example from the RFC:

class Foo 
{
    private WeakMap $cache;
 
    public function getSomethingWithCaching(object $obj): object
    {
        return $this->cache[$obj]
           ??= $this->computeSomethingExpensive($obj);
    }
}

# Allowing ::class on objects RFC

A small, yet useful, new feature: it's now possible to use ::class on objects, instead of having to use get_class() on them. It works the same way as get_class().

$foo = new Foo();

var_dump($foo::class);

# Non-capturing catches RFC

Whenever you wanted to catch an exception before PHP 8, you had to store it in a variable, regardless whether you used that variable or not. With non-capturing catches, you can omit the variable, so instead of this:

try {
    // Something goes wrong
} catch (MySpecialException $exception) {
    Log::error("Something went wrong");
}

You can now do this:

try {
    // Something goes wrong
} catch (MySpecialException) {
    Log::error("Something went wrong");
}

Note that it's required to always specify the type, you're not allowed to have an empty catch. If you want to catch all exceptions and errors, you can use Throwable as the catching type.


# Trailing comma in parameter lists RFC

Already possible when calling a function, trailing comma support was still lacking in parameter lists. It's now allowed in PHP 8, meaning you can do the following:

public function(
    string $parameterA,
    int $parameterB,
    Foo $objectfoo,
) {
    // …
}

As a sidenote: trailing commas are also supported in the use list of closures, this was an oversight and now added via a separate RFC.


# Create DateTime objects from interface

You can already create a DateTime object from a DateTimeImmutable object using DateTime::createFromImmutable($immutableDateTime), but the other way around was tricky. By adding DateTime::createFromInterface() and DatetimeImmutable::createFromInterface() there's now a generalised way to convert DateTime and DateTimeImmutable objects to each other.

DateTime::createFromInterface(DateTimeInterface $other);

DateTimeImmutable::createFromInterface(DateTimeInterface $other);

# New Stringable interface RFC

The Stringable interface can be used to type hint anything that implements __toString(). Whenever a class implements __toString(), it automatically implements the interface behind the scenes and there's no need to manually implement it.

class Foo
{
    public function __toString(): string
    {
        return 'foo';
    }
}

function bar(string|Stringable $stringable) { /* … */ }

bar(new Foo());
bar('abc');

# New str_contains() function RFC

Some might say it's long overdue, but we finally don't have to rely on strpos() anymore to know whether a string contains another string.

Instead of doing this:

if (strpos('string with lots of words', 'words') !== false) { /* … */ }

You can now do this

if (str_contains('string with lots of words', 'words')) { /* … */ }

# New str_starts_with() and str_ends_with() functions RFC

Two other ones long overdue, these two functions are now added in the core.

str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true

# New fdiv() function PR

The new fdiv() function does something similar as the fmod() and intdiv() functions, which allows for division by 0. Instead of errors you'll get INF, -INF or NAN, depending on the case.


# New get_debug_type() function RFC

get_debug_type() returns the type of a variable. Sounds like something gettype() would do? get_debug_type() returns more useful output for arrays, strings, anonymous classes and objects.

For example, calling gettype() on a class \Foo\Bar would return object. Using get_debug_type() will return the class name.

A full list of differences between get_debug_type() and gettype() can be found in the RFC.


# New get_resource_id() function PR

Resources are special variables in PHP, referring to external resources. One example is a MySQL connection, another one a file handle.

Each one of those resources gets assigned an ID, though previously the only way to know that id was to cast the resource to int:

$resourceId = (int) $resource;

PHP 8 adds the get_resource_id() functions, making this operation more obvious and type-safe:

$resourceId = get_resource_id($resource);

# Abstract methods in traits improvements RFC

Traits can specify abstract methods which must be implemented by the classes using them. There's a caveat though: before PHP 8 the signature of these method implementations weren't validated. The following was valid:

trait Test {
    abstract public function test(int $input): int;
}

class UsesTrait
{
    use Test;

    public function test($input)
    {
        return $input;
    }
}

PHP 8 will perform proper method signature validation when using a trait and implementing its abstract methods. This means you'll need to write this instead:

class UsesTrait
{
    use Test;

    public function test(int $input): int
    {
        return $input;
    }
}

# Object implementation of token_get_all() RFC

The token_get_all() function returns an array of values. This RFC adds a PhpToken class with a PhpToken::tokenize() method. This implementation works with objects instead of plain values. It consumes less memory and is easier to read.


# Variable syntax tweaks RFC

From the RFC: "the Uniform Variable Syntax RFC resolved a number of inconsistencies in PHP's variable syntax. This RFC intends to address a small handful of cases that were overlooked."


# Type annotations for internal functions EXTERNALS

Lots of people pitched in to add proper type annotations to all internal functions. This was a long standing issue, and finally solvable with all the changes made to PHP in previous versions. This means that internal functions and methods will have complete type information in reflection.


# ext-json always available RFC

Previously it was possible to compile PHP without the JSON extension enabled, this is not possible anymore. Since JSON is so widely used, it's best developers can always rely on it being there, instead of having to ensure the extension exist first.

# Breaking changes

As mentioned before: this is a major update and thus there will be breaking changes. The best thing to do is take a look at the full list of breaking changes over at the UPGRADING document.

Many of these breaking changes have been deprecated in previous 7.* versions though, so if you've been staying up-to-date over the years, it shouldn't be all that hard to upgrade to PHP 8.

# Consistent type errors RFC

User-defined functions in PHP will already throw TypeError, but internal functions did not, they rather emitted warnings and returned null. As of PHP 8 the behaviour of internal functions have been made consistent.


# Reclassified engine warnings RFC

Lots of errors that previously only triggered warnings or notices, have been converted to proper errors. The following warnings were changed.

  • Undefined variable: warning instead of notice
  • Undefined array index: warning instead of notice
  • Division by zero: DivisionByZeroError exception instead of warning
  • Attempt to increment/decrement property '%s' of non-object: Error exception instead of warning
  • Attempt to modify property '%s' of non-object: Error exception instead of warning
  • Attempt to assign property '%s' of non-object: Error exception instead of warning
  • Creating default object from empty value: Error exception instead of warning
  • Trying to get property '%s' of non-object: warning instead of notice
  • Undefined property: %s::$%s: warning instead of notice
  • Cannot add element to the array as the next element is already occupied: Error exception instead of warning
  • Cannot unset offset in a non-array variable: Error exception instead of warning
  • Cannot use a scalar value as an array: Error exception instead of warning
  • Only arrays and Traversables can be unpacked: TypeError exception instead of warning
  • Invalid argument supplied for foreach(): TypeError exception instead of warning
  • Illegal offset type: TypeError exception instead of warning
  • Illegal offset type in isset or empty: TypeError exception instead of warning
  • Illegal offset type in unset: TypeError exception instead of warning
  • Array to string conversion: warning instead of notice
  • Resource ID#%d used as offset, casting to integer (%d): warning instead of notice
  • String offset cast occurred: warning instead of notice
  • Uninitialized string offset: %d: warning instead of notice
  • Cannot assign an empty string to a string offset: Error exception instead of warning
  • Supplied resource is not a valid stream resource: TypeError exception instead of warning

# The @ operator no longer silences fatal errors

It's possible that this change might reveal errors that again were hidden before PHP 8. Make sure to set display_errors=Off on your production servers!


# Default error reporting level

It's now E_ALL instead of everything but E_NOTICE and E_DEPRECATED. This means that many errors might pop up which were previously silently ignored, though probably already existent before PHP 8.


# Default PDO error mode RFC

From the RFC: The current default error mode for PDO is silent. This means that when an SQL error occurs, no errors or warnings may be emitted and no exceptions thrown unless the developer implements their own explicit error handling.

This RFC changes the default error will change to PDO::ERRMODE_EXCEPTION in PHP 8.


# Concatenation precedence RFC

While already deprecated in PHP 7.4, this change is now taken into effect. If you'd write something like this:

echo "sum: " . $a + $b;

PHP would previously interpret it like this:

echo ("sum: " . $a) + $b;

PHP 8 will make it so that it's interpreted like this:

echo "sum: " . ($a + $b);

# Stricter type checks for arithmetic and bitwise operators RFC

Before PHP 8, it was possible to apply arithmetic or bitwise operators on arrays, resources or objects. This isn't possible anymore, and will throw a TypeError:

[] % [42];
$object + 4;

# Namespaced names being a single token RFC

PHP used to interpret each part of a namespace (separated by a backslash \) as a sequence of tokens. This RFC changed that behaviour, meaning reserved names can now be used in namespaces.


# Saner numeric strings RFC

PHP's type system tries to do a lot of smart things when it encounters numbers in strings. This RFC makes that behaviour more consistent and clear.


# Saner string to number comparisons RFC

This RFC fixes the very strange case in PHP where 0 == "foo" results in true. There are some other edge cases like that one, and this RFC fixes them.


# Reflection changes

A few reflection methods have been deprecated:

  • ReflectionFunction::isDisabled()
  • ReflectionParameter::getClass()
  • ReflectionParameter::isCallable()

You should now use ReflectionType to get information about a parameter's type:

$reflectionParameter->getType()->allowsNull();

If the type is a single type, ReflectionParameter::getType() returns an instance of ReflectionNamedType, which you can get its name from and whether it's built-in:

$reflectionParameter->getType()->getName();
$reflectionParameter->getType()->isBuiltin();

If the type is a union type however, you'll get an instance of ReflectionUnionType, which can give you an array of ReflectionNamedType like so:

$reflectionParameter->getType()->getTypes();

Checking whether a type is a union or not can be done with an instanceof check:

if ($reflectionParameter->getType() instanceof ReflectionNamedType) { 
    // It's a single type
}

if ($reflectionParameter->getType() instanceof ReflectionUnionType) {
    // It's a union type
}

Next up, three method signatures of reflection classes have been changed:

ReflectionClass::newInstance($args);
ReflectionFunction::invoke($args);
ReflectionMethod::invoke($object, $args);

Have now become:

ReflectionClass::newInstance(...$args);
ReflectionFunction::invoke(...$args);
ReflectionMethod::invoke($object, ...$args);

The upgrading guide specifies that if you extend these classes, and still want to support both PHP 7 and PHP 8, the following signatures are allowed:

ReflectionClass::newInstance($arg = null, ...$args);
ReflectionFunction::invoke($arg = null, ...$args);
ReflectionMethod::invoke($object, $arg = null, ...$args);


# Stable sorting RFC

Before PHP 8, sorting algorithms were unstable. This means that the order of equal elements wasn't guaranteed. PHP 8 changes the behaviour of all sorting functions to stable sorting.


# Fatal error for incompatible method signatures RFC

From the RFC: Inheritance errors due to incompatible method signatures currently either throw a fatal error or a warning depending on the cause of the error and the inheritance hierarchy.


# Other deprecations and changes

During the PHP 7.* development, several deprecations were added that are now finalised in PHP 8.


Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-11-26T00:00:00+00:00
<![CDATA[ PHP 8: the null safe operator ]]> https://www.stitcher.io/blog/php-8-nullsafe-operator If you've used the null coalescing operator in the past, you probably also noticed its shortcomings: null coalescing doesn't work on method calls. Instead you need intermediate checks, or rely on optional helpers provided by some frameworks:

$startDate = $booking->getStartDate();

$dateAsString = $startDate ? $startDate->asDateTimeString() : null;

The nullsafe operator provides functionality similar to null coalescing, but also supports method calls. Instead of writing this:

$country =  null;
 
if ($session !== null) {
    $user = $session->user;
 
    if ($user !== null) {
        $address = $user->getAddress();
 
        if ($address !== null) {
            $country = $address->country;
        }
    }
}

PHP 8 allows you to write this:

$country = $session?->user?->getAddress()?->country;

Let's take a look at what this new operator can and cannot do!

# Nullsafe operator in depth

Let's start by addressing the most important question: what exactly is the difference between the null coalescing operator and the nullsafe operator?

Let's take a look at this example:

class Order
{
    public ?Invoice $invoice = null;
}

$order = new Order();

Here we have an Order object which has an optional relation to an Invoice object. Now imagine we'd want to get the invoice's number (if the invoice isn't null). You could do this both with the null coalescing operator and the nullsafe operator:

var_dump($order->invoice?->number);
var_dump($order->invoice->number ?? null);

So what's the difference? While you could use both operators to achieve the same result in this example, they also have specific edge cases only one of them can handle. For example, you can use the null coalescing operator in combination with array keys, while the nullsafe operator can't handle them:

$array = [];

var_dump($array['key']->foo ?? null);
var_dump($array['key']?->foo);

Warning: Undefined array key "key"

The nullsafe operator, on the other hand, can work with method calls, while the null coalescing operator can't. Imagine an Invoice object like so:

class Invoice
{
    public function getDate(): ?DateTime { /* … */ }
    
    // …
}

$invoice = new Invoice();

You could use the nullsafe operator to call format on the invoice's date, even when it's null:

var_dump($invoice->getDate()?->format('Y-m-d'));

// null

While the null coalescing operator would crash:

var_dump($invoice->getDate()->format('Y-m-d') ?? null);

Fatal error: Uncaught Error: Call to a member function format() on null

# Short circuiting

Sometimes you could use either the null coalescing or nullsafe operator, and other times you'd need to use a specific one. The difference is that the nullsafe operator uses a form of "short circuiting": writing ?-> will cause PHP to look at whats on the lefthand side of this operator, if it's null then the righthand side will simply be discarded. The null coalescing operator is actually an isset call in disguise on its lefthand operand, which doesn't support short circuiting.

Short circuiting also means that when writing something like this:

$foo?->bar(expensive_function());

expensive_function would only be executed if $foo is actually not null.

# Nested nullsafe operators

It's possible to nest several nullsafe operator calls like so:

$foo?->bar?->baz()?->boo?->baa();

# Only for reading data

You cannot use the nullsafe operator to write data to objects:

$offer?->invoice?->date = new DateTime(); 

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

The nullsafe operator is definitely a missing piece of the puzzle finally added in PHP. Given its dynamic nature, it feels good to have a smooth way of dealing with null. The difference and overlap between the nullsafe operator and null coalescing operator feels a bit confusing at first, but I'm sure we'll get used to it.

]]>
2020-11-17T00:00:00+00:00
<![CDATA[ PHP 8: How to setup the JIT ]]> https://www.stitcher.io/blog/php-8-jit-setup PHP 8 adds a JIT compiler to PHP's core which has the potential to speed up performance dramatically. There are some sidenotes to be made about the actual impact on real-life web applications, which is why I ran some benchmarks on how the JIT performs (I've listed all relevant references in the footnotes as well).

I wanted to dedicate a blog post on how to setup the JIT as well, since there's a few things to talk about.

Honestly, setting up the JIT is one of the most confusing ways of configuring a PHP extension I've ever seen. Luckily there are some configuration shorthands available so that it's more easy to set up. Still it's good to know about the JIT config in depth, so here goes.

First of all, the JIT will only work if opcache is enabled, this is the default for most PHP installations, but you should make sure that opcache.enable is set to 1 in yourphp.ini file. Enabling the JIT itself is done by specifying opcache.jit_buffer_size in php.ini.

Note that if you're running PHP via the commandline, you can also pass these options via the -d flag, instead of adding them to php.ini:

php -dopcache.enable=1 -dopcache.jit_buffer_size=100M

If this directive is excluded, the default value is set to 0, and the JIT won't run. If you're testing the JIT in a CLI script, you'll need to use opcache.enable_cli instead to enable opcache:

php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=100M

The difference between opcache.enable and opcache.enable_cli is that the first one should be used if you're running, for example, the built-in PHP server. If you're actually running a CLI script, you'll need opcache.enable_cli.

Before continuing, let's ensure the JIT actually works, create a PHP script that's accessible via the browser or the CLI (depending on where you're testing the JIT), and look at the output of opcache_get_status():

var_dump(opcache_get_status()['jit']);

The output should be something like this:

array:7 [
  "enabled" => true
  "on" => true
  "kind" => 5
  "opt_level" => 4
  "opt_flags" => 6
  "buffer_size" => 4080
  "buffer_free" => 0
]

If enabled and on are true, you're good to go!

Next, there's several ways to configure the JIT (and this is where we'll get into the configuration mess). You can configure when the JIT should run, how much it should try to optimise, etc. All of these options are configured using a single (!) config entry: opcache.jit. It could look something like this:

opcache.enable=1 
opcache.jit=1255

Now, what does that number mean? The RFC lists the meaning of each of them. Mind you: this is not a bit mask, each number simply represents another configuration option. The RFC lists the following options:

# O — Optimization level

0 don't JIT
1 minimal JIT (call standard VM handlers)
2 selective VM handler inlining
3 optimized JIT based on static type inference of individual function
4 optimized JIT based on static type inference and call tree
5 optimized JIT based on static type inference and inner procedure analyses

# T — JIT trigger

0 JIT all functions on first script load
1 JIT function on first execution
2 Profile on first request and compile hot functions on second request
3 Profile on the fly and compile hot functions
4 Compile functions with @jit tag in doc-comments
5 Tracing JIT

# R — register allocation

0 don't perform register allocation
1 use local liner-scan register allocator
2 use global liner-scan register allocator

# C — CPU specific optimization flags

0 none
1 enable AVX instruction generation

One small gotcha: the RFC lists these options in reverse order, so the first digit represents the C value, the second the R, and so on. Why there simply weren't four configuration entries added is beyond my comprehension, probably to make configuring the JIT faster… right?

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

Anyways, internals propose 1255 as the best default, it will do maximum jitting, use the tracing JIT, use a global liner-scan register allocator — whatever that might be — and enables AVX instruction generation.

So your ini settings (or -d flags) should have these values:

opcache.enable=1 
opcache.jit_buffer_size=100M
opcache.jit=1255

Keep in mind that opcache.jit is optional by the way. The JIT will use a default value if that property is omitted.

Which default, you ask? That would be opcache.jit=tracing.

Hang on, that's not the strange bitmask-like structure we saw earlier? That's right: after the original RFC passed, internals recognised that the bitmask-like options weren't all that user-friendly, so they added two aliases which are translated to the bitmask under the hood. There's opcache.jit=tracing and opcache.jit=function.

The difference between the two is that the function JIT will only try to optimise code within the scope of a single function, while the tracing JIT can look at the whole stack trace to identify and optimise hot code. Internals recommends to use the tracing JIT, because it'll probably almost always give the best results. You can read about those results in the benchmarks I've done.

So the only option you actually need to set to enable the JIT with its optimal configuration is opcache.jit_buffer_size, but if you want to be explicit, listing opcache.jit wouldn't be such a bad idea:

opcache.enable=1 
opcache.jit_buffer_size=100M
opcache.jit=tracing

]]>
2020-10-29T00:00:00+00:00
<![CDATA[ Front Line PHP ]]> https://www.stitcher.io/blog/front-line-php I've been blogging for three years and a half: I've written about PHP, the web, programming in general, and here and there something completely off topic. When I started this project I never imagined it would be received so well by so many. But here we are: thousands of people visit my blog every month, with a total of over 2 million pageviews in the span of three years. I realise those numbers are dwarfed by many other websites, still to me it feels like a great achievement.

The last year and a half I've been focussing on more specific topics: the "What's new in PHP" series has been really popular, and the in-depth PHP feature posts have sparked many great discussions and conversations. With those PHP-related topics, I always envisioned them to be part of a greater whole and not separate posts. I've been trying to tell the story of modern PHP for a while now, and I'm trying to make it as focussed and of the highest quality as possible.

So that's why I'm introducing the next chapter, or better said: a bundle of chapters. I'm working on a book that teaches you modern PHP, its best practices and the community surrounding it — it's called "Front Line PHP". Some parts of it will be based on what I've written throughout the years on this blog, but large parts will be brand new. I'll cover the language itself, patterns and principles, frameworks, and most importantly: the mindset of a professional web developer. I'm really looking forward being able to share everything I want as a unified whole.

Like my previous book, I'm working together with Spatie — the company I work at — to ensure it'll be the greatest product possible. You can expect Front Line PHP to launch in December 2020, right around the time PHP 8 arrives. It'll be accompanied by a free video series, teaching you all about PHP 8 in practice.

I'm really looking forward to sharing all of this with you, so make sure you subscribe to the dedicated newsletter to receive all relevant updates. If you want to know a little more, head over to the Front Line PHP website, which will receive regular updates over the coming weeks. I hope you're as excited as I am. If you have any questions or remarks, you're of course free to reach out to me via Twitter or e-mail.

Thanks!

]]>
2020-10-05T00:00:00+00:00
<![CDATA[ What a good PR looks like ]]> https://www.stitcher.io/blog/what-a-good-pr-looks-like It's Hacktoberfest, your chance to contribute to open source and win a t-shirt while at it! Being an open source maintainer myself, I find it a great initiative, but it comes with responsibility as well. Many people try to be smart and send low-effort, borderline-spam PRs, essentially ruining a maintainer's day.

So to help maintainers and contributors in creating quality open source, here are a few tips!

# Start by looking at existing issues and PRs

Even though most PRs are well meant, it's important to first look around the repo for existing issues or PRs addressing the same problem. Someone else might already be working on a similar PR or there might be issues preventing the PR from being made. If you want to help out, start by participating in the discussions already happening, instead of working on your own.

# Discuss first

If nothing has been said about your feature, it might be a good idea to discuss it first, instead of going ahead and potentially changing hundreds of lines of code. I'll be the one who has to maintain your PR for the foreseeable future, so it's best to first discuss it with me! I can probably give you some tips about the code base, as well as tell you about our expectations regarding implementation.

You can choose to open an issue first, or submit a draft PR with a minimal implementation, a proof of concept. I actually really like those draft PRs: it visualises what you want to change, but also indicates that you realise there's more work to do.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Follow the style guide

Speaking of expectations, check whether the repository you're committing to has any code style guide or linter set up. Follow those rules. Don't submit a PR that deliberately uses other styling rules. You're submitting code to another codebase, and that style guide has to be followed.

A note to maintainers: you can help out here by using GitHub actions to automatically run your linters on PRs.

# Document your PR

I'm going to review your code, so I'd like it to be as clear as possible. This starts by writing clean code: using proper variable names, maybe even add a doc block here and there; but it's equally important to explain your thought process as well. You could use review comments to clarify specific parts of the code, or you could add some general comments in the PR.

# Clean commits

Please try to avoid "wip" commit messages, please? You don't have to write a book for every commit, and I realise that you might not want to think about them while in the zone; but something like "Refactor XX to YY" is already infinitely better than "wip". If you really want a good commit message, try to have your commits only do one thing, and try to explain why this commit is necessary.

# Only relevant changes

Only submit changes that are relevant within the scope of the PR. You might be tempted to fix another thing or two while at it — and you're allowed to — but keep those changes in a separate PR.

You can always base a new branch on your PR branch if necessary, and mention in the second PR that it depends on the first one to be merged. I'm happy to merge them for you, in the right order.

# Be patient

It might take a while for maintainers to merge your PR. I even confess to have lost track of a few PRs over the past years. A friendly "bump" after a few days is always appreciated, but it still might take some time. Remember that most OSS maintainers are doing this on a voluntary basis, so don't be mad when a PR takes a little longer to merge.

# Be friendly

This goes for both maintainers and contributors: don't be a jerk. Sometimes a PR gets declined, sometimes a maintainers looses their patience. Stop and breath, realise it's not the end of the world and move on. Be friendly and respectful.

# Don't wait too long to tag

This one is for the maintainers: one of the most frustrating things is a PR getting accepted, and than having to wait another month for a release to be tagged. You shouldn't be afraid of high version numbers, that's what semver is for. Tag the release as soon as possible, and please don't wait another week!


Do you have any more tips? Let them know via Twitter or email. Here's already another good read by my Colleague Sebastian, on how to write a good issue.

]]>
2020-10-02T00:00:00+00:00
<![CDATA[ Which colour scheme is better? ]]> https://www.stitcher.io/blog/why-light-themes-are-better-according-to-science As a programmer I think I should always critically look at my own toolset and try to optimise it, regardless of my own subjective preference. It's by doing so that I've come to the conclusion that light colour schemes are better than dark ones, and today I want to share those thoughts with you.

Before looking at theory, grab a pair of sunglasses if you have any laying around. With both eyes open, cover only one eye with one of the sunglass glasses. Make it so you're looking through your sunglasses with one eye, and use the other one like you're used to.

With that setup in place, have fun watching this video in 3D!

Did you see the 3D effect? It might not work as well for parts of the video and some of you might not notice the 3D effect at all. That's fine, this post isn't about 3D, but it is about the reason why you can watch that video in 3D with only a pair of sunglasses: the Pulfrich effect.

The Pulfrich effect

The Pulfrich effect is a psychophysical percept wherein lateral motion of an object in the field of view is interpreted by the visual cortex as having a depth component, due to a relative difference in signal timings between the two eyes.

All sources are listed in the footnotes by the way, you'll find them at the end of this post.

To clarify, the 3D effect in the video is indeed your brain tricking you; it thinks there's depth in a moving flat image because there's a slight difference in timing between your left and right eye. What's interesting though is what causes that timing difference. Can you guess? It's because you covered one eye with sunglasses, making the image darker. It turns out that dark images take longer to process than light ones.

The Pulfrich effect […] yields about a 15 ms delay for a factor of ten difference in average retinal illuminance

By only covering one eye with sunglasses, you add a few milliseconds of delay to that one. The exact delay will depend on the brightness of the screen and the darkness of the sunglasses, which might explain why some people see the 3D effect better than others. The timing difference between your eyes causes your brain to interpret that image as having depth, hence 3D.

Now on to programming. If you're using a dark colour scheme, you're deliberately adding extra delay, so says the Pulfrich effect. Sure the difference seems negligible, it's only a few milliseconds. Actually, it's a few milliseconds every time you "rescan" your screen; that's between 10 or 50 times a second, depending on what research you want to believe. Still you probably won't notice any real-time difference, but over time this adds up, and the extra effort needed by your eyes can become to feel exhausting.

Besides the Pulfrich effect, there are other reasons that make light colour schemes superior. First of there's what human eyes are used to, what they are built for. Most of us are awake during the day and asleep at night. The human eye is better adapted to interpreting light scenes with dark points of focus, instead of the other way around.

On the other hand there's the case of astigmatism, which is caused by an imperfection of your corneas or lenses. It's estimated that between 30% and 60% of adults in Europe and Asia have it (I actually have it myself, which is why I wear glasses). For people with astigmatism, a bright display with dark text is easier to read, because the iris closes a little more given the additional light; which decreases the impact of the defect in your cornea or lens.

As a sidenote: if you often experience headaches after a day of programming, you might want to test for astigmatism. Glasses make a world of difference

Lastly, there have been extensive studies about the readability of computer screens, one example is a study by Etienne Grandjean, called "Ergonomic Aspects of Visual Display Terminals". You can't read it online; if you manage to find it in a library you should check out pages 137-142. Its conclusion, like several other studies is that it's indeed easier to read dark text on a light background, then the other way around.

Often when I share these arguments with someone who clings to the dark side, they tell me light colour schemes hurt their eyes because they are too bright; you might be thinking the same right now. I've got two answers for you.

First: you don't need to use a white #fff background with black #000 text. There's lots of light colour schemes that don't go to the extreme ends. The important thing is that there's enough contrast between fore- and background, and that the background is lighter than the foreground. Second: you can always adjust the brightness of your screen. You don't need to turn it up to a 100%! You'd only do that if the text is otherwise unreadable, and guess when that happens? If you'd use a dark scheme!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

I don't want to end with theory though. Over the past three years, I've put light themes to the test: I've challenged myself and dozens of others to switch to a light theme for one week. I wanna do the same with you: try it for one week, and let me know whether you're switching back to a dark theme or not. Based on my past experiments I can tell you that only a few people decide to switch back. The majority stays with a light scheme because, guess what, it's actually better.

Now I reckon there are people who can't use a light colour scheme because of an eye illness. There are legitimate cases when dark colour schemes are better for some people's health, the exceptions to the rule.

So try it out, and let me know your findings via Twitter or e-mail!

PS: if you're using PhpStorm or any other JetBrain IDE, you can check these two light colour schemes:

]]>
2020-09-26T00:00:00+00:00
<![CDATA[ The case for transpiled generics in PHP ]]> https://www.stitcher.io/blog/the-case-for-transpiled-generics

This is a mail I sent to PHP's internals, these are my thoughts, and you can follow the internals discussion here, or share your own thoughts on Reddit.


Hello internals

Today I'd like to hear your thoughts on what might be a controversial topic, though I think it's worth having this discussion. I want to make the case for adding generic syntax, without actually enforcing any additional type checks at runtime. Please hear me out.

We've been discussing generics for years now [1][2], all without any result. Nikita's latest attempt [3] stalled because, from what I gathered and amongst other things, doing generic type checks at runtime has a significant impact on performance.

On the other hand, static analysers have been making their rise for a few years now. Granted: not the whole community might like this kind of type strictness, and PHP doesn't force them to; but still projects like PhpStorm acknowledge their significance — they will add built-in support for both psalm and PHPStan later this year [4]. Rasmus Lerdorf also showed interest in the idea of improving PHP's static analysis capabilities two years ago [5].

That all to say that there's a significant part of the PHP community who's interested in embracing the benefits of static analysis.

If we look outside of our PHP bubble, we can see the same thing happening in JavaScript: the core benefit that TypeScript adds is its robust static analysis. Sure those developers need an extra compilation step to transpile their code to plain old JavaScript, but it seems that they are… fine with that?

I'd like to discuss a similar idea for PHP. If runtime generics aren't possible because of performance issues, why not explore the other option: adding generic syntax that is ignored by the interpreter, but can be used by static analysis tools — third party of built-into PHP, that's another discussion. I realise this thought goes against the "PHP mindset" we've been programming with for more than 20 years, but we shouldn't ignore what's happening in the PHP- and wider programming community: static analysis is relevant, whether you want to use it or not, and a stricter type system is preferred by many.

Now I know there are alternatives we can use today. Static analysers already support generics, using doc blocks. I'm not trying to argue that it's impossible to achieve the same results with the toolset we have, but rather that there's room for improvement from the developer's point of view. History has shown that such convenience additions to PHP have been a difficult pill to swallow for some, but on the other hand those kind of changes have been happening more and more often anyway: property promotion, short closures, named arguments, attributes, yes even types themselves: you can write the same working PHP program without any of those features, and yet they have been proven so useful and wanted over the last years.

As a sidenote: the idea of transpiling is already present in PHP. Looking at constructor property promotion: a purely syntactical feature, which is transformed to simpler PHP code at runtime. Nikita called this principle "desugaring" in the constructor property promotion RFC [6].

So here's my case for transpiled generics summarized:

  • There's no significant runtime performance impact
  • The PHP community is already embracing static analysis
  • Transpiling has been proved to be a viable workflow, thanks to TypeScript
  • As with all things-PHP: it's opt-in. You don't have to use the syntax if you don't want to and you won't experience any downsides

So with all that being said, I'm looking forward to hearing your thoughts.

Kind regards
Brent

]]>
2020-09-17T00:00:00+00:00
<![CDATA[ Don't get stuck ]]> https://www.stitcher.io/blog/dont-get-stuck Note: this post is about a previous job, not my current one.

Both managers were looking at me in disbelief, they seemed to be stunned for a few seconds. I just told them I had decided to resign. One of them quickly recovered, smiled, and said he regretted my decision; though he also realised it's expected in our line of work: people usually don't spend more than a few years at the same company.

I didn't want to chit-chat much longer, so I nodded, apologised (I'm not sure why), and told them I'd be checking out the paperwork with the office manager later that week. I left the room, went back to my desk.

I received a mail from the other manager over the weekend, the one who didn't say much during our conversation. He wrote he was perplexed, didn't see this coming, and regretted the decision tremendously. He told me if there was anything he could do to change my mind, I should tell him.

My decision to leave was final though.

Mind you, it wasn't a financial one; in fact I was just about to get a significant raise and more responsibilities. It also wasn't a relational one, I really appreciated all of my colleagues back then; I'd even call some of them good friends.

I decided to leave because I got stuck and there wasn't any room to grow anymore. The perspective of being a developer who's 5 years behind of modern day practices made me miserable.

Even though I'd been advocating within the company to make significant changes, both on a technical level, as well as on the management side; it didn't seem achievable. We were still struggling to deliver the quality we promised our clients, we were using out of date technologies, I went home feeling down and depressed almost every day.

I wasn't the only one, by the way. During the 3 years I worked for this company, 8 out of 25 people left, and around the same amount were hired; always young developers, straight from school, just like I three years before. Every time this happened, it was described as "the normal flow" a web development company has to deal with, just like our manager told me during my resignation.

I've since realised that this manager was wrong: it's not normal to switch jobs every 5 years, to have a third of your company come and go, and be replaced with inexperienced developers. And mind you: there should always be room for those juniors to grow, but who will teach them if most of the experienced developers left?

No, these kinds of things are only "normal" when your company fails to invest in people the way it should. And that, it turns out, is very common indeed.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

I don't regret having worked for that company: I did learn valuable lessons there. It's ok to be at a place where there's little or no room for growth, as long as it's not too long. You've got to watch out, and critically assess your situation from time to time; you might get stuck without even knowing it.

My advice? Either try to change your position and responsibilities within the company or, if that doesn't work, change jobs. I realise that's easy to write, like I also realise it's not as easy as it sounds. Personally, I noticed being stuck after two years and it took me another whole year to find a place where I believed there was enough room to grow.

Whatever turns your path takes, knowing you want to go somewhere and not stand idle is the most important first step.

Thanks for reading! This post is part of my "Dev Diaries" series where I write about my own and personal experiences as a developer. Would you like to read some more?

If you want to stay up to date about what's happening on this blog, you can follow me on Twitter or subscribe to my newsletter:

]]>
2020-08-29T00:00:00+00:00
<![CDATA[ Annotations ]]> https://www.stitcher.io/blog/annotations

Show large image

]]>
2020-08-28T00:00:00+00:00
<![CDATA[ Differences ]]> https://www.stitcher.io/blog/differences

Show large image

]]>
2020-08-21T00:00:00+00:00
<![CDATA[ My journey into event sourcing ]]> https://www.stitcher.io/blog/my-journey-into-event-sourcing In this post I want to share four talks that have guided me into the world of event driven development, and by extent into event sourcing.

I wanted to share these talks here on my blog, because I figured some of you might be interested in them, and this way I can revisit them in the future.


Starting with Martin Fowler, who explains the basics of event driven development, the pros and cons, as well as the different patterns that can be applied on top of EDD, one of them event sourcing.


Next Greg Young, one of the founding fathers of event sourcing and CQRS. What's most interesting in this talk is the misconceptions Greg talks about. The one that stood out to me most, is that event sourcing isn't a top-level architecture, it's a pattern that should be applied in parts of your projects where relevant. A great insight, one that has guided us throughout our latest project.


Next up an old talk be Eric Evans. I know the video and sound quality is crap, but the way he talks about splitting large systems in small pieces is awesome.

The greatest insight for me is how he explains the concept of micro services within the context of one large system. Eric explains concrete ways of dealing with such a split system, which directly ties in the point made by Greg Young earlier: event sourcing should only be applied in parts of your system. Eric gives us concrete strategies of doing that.


Finally, putting everything into practice with code: Freek shows a hands-on integration of event sourcing into a Laravel projects, the framework I also work with daily.


Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-07-25T00:00:00+00:00
<![CDATA[ PHP 8: named arguments ]]> https://www.stitcher.io/blog/php-8-named-arguments It was a close call, but named arguments — also called named parameters — are supported in PHP 8! In this post I'll discuss their ins and outs, but let me show you first what they look like with a few examples in the wild:

setcookie(
    name: 'test',
    expires: time() + 60 * 60 * 2,
);

Named arguments used on a built-in PHP function

class CustomerData
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}
}

$data = new CustomerData(
    name: $input['name'],
    email: $input['email'],
    age: $input['age'],
);

A DTO making use of promoted properties, as well as named arguments

$data = new CustomerData(...$customerRequest->validated());

Named arguments also support array spreading

You might have guessed it from the examples: named arguments allow you to pass input data into a function, based on their argument name instead of the argument order.

I would argue named arguments are a great feature that will have a significant impact on my day-to-day programming life. You're probably wondering about the details though: what if you pass a wrong name, what's up with that array spreading syntax? Well, let's look at all those questions in-depth.

# Why named arguments?

Let's say this feature was a highly debated one, and there were some counter arguments to not adding them. However, I'd say their benefit far outweigh the fear of backwards compatibility problems or bloated APIs. The way I see it, they will allow us to write cleaner and more flexible code.

For one, named arguments allow you to skip default values. Take a look again at the cookie example:

setcookie(
    name: 'test',
    expires: time() + 60 * 60 * 2,
);

Its method signature is actually the following:

setcookie ( 
    string $name, 
    string $value = "", 
    int $expires = 0, 
    string $path = "", 
    string $domain = "", 
    bool $secure = false, 
    bool $httponly = false,
) : bool

In the example I showed, we didn't need to set the a cookie $value, but we did need to set an expiration time. Named arguments made this method call a little more concise:

setcookie(
    'test',
    '',
    time() + 60 * 60 * 2,
);

setcookie without named arguments

setcookie(
    name: 'test',
    expires: time() + 60 * 60 * 2,
);

setcookie with named arguments

Besides skipping arguments with default values, there's also the benefit of having clarity about which variable does what; something that's especially useful in functions with large method signatures. Now we could say that lots of arguments are usually a code smell; we still have to deal with them no matter what, so it's better to have a sane way of doing so, than nothing at all.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Named arguments in depth

With the basics out of the way, let's look at what named arguments can and can't do.

First of all, named arguments can be combined with unnamed — also called ordered — arguments. In that case the ordered arguments must always come first.

Take our DTO example from before:

class CustomerData
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}
}

You could construct it like so:

$data = new CustomerData(
    $input['name'],
    age: $input['age'],
    email: $input['email'],
);

However, having an ordered argument after a named one would throw an error:

$data = new CustomerData(
    age: $input['age'],
    $input['name'],
    email: $input['email'],
);

Next, it's possible to use array spreading in combination with named arguments:

$input = [
    'age' => 25,
    'name' => 'Brent',
    'email' => 'brent@stitcher.io',
];

$data = new CustomerData(...$input);

If, however, there are missing required entries in the array, or if there's a key that's not listed as a named argument, an error will be thrown:

$input = [
    'age' => 25,
    'name' => 'Brent',
    'email' => 'brent@stitcher.io',
    'unknownProperty' => 'This is not allowed',
];

$data = new CustomerData(...$input);

It is possible to combine named and ordered arguments in an input array, but only if the ordered arguments follow the same rule as before: they must come first!

$input = [
    'Brent',
    'age' => 25,
    'email' => 'brent@stitcher.io',
];

$data = new CustomerData(...$input);


If you're using variadic functions, named arguments will be passed with their key name into the variadic arguments array. Take the following example:

class CustomerData
{
    public static function new(...$args): self
    {
        return new self(...$args);
    }

    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}
}

$data = CustomerData::new(
    email: 'brent@stitcher.io',
    age: 25,
    name: 'Brent',
);

In this case, $args in CustomerData::new will contain the following data:

[
    'age' => 25,
    'email' => 'brent@stitcher.io',
    'name' => 'Brent',
]

Attributes — also known as annotations — also support named arguments:

class ProductSubscriber
{
    #[ListensTo(event: ProductCreated::class)]
    public function onProductCreated(ProductCreated $event) { /* … */ }
}

It's not possible to have a variable as the argument name:

$field = 'age';

$data = CustomerData::new(
    $field: 25,
);

And finally, named arguments will deal in a pragmatic way with name changes during inheritance. Take this example:

interface EventListener {
    public function on($event, $handler);
}

class MyListener implements EventListener
{
    public function on($myEvent, $myHandler)
    {
        // …
    }
}

PHP will silently allow changing the name of $event to $myEvent, and $handler to $myHandler; but if you decide to use named arguments using the parent's name, it will result in a runtime error:

public function register(EventListener $listener)
{
    $listener->on(
        event: $this->event,
        handler: $this->handler, 
    );
}

Runtime error in case $listener is an instance of MyListener

This pragmatic approach was chosen to prevent a major breaking change when all inherited arguments would have to keep the same name. Seems like a good solution to me.


That's most there is to tell about named arguments. If you want to know a little more backstory behind some design decisions, I'd encourage you to read the RFC.

Are you looking forward to using named arguments? Let me know via Twitter or via e-mail!

]]>
2020-07-23T00:00:00+00:00
<![CDATA[ PHP 8: before and after ]]> https://www.stitcher.io/blog/php-8-before-and-after It's only a few months before PHP 8 will be released, and honestly there are so many good features. In this post I want to share the real-life impact that PHP 8 will have on my own code.

# Events subscribers with attributes

I'm going to try not to abuse attributes, but I think configuring event listeners is an example of an annotation I'll be using extensively.

You might know that I've been working on event sourced systems lately, and I can tell you: there's lots of event configuration to do. Take this simple projector, for example:

// Before

class CartsProjector implements Projector
{
    use ProjectsEvents;

    protected array $handlesEvents = [
        CartStartedEvent::class => 'onCartStarted',
        CartItemAddedEvent::class => 'onCartItemAdded',
        CartItemRemovedEvent::class => 'onCartItemRemoved',
        CartExpiredEvent::class => 'onCartExpired',
        CartCheckedOutEvent::class => 'onCartCheckedOut',
        CouponAddedToCartItemEvent::class => 'onCouponAddedToCartItem',
    ];

    public function onCartStarted(CartStartedEvent $event): void
    { /* … */ }

    public function onCartItemAdded(CartItemAddedEvent $event): void
    { /* … */ }

    public function onCartItemRemoved(CartItemRemovedEvent $event): void
    { /* … */ }

    public function onCartCheckedOut(CartCheckedOutEvent $event): void
    { /* … */ }

    public function onCartExpired(CartExpiredEvent $event): void
    { /* … */ }

    public function onCouponAddedToCartItem(CouponAddedToCartItemEvent $event): void
    { /* … */ }
}

PHP 7.4

There are two benefits attributes will give me:

  • Event listener configuration and handlers are put together, I don't have to scroll to the top of the file to know whether a listener is configured correctly.
  • I don't have to bother anymore writing and managing method names as strings: your IDE can't autocomplete them, there's no static analysis on typos and method renaming doesn't work.

Luckily, PHP 8 solves these problems:

class CartsProjector implements Projector
{
    use ProjectsEvents;

    #[SubscribesTo(CartStartedEvent::class)]
    public function onCartStarted(CartStartedEvent $event): void
    { /* … */ }

    #[SubscribesTo(CartItemAddedEvent::class)]
    public function onCartItemAdded(CartItemAddedEvent $event): void
    { /* … */ }

    #[SubscribesTo(CartItemRemovedEvent::class)]
    public function onCartItemRemoved(CartItemRemovedEvent $event): void
    { /* … */ }

    #[SubscribesTo(CartCheckedOutEvent::class)]
    public function onCartCheckedOut(CartCheckedOutEvent $event): void
    { /* … */ }

    #[SubscribesTo(CartExpiredEvent::class)]
    public function onCartExpired(CartExpiredEvent $event): void
    { /* … */ }

    #[SubscribesTo(CouponAddedToCartItemEvent::class)]
    public function onCouponAddedToCartItem(CouponAddedToCartItemEvent $event): void
    { /* … */ }
}

PHP 8

# Static instead of doc blocks

A smaller one, but this one will have a day-by-day impact. I often find myself still needing doc blocks because of two things: static return types and generics. The latter one can't be solved yet, but luckily the first one will in PHP 8!

When I'd write this in PHP 7.4:

/**
 * @return static
 */
public static function new()
{
    return new static();
}

PHP 7.4

I'll now be able to write:

public static function new(): static
{
    return new static();
}

PHP 8

# DTO's, property promotion and named arguments

If you read my blog, you know I wrote quite a bit about the use of PHP's type system combined with data transfer objects. Naturally, I use lots of DTOs in my own code, so you can imagine how happy I am, being able to rewrite this:

class CustomerData extends DataTransferObject
{
    public string $name;

    public string $email;

    public int $age;
    
    public static function fromRequest(
        CustomerRequest $request
    ): self {
        return new self([
            'name' => $request->get('name'),
            'email' => $request->get('email'),
            'age' => $request->get('age'),
        ]);
    }
}

$data = CustomerData::fromRequest($customerRequest);

PHP 7.4

As this:

class CustomerData
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}
}

$data = new CustomerData(...$customerRequest->validated());

PHP 8

Note the use of both constructor property promotion, as well as named arguments. Yes, they can be passed using named arrays and the spread operator!

# Enums and the match expression

Do you sometimes find yourself using an enum with some methods on it, that will give a different result based on the enum value?

/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        return [
            self::PENDING => 'orange',
            self::PAID => 'green',
        ][$this->value] ?? 'gray';   
    }
}

PHP 7.4

I would argue that for more complex conditions, you're better off using the state pattern, yet there are cases where an enum does suffice. This weird array syntax already is a shorthand for a more verbose conditional:

/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        if ($this->value === self::PENDING) {
            return 'orange';
        }
    
        if ($this->value === self::PAID) {
            return 'green'
        }

        return 'gray';
    }
}

PHP 7.4 — alternative

But with PHP 8, we can use the match expression instead!

/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        return match ($this->value) {
            self::PENDING => 'orange',
            self::PAID => 'green',
            default => 'gray',
        };
    }
}

PHP 8

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Union types instead of doc blocks

When I mentioned the static return type before, I forgot another use case where docblock type hints were required: union types. At least, they were required before, because PHP 8 supports them natively!

/**
 * @param string|int $input
 *
 * @return string 
 */
public function sanitize($input): string;

PHP 7.4

public function sanitize(string|int $input): string;

PHP 8

# Throw expressions

Before PHP 8, you couldn't use throw in an expression, meaning you'd have to do explicit checks like so:

public function (array $input): void
{
    if (! isset($input['bar'])) {
        throw BarIsMissing::new();
    }
    
    $bar = $input['bar'];

    // …
}

PHP 7.4

In PHP 8, throw has become an expression, meaning you can use it like so:

public function (array $input): void
{
    $bar = $input['bar'] ?? throw BarIsMissing::new();

    // …
}

PHP 8

# The nullsafe operator

If you're familiar with the null coalescing operator you're already familiar with its shortcomings: it doesn't work on method calls. Instead you need intermediate checks, or rely on optional helpers provided by some frameworks:

$startDate = $booking->getStartDate();

$dateAsString = $startDate ? $startDate->asDateTimeString() : null;

PHP 7.4

With the addition of the nullsafe operator, we can now have null coalescing-like behaviour on methods!

$dateAsString = $booking->getStartDate()?->asDateTimeString();

PHP 8

What's your favourite PHP 8 feature? Let me know via Twitter or via e-mail!

]]>
2020-07-20T00:00:00+00:00
<![CDATA[ Shorthand comparisons in PHP ]]> https://www.stitcher.io/blog/shorthand-comparisons-in-php You probably already know some comparison operators in PHP. Things like the ternary ?:, the null coalescing ?? and the spaceship <=> operators. But do you really know how they work? Understanding these operators makes you use them more, resulting in a cleaner codebase.

Before looking at each operator in depth, here's a summary of what each of them does:

# Ternary operator

The ternary operator is a shorthand for the if {} else {} structure. Instead of writing this:

if ($condition) {
    $result = 'foo' 
} else {
    $result = 'bar'
}

You can write this:

$result = $condition ? 'foo' : 'bar';

If this $condition evaluates to true, the lefthand operand will be assigned to $result. If the condition evaluates to false, the righthand will be used.

Interesting fact: the name ternary operator actually means "an operator which acts on three operands". An operand is the term used to denote the parts needed by an expression. The ternary operator is the only operator in PHP which requires three operands: the condition, the true and the false result. Similarly, there are also binary and unary operators. You can read more about it here.

Back to ternary operators: do you know which expressions evaluate to true, and which don't? Take a look at the boolean column of this table.

The ternary operator will use its lefthand operand when the condition evaluates to true. This could be a string, an integer, a boolean etc. The righthand operand will be used for so called "falsy values".

Examples would be 0 or '0', an empty array or string, null, an undefined or unassigned variable, and of course false itself. All these values will make the ternary operator use its righthand operand.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Shorthand ternary operator

Since PHP 5.3, it's possible to leave out the lefthand operand, allowing for even shorter expressions:

$result = $initial ?: 'default';

In this case, the value of $result will be the value of $initial, unless $initial evaluates to false, in which case the string 'default' is used.

You could write this expression the same way using the normal ternary operator:

$result = $condition ? $condition : 'default';

Ironically, by leaving out the second operand of the ternary operator, it actually becomes a binary operator.

# Chaining ternary operators

The following, even though it seems logical; doesn't work in PHP:

$result = $firstCondition
    ? 'truth'
    : $elseCondition
        ? 'elseTrue'
        : 'elseFalse';

The reason because is that the ternary operator in PHP is left-associative, and thus parsed in a very strange way. The above example would always evaluate the $elseCondition part first, so even when $firstCondition would be true, you'd never see its output.

I believe the right thing to do is to avoid nested ternary operators altogether. You can read more about this strange behaviour in this Stack Overflow answer.

Furthermore, as PHP 7.4, the use of chained ternaries without brackets is deprecated.

# Null coalescing operator

Did you take a look at the types comparison table earlier? The null coalescing operator is available since PHP 7.0. It similar to the ternary operator, but will behave like isset on the lefthand operand instead of just using its boolean value. This makes this operator especially useful for arrays and assigning defaults when a variable is not set.

$undefined ?? 'fallback'; // 'fallback'

$unassigned;
$unassigned ?? 'fallback'; // 'fallback'

$assigned = 'foo';
$assigned ?? 'fallback'; // 'foo'

'' ?? 'fallback'; // ''
'foo' ?? 'fallback'; // 'foo'
'0' ?? 'fallback'; // '0'
0 ?? 'fallback'; // 0
false ?? 'fallback'; // false

The null coalescing operator takes two operands, making it a binary operator. "Coalescing" by the way, means "coming together to form one mass or whole". It will take two operands, and decide which of those to use based on the value of the lefthand operand.

# Null coalescing on arrays

This operator is especially useful in combination with arrays, because of its acts like isset. This means you can quickly check for the existence of keys, even nested keys, without writing verbose expressions.

$input = [
    'key' => 'key',
    'nested' => [
        'key' => true
    ]
];

$input['key'] ?? 'fallback'; // 'key'
$input['nested']['key'] ?? 'fallback'; // true
$input['undefined'] ?? 'fallback'; // 'fallback'
$input['nested']['undefined'] ?? 'fallback'; // 'fallback'

null ?? 'fallback'; // 'fallback'

The first example could also be written using a ternary operator:

$output = isset($input['key']) ? $input['key'] : 'fallback';

Note that it's impossible to use the shorthand ternary operator when checking the existence of array keys. It will either trigger an error or return a boolean, instead of the real lefthand operand's value.

// Returns `true` instead of the value of `$input['key']`
$output = isset($input['key']) ?: 'fallback' 

// The following will trigger an 'undefined index' notice 
// when $input is no array or has no 'key'.
//
// It will trigger an 'undefined variable' notice 
// when $input doesn't exist.
$output = $input['key'] ?: 'fallback';

# Null coalesce chaining

The null coalescing operator can easily be chained:

$input = [
    'key' => 'key',
];

$input['undefined'] ?? $input['key'] ?? 'fallback'; // 'key'

# Nested coalescing

It's possible to use the null coalescing operator on nested object properties, even when a property in the chain is null.

$a = (object) [
    'prop' => null,
];

var_dump($a->prop->b ?? 'empty');

// 'empty'

# Null coalescing assignment operator

In PHP 7,4, we can expect an even shorter syntax called the "null coalescing assignment operator".

// This operator will be available in PHP 7.4

function (array $parameters = []) {
    $parameters['property'] ??= 'default';
}

In this example, $parameters['property'] will be set to 'default', unless it is set in the array passed to the function. This would be equivalent to the following, using the current null coalescing operator:

function (array $parameters = []) {
    $parameters['property'] = $parameters['property'] ?? 'default';
}

# Spaceship operator

The spaceship operator, while having quite a peculiar name, can be very useful. It's an operator used for comparison. It will always return one of three values: 0, -1 or 1.

0 will be returned when both operands are equals, 1 when the left operand is larger, and -1 when the right operand is larger. Let's take a look at a simple example:

1 <=> 2; // Will return -1, as 2 is larger than 1.

This simple example isn't all that exiting, right? However, the spaceship operator can compare a lot more than simple values!

// It can compare strings,
'a' <=> 'z'; // -1

// and arrays,
[2, 1] <=> [2, 1]; // 0

// nested arrays,
[[1, 2], [2, 2]] <=> [[1, 2], [1, 2]]; // 1

// and even casing.
'Z' <=> 'z'; // -1

Strangely enough, when comparing letter casing, the lowercase letter is considered the highest. There's a simple explanation though. String comparison is done by comparing character per character. As soon as a character differs, their ASCII value is compared. Because lowercase letters come after uppercase ones in the ASCII table, they have a higher value.

# Comparing objects

The spaceship operator can almost compare anything, even objects. The way objects are compared is based on the kind of object. Built-in PHP classes can define their own comparison, while userland objects are compared based on their attributes and values.

When would you want to compare objects you ask? Well, there's actually a very obvious example: dates.

$dateA = DateTime::createFromFormat('Y-m-d', '2000-02-01');

$dateB = DateTime::createFromFormat('Y-m-d', '2000-01-01');

$dateA <=> $dateB; // Returns 1

Of course, comparing dates is just one example, but a very useful one nevertheless.

# Sort functions

One great use for this operator, is to sort arrays. There are quite a few ways to sort an array in PHP, and some of these methods allow a user defined sort function. This function has to compare two elements, and return 1, 0, or -1 based on their position.

An excellent use case for the spaceship operator!

$array = [5, 1, 6, 3];

usort($array, function ($a, $b) {
    return $a <=> $b;
});

// $array = [1, 3, 5, 6];

To sort descending, you can simply invert the comparison result:

usort($array, function ($a, $b) {
    return -($a <=> $b);
});

// $array = [6, 5, 3, 1];

Hi there, thanks for reading! I hope this blog post helped you! If you'd like to contact me, you can do so on Twitter or via e-mail. I always love to chat!

]]>
2020-07-14T00:00:00+00:00
<![CDATA[ Why we need named arguments in PHP ]]> https://www.stitcher.io/blog/why-we-need-named-params-in-php There's a new RFC in town for PHP 8, and its name is the named arguments RFC.

If you're eligible to vote, or know someone who can: I want to ask you to take five minutes to read this, and to be clear up front: I want you to vote yes.

Here's why from the point of view of a userland developer, both for client projects and open source.

# As an OSS maintainer

The main argument against named arguments — the PHP 8 puns continue — is that they would make maintaining open source software a pain: changing the name of an argument would become a breaking change.

Here's what that means. Imagine an open source package which has this function as part of its public API:

public function toMediaCollection(string $collection, string $disk = null);

Named parameters would allow to call this function like so:

$pendingMedia
    ->toMediaCollection(collection: 'downloads', disk: 's3');

If, for some reason, the open source maintainer would want to change the name of the $collection or $disk variables, they would have to tag a major release, because named arguments would make that a breaking change.

Now, let me tell you something from my point of view as an open source maintainer: this rarely happens.

As a matter of fact, I can only think of a handful occurrences. And the only reason we decided to do renames on those occurrences, was because we were already working on a new major version and we figured we might as well improve the naming a little bit while we were at it.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

I'm not the only one with that opinion by the way, Nicolas Grekas is amongst the people voting yes, and he knows a thing or two about OSS development. Oh and here's Mohamed Said, one of the core maintainers of Laravel:

I've been working at Laravel for 4 years now and I rarely find us wanting to change argument names in a refactor. We either add or remove arguments, but I'd say we never had to change names.

Actually that's an interesting thought: argument lists already are prone to breaking backwards compatibility: changing the order of arguments already is a breaking change! And we're dealing with that just fine now, aren't we?

Now even if you, as an open source maintainer, don't want to take the responsibility of making sure argument names don't change between major releases, here's what you do: tell your users you won't actively support named arguments, and using them is at their own risk. Just put this in the README:

**Heads up**: this package doesn't actively support named arguments. 
This means that argument names might change in minor and patch releases. 
You can use them, but at your own risk.

Don't deny all PHP developers this flexibility, because you're afraid of a slight chance it might break something somewhere in the far far future. Don't be afraid.

# As a programmer doing client work

Another argument is that this RFC would encourage bad API design. It would encourage people to write large method definitions, which in turn often indicates a code smell.

I as well can come up with lots of examples that aren't a good fit for named parameters. But that doesn't mean there are no use cases for them at all! Have you heard of data transfer objects or value objects before? If you're following this blog, chances are you have.

I'm not going to copy my writing on them in this post, but I can summarise the main thought behind them: treat data as a first class citizen of your application, model them with objects. For example: an address has a street, number, postal code, city, country, sometimes even more than that. That data should be represented by a strongly typed object in PHP, and not passed between contexts as an array full of random stuff, its constructor would look like this:

class Address
{
    public function __construct(
        string $street,
        string $number,
        string $postal,
        string $city,
        string $country,
    ) { /* … */ }
}

DTOs and VOs are valid cases where these kinds of large constructors are allowed, it's no code smell at all. I had a quick look at an old project of ours, at the time of tracking its stats, it already had 63 DTO classes, and the project was far from finished at that point!

Large constructors happen, and named parameters would not only add more clarity, but also offer the flexibility of changing the parameter order after the fact, without having to worry about fixing the order at all.

Take our Address object, for example. Let's say we need to support number suffixes. We can add that argument without having to worry about the order that other places called it:

class Address
{
    public function __construct(
        string $street,
        string $number,
        string $numberSuffix,
        string $postal,
        string $city,
        string $country,
    ) { /* … */ }
}

Sure the calling site still need to add it, but at least you don't have to worry about micro managing the parameter order anymore.

But what if users decide to use ordered arguments instead? You'd need some way to ensure named arguments are used in these cases. The answer is surprisingly dull: establish conventions with your team, and optionally enforce them with tools like phpcs.

Yes, ideally, we'd want the language to prevent us from any possible misstep; but that simply isn't a realistic expectation. To me, that still isn't an argument for voting against this RFC. I've been working with teams of developers for years now, and project conventions need to be established anyway. They work just fine.

# Dealing with PHP's own legacy

Pop quiz! How to set a cookie without a value, which expires two hours from now?

Did you look up the docs or consult your IDE?

That's fine, it's a confusing function after all. Named arguments can offer a little more clarity though. Compare the two following notations:

setcookie(
    'test', 
    '', 
    time() + 60 * 60 * 2
);

Or:

setcookie(
    name: 'test',
    expires: time() + 60 * 60 * 2,
);

I know which I would pick, everyone benefits from named arguments in this case. And since chances are slim that PHP's internal functions will change anytime soon, it's another very good reason to add them.

# Looking at other languages

Lastly, let's face the simple facts: several other languages — many also focused on web development — already support named arguments. Some deal with them in slightly different ways, but the base concept is known to many other programmers, and it's a good thing.

Here are a few examples:

So let's not spread fear about harder-to-maintain code, or open source software that will become a nightmare to maintain. Named arguments are a known feature in the larger software community, and have proven their worth. No need for hypothetical problems, we will manage.


By the way the vote will pass at this point! You can read about named arguments in depth on this blog!

]]>
2020-07-11T00:00:00+00:00
<![CDATA[ PHP 8: match or switch? ]]> https://www.stitcher.io/blog/php-8-match-or-switch PHP 8 introduces the new match expression. A powerful feature that will often be the better choice to using switch. So what exactly are the differences?

Let's start by comparing the two. Here's a classic switch example:

switch ($statusCode) {
    case 200:
    case 300:
        $message = null;
        break;
    case 400:
        $message = 'not found';
        break;
    case 500:
        $message = 'server error';
        break;
    default:
        $message = 'unknown status code';
        break;
}

Here's its match equivalent:

$message = match ($statusCode) {
    200, 300 => null,
    400 => 'not found',
    500 => 'server error',
    default => 'unknown status code',
};

First of all, the match expression is significantly shorter:

  • it doesn't require a break statement
  • it can combine different arms into one using a comma
  • it returns a value, so you only have to assign value once

That's already quite a lot, but there's even more to it!

# No type coercion

match will do strict type checks instead of loose ones. It's like using === instead of ==. People will probably disagree whether that's a good thing or not, but that's a topic on its own.

$statusCode = '200';

$message = match ($statusCode) {
    200 => null,
    default => 'unknown status code',
};

// $message = 'unknown status code'

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Unknown values cause errors

If you forget to check for a value, and when there's no default arm specified, PHP will throw an UnhandledMatchError exception. Again more strictness, but it will prevent subtle bugs from going unnoticed.

$statusCode = 400;

$message = match ($statusCode) {
    200 => 'perfect',
};

// UnhandledMatchError

# Only single-line expressions, for now

Just like short closures, you can only write one expression. Expression blocks will probably get added at one point, but it's still not clear when exactly.

# Combining conditions

You already noticed the lack of break? This also means match doesn't allow for fallthrough conditions, like the two combined case lines in the first switch example. On the other hand though, you can combine conditions on the same line, separated by commas.

So you have the same functionality as switch in this regards, but with less writing, and less ways to screw up. Win-win!

$message = match ($statusCode) {
    200, 300, 301, 302 => 'combined expressions',
};

# Complex conditions and performance

During the RFC discussion, some people suggested the following pattern as an argument against adding the match expression:

$message = [
    $this->matchesRegex($line) => 'match A',
    $this->matchesOtherRegex($line) => 'match B',
][$line] ?? 'no match';

There's one big caveat though: this technique will execute all regex functions first, decreasing performance. A good argument for match.

# Throwing exceptions

Finally, because of throw expressions in PHP 8, it's also possible to directly throw from an arm, if you'd like to.

$message = match ($statusCode) {
    200 => null,
    500 => throw new ServerError(),
    default => 'unknown status code',
};

# Pattern matching

Ok, there's one more thing: pattern matching. It's a technique used in other programming languages, to allow complexer matching than simple values. Think of it as regex, but for variables instead of text.

Pattern matching isn't supported right now, because it's quite a complex feature, but Ilija Tovilo, the RFC author did mention it as a possible future feature. Something to look out for!

# So, switch or match?

If I'd need to summarise the match expression in one sentence, I'd say it's the stricter and more modern version of it's little switch brother.

There are some cases — see what I did there? — where switch will offer more flexibility, especially with multiline code blocks. However, the strictness of the match operator is appealing, and the perspective of pattern matching would be a game-changer for PHP.

I admit I never wrote a switch statement in the past years because of its many quirks; quirks that match actually solve. So while it's not perfect yet, there are use cases that I can think of, where match would be a good… match.

What's your opinion?

]]>
2020-07-08T00:00:00+00:00
<![CDATA[ PHP 8: JIT performance in real-life web applications ]]> https://www.stitcher.io/blog/jit-in-real-life-web-applications For those interested in the JIT in PHP 8, I did some benchmarks for you in real-world web application scenario. Be aware that these benchmarks don't say anything about whether the JIT is useful or not, they only show whether it can improve the performance of your average web application, or not.

# Setup

Let's set the scene first. These benchmarks were run on my local machine. As so, they don't say anything about absolute performance gains, I'm only interested in making conclusions about the relative impact the JIT has on real-life code.

I'll be running PHP FPM, configured to spawn 20 child processes, and I'll always make sure to only run 20 concurrent requests at once, just to eliminate any extra performance hits on the FPM level. Sending these requests is done using the following command, with ApacheBench:

ab -n 100 -c 20 -l http://aggregate.stitcher.io.test:8081/discover

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# JIT Setup

With the project in place, let's configure the JIT itself. The JIT is enabled by specifying the opcache.jit_buffer_size option in php.ini. If this directive is excluded, the default value is set to 0, and the JIT won't run.

opcache.jit_buffer_size=100M

You'll also want to set a JIT mode, which will determine how the JIT will monitor and react to hot parts of your code. You'll need to use the opcache.jit option. Its default is set to tracing, but you can override it using function:

opcache.jit=function
; opcache.jit=tracing

In our real-life benchmarks, I'll compare both modes with each other. So let's start benchmarking!

# Establishing a baseline

First it's best to establish whether the JIT is working properly or not. We know from the RFC that it does have a significant impact on calculating a fractal. So let's start with that example. I copied the mandelbrot example from the RFC, and accessed it via the same HTTP application I'll run the next benchmarks on:

public function index()
{
    for ($y = -39; $y < 39; $y++) {
        printf("\n");

        for ($x = -39; $x < 39; $x++) {
            $i = $this->mandelbrot(
                $x / 40.0,
                $y / 40.0
            );

            if ($i == 0) {
                printf("*");
            } else {
                printf(" ");
            }
        }
    }

    printf("\n");
}

private function mandelbrot($x, $y)
{
    $cr = $y - 0.5;
    $ci = $x;
    $zi = 0.0;
    $zr = 0.0;
    $i = 0;

    while (1) {
        $i++;
        
        $temp = $zr * $zi;
        
        $zr2 = $zr * $zr;
        $zi2 = $zi * $zi;
        
        $zr = $zr2 - $zi2 + $cr;
        $zi = $temp + $temp + $ci;

        if ($zi2 + $zr2 > 16) {
            return $i;
        }

        if ($i > 5000) {
            return 0;
        }
    }
}

After running ab for a few hundred requests, we can see the results:

requests/second (more is better)
Mandelbrot without JIT 3.60
Mandelbrot with tracing JIT 41.36

Great, it looks like the JIT is working! That's even a ten times performance increase! Having verified it works as expected, let's move on to our first real-life comparison. We're going to compare no JIT with the function and tracing JIT; using 100MB of memory. The page we're going to benchmark shows an overview of posts, so there's some recursion happening. We're also touching several core parts of Laravel as well: routing, the dependency container, as well as the ORM layer.

Side note:

If you want to verify whether the JIT is running, you can use opcache_get_status(), it has a jit entry which lists all relevant information:

dd(opcache_get_status()['jit']);

// array:7 [▼
//   "enabled" => true
//   "on" => true
//   "kind" => 5
//   "opt_level" => 4
//   "opt_flags" => 6
//   "buffer_size" => 104857584
//   "buffer_free" => 104478688
// ]
requests/second (more is better)
No JIT 63.56
Function JIT 66.32
tracing JIT 69.45

Here we see the results: enabling the JIT only has a slight improvement. In fact, running the benchmarks over and over, the results differ slightly every time: I've even seen cases where a JIT enabled run performs worse than the non JIT'ed version. Before drawing final conclusions, let's bump the memory buffer limit. We'll give the JIT a little more room to breathe with 500MB of memory instead of 100MB.

requests/second (more is better)
No JIT 71.69
Function JIT 72.82
Tracing JIT 70.11

As you can see: a case of the JIT performing worse. Like I said at the beginning of this post: I want to measure the relative impact the JIT has on real-life web projects. It's clear from these tests that sometimes there might be benefits, but it's in no way as noticeable as the fractal example we started out with. I admit I'm not really surprised by that. Like I wrote before: there's very little hot code to be optimised in real-life applications, we're only rarely doing fractal-like computations.

So am I saying there's no need for the JIT? Not quite, I think the JIT can open up new areas for PHP: areas where complex computations do benefit from JIT'ed code. I'm thinking about machine learning, AI, stuff like that. The JIT might give opportunities to the PHP community that didn't exist yet, but it's unclear to say anything with certainty at this point.


So, that concludes my JIT testing. As expected: the JIT probably won't have a significant impact on web applications, at least not right now.

I won't discuss my thoughts on whether the JIT itself is a good addition or not in this post, let's have those discussions together over here!

]]>
2020-07-02T00:00:00+00:00
<![CDATA[ Braille, and the evolution of software development ]]> https://www.stitcher.io/blog/braille-and-the-history-of-software Back in 1785, a Frenchman named Valentin Haüy founded the Institute for Blind Youth in Paris. Several years before, he was touched seeing blind children on the streets, cast out by society. They would never be able to receive education, until Haüy came along.

Besides managing the school, he was also a translator for the King of France, Louis XVI. From time to time, he received fancy invitations from the royal family, which had embossed letters on them. It didn't take long before Haüy wondered whether these raised letters could be a way of teaching his pupils to read, via touch.

In 1786, he printed his first book with raised letters: it was readable by touch and by sight, so both blind and seeing people could read it.

It was a revolutionary idea, but with its downsides: the fancy and curly fonts of that time made it very hard for blind people to distinguish between letters; furthermore, it was also rather expensive to print such books.

Fast forward to 1821, when a French army captain came to the Institute for Blind Youth; now called National Institute for the Young Blind, because of the French revolution. This captain gave a lecture about a technique used in the army for nocturnal writing, in other words: blind writing. They used a 12-dot system to pass messages during the night.

In that lecture was a 12-year old boy, named Louis Braille.

You can probably guess what came next: inspired by the army's nocturnal writing style, Louis came up with a 6-dot system to represent the letters of the alphabet.

It turned out these dots were significantly more easy to distinguish by touch than Haüy's embossed letters. It was a system built upon previous iterations of the same idea, but most importantly it was invented by someone who was blind himself, who understood the problem that needed to be solved first hand.

It shouldn't be a surprise: this technique grew rapidly in popularity among the blind.

While Braille was still developing his system, an American named Samuel Howe came to visit France. He was going to start the first school for blind children in the U.S. and wanted to do practical research. He took the original idea of Haüy's embossed letters, and made a new font for it; a font that would be less "artsy" and more "practical" to read. It was called Boston Line Type.

By the time the system was introduced in the U.S., blind U.S. citizens also heard of braille. Upper management resisted though: it was a European invention, for sure Boston Line Type would be the superior system. It was still readable by both blind and seeing people, and was more clear than Haüy's method.

Despite this reasoning, perhaps even a bit of American pride, it was clear that Boston Line Type would never be able to surpass braille.

Still, schools in the U.S. couldn't live with using the European standard. So what did they do instead? Two or three variations of braille were created over a course of 50 years; all based on the same principle, but all different in their implementation.

This went on until 1932, until braille finally became the accepted standard, probably what most blind people wanted all along. A period that took more than a century; it was later poetically called "The war of the Dots".


Let's talk about software development, shall we? There are one or two parallels with the history of braille that I think we can learn valuable lessons from.

First of all: we shouldn't always aim to please everyone. Both Haüy's method and Boston Line Type aimed to write text that was accessible both for blind and seeing people. The end result was something suboptimal for both groups.

Sometimes it's better, also in software development, to focus on that one specific problem, and find the best solution for it, instead of trying to solve everything. It's better to focus on a specific problem, instead of a generalised one.

Second: it took braille more than 100 years to become the standard, even though it was clear from the start that it was a great system. Software development is still in its infancy. I don't think there will still be 200 different frameworks solving the same problem in 50 years.

Maybe there will even be only a handful of languages, one or two per field: web development, mobile apps, desktop apps, machine learning, etc. Each field will evolve towards the best solution, and we might already know that solution today, but aren't ready to accept it yet.

Braille is just one example of how much time it takes to optimise a process or system. Let's not fool ourselves thinking we already know the best solutions for our problems in software development. Let's keep that in mind when were advocating for the next big thing. Let's stay humble and realise we're only playing a small part in history.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-06-27T00:00:00+00:00
<![CDATA[ Abstract resources in Laravel Nova ]]> https://www.stitcher.io/blog/abstract-resources-in-laravel-nova One of the major coding architecture strategies I use when building a complex Laravel Nova project is the ability to have an abstract resource class.

Before starting by the way, if you want deep dive in Nova, I suggest you subscribe to updates in my upcoming course Mastering Nova that will be released this mid-summer!


An abstract resource class will inherit the base Resource class. This allows you to override specific methods to add functionality on your real resource classes.

In the end, any method that you improve in your custom base class, will be available on your model resources. I'll show you how to create the abstract resource, and then we'll at concrete improvements.

We start by creating a file AbstractResource.php inside app/Nova, like this:

app/
  Nova/
     AbstractResource.php

At first, the AbstractResource looks like this:

namespace App\Nova;

abstract class AbstractResource extends Resource
{
}

Next, in your Resource classes just inherit from this abstract Resource instead of the Nova Resource one:

namespace App\Nova;

use App\Nova\AbstractResource;

class Review extends AbstractResource
{
    //
}

So, let's look at some examples of improvements you can add to your new abstract resource.

# Default sorting

On your abstract Resource write this code:

public static function indexQuery(NovaRequest $request, $query)
{
    $uriKey = static::uriKey();
    
    if (($request->orderByDirection ?? null) !== null) {
        return $query;
    }
    
    if (! empty(static::$indexDefaultOrder)) {
        $query->getQuery()->orders = [];

        return $query->orderBy(
            key(static::$indexDefaultOrder), 
            reset(static::$indexDefaultOrder)
        );
    }
}

Then on your model resource:

public static $indexDefaultOrder = ['email' => 'asc'];

This will sort your index query by "email, asc" in case there is not a pre-selected sorting order.

# Search relationships

If you have a relationship field, you might have seen that you cannot use it to search on your Resource search field. In that case, you can use the titasgailius/search-relations package.

To install it, just import it via Composer:

composer require titasgailius/search-relations

Then in your Abstract Resource, you can add it like:

use Titasgailius\SearchRelations\SearchesRelations;

abstract class AbstractResource extends Resource
{
    use SearchesRelations;
}

Henceforth, on your model resources, you can simply add:

public static $searchRelations = [
    'user' => ['username', 'email'],
];

where the key is your relationship name, and then an array of searchable column values. Therefore you can now search on your relationship columns!

# Sharing Cards, Lenses, Actions and Filters

Let's say you would like to have a generic card that shows information about when was the last time your current resource was updated, and some other extra information regarding your resource; or an action that actually will change status on models that share a status_type column.

All of this functionality can be shared between model resources.

As an example, let's say you want to add a new Card to all of the model resources that share your abstract resource, you can do it like:

public function cards(Request $request){
    return [
        new ResourceInformation(
            $this->getCurrentResourceInstance($this->getModelInstance())
        ),
    ];
}

protected function getModelInstance()
{
    $resourceKey = explode('/', request()->path())[1];

    $resourceClass = Nova::resourceForKey($resourceKey);

    return new $resourceClass::$model;
}

and in your model Resource:

public function cards(Request $request)
{
    return array_merge(
        [/* your cards */], 
        parent::cards($request),
    );
}

# Disable 'trashed' behavior

The BelongsTo field already has an option to remove the checkbox 'With Trashed' (basically not to show trashed items), but what if want to remove it from any other relationship operation (e.g.: BelongsToMany)?

You just need to apply the following code in your abstract resource:

use Illuminate\Support\Facades\Gate;

/**
 * Based the trashed behavior on a new policy called trashedAny()
 *
 * @return boolean
 */
public static function softDeletes()
{
    // Is this resource authorized on trashedAny?
    if (static::authorizable()) {
        if (! method_exists(
            Gate::getPolicyFor(static::newModel()),
            'trashedAny'
        )) {
            return true;
        }       

        return Gate::check('trashedAny', static::class));
    };

    return parent::softDeletes();
}

in this example, all you have to do is to define a policy for your model, and then create a new method called trashedAny(User $user), as example:

public function trashedAny(User $user)
{
    return false;
}

These were examples that can trigger your thoughts about how to leverage Abstract Resources on your Nova projects.

And if I was able to convince you :) I suggest you subscribe to updates in my upcoming course Mastering Nova that will be released this mid-summer!

Best, Bruno

]]>
2020-06-23T00:00:00+00:00
<![CDATA[ PHP 8: Constructor property promotion ]]> https://www.stitcher.io/blog/constructor-promotion-in-php-8 Personally, I use value objects and data transfer objects all the time in my projects. I even wrote a dedicated post on how to treat data in our code a while back.

Naturally, I'm very happy with the constructor property promotion RFC, it's passed and will be added in PHP 8. You see, this feature reduces a lot of boilerplate code when constructing simple objects such as VOs and DTOs.

In short: property promotion allows you to combine class fields, constructor definition and variable assignments all into one syntax, in the construct parameter list.

So instead of doing this:

class CustomerDTO
{
    public string $name;

    public string $email;

    public DateTimeImmutable $birth_date;

    public function __construct(
        string $name, 
        string $email, 
        DateTimeImmutable $birth_date
    ) {
        $this->name = $name;
        $this->email = $email;
        $this->birth_date = $birth_date;
    }
}

You would write this:

class CustomerDTO
{
    public function __construct(
        public string $name, 
        public string $email, 
        public DateTimeImmutable $birth_date,
    ) {}
}

Let's look at how it works!


# How it works

The basic idea is simple: ditch all the class properties and the variable assignments, and prefix the constructor parameters with public, protected or private. PHP will take that new syntax, and transform it to normal syntax under the hood, before actually executing the code.

So it goes from this:

class MyDTO
{
    public function __construct(
        public string $name = 'Brent',
    ) {}
}

To this:

class MyDTO
{
    public string $name;

    public function __construct(
        string $name = 'Brent'
    ) {
        $this->name = $name;
    }
}

And only executes it afterwards.

Note by the way that the default value is not set on the class property, but on the method argument in the constructor.

So let's look at what promoted properties can and can't do, there's quite a lot of little intricacies worth mentioning!


# Only in constructors

Promoted properties can only be used in constructors. That might seem obvious but I thought it was worth mentioning this, just to be clear.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# No duplicates allowed

You're not able to declare a class property and a promoted property with the same name. That's also rather logical, since the promoted property is simply transpiled to a class property at runtime.

class MyClass
{
    public string $a;

    public function __construct(
        public string $a,
    ) {}
}

# Untyped properties are allowed

You're allowed to promote untyped properties, though I'd argue that these days with modern PHP, you're better off typing everything.

class MyDTO
{
    public function __construct(
        public $untyped,
    ) {}
}

# Simple defaults

Promoted properties can have default values, but expressions like new … are not allowed.

public function __construct(
    public string $name = 'Brent',
    public DateTimeImmutable $date = new DateTimeImmutable(),
) {}

# Combining promoted- and normal properties

Not all constructor properties should be promoted, you can mix and match.

class MyClass
{
    public string $b;

    public function __construct(
        public string $a,
        string $b,
    ) {
        $this->b = $b;
    }
}

I'd say: be careful mixing the syntaxes, if it makes the code less clear, consider using a normal constructor instead.


# Access promoted properties from the constructor body

You're allowed to read the promoted properties in the constructor body. This can be useful if you want to do extra validation checks. You can use both the local variable and the instance variable, both work fine.

public function __construct(
    public int $a,
    public int $b,
) {
    assert($this->a >= 100);

    if ($b >= 0) {
        throw new InvalidArgumentException('…');
    }
}

# Doc comments on promoted properties

You can add doc comments on promoted properties, and they are still available via reflection.

class MyClass 
{
    public function __construct(
        /** @var string */
        public $a,
    ) {}
}
$property = new ReflectionProperty(MyClass::class, 'a');

$property->getDocComment(); // "/** @var string */"

# Attributes

Just like doc blocks, attributes are allowed on promoted properties. When transpiled, they will be present both on the constructor parameter, as well as the class property.

class MyClass
{
    public function __construct(
        #[MyAttribute]
        public $a,  
    ) {}
}

Will be transpiled to:

class MyClass 
{
    #[MyAttribute]
    public $a;
 
    public function __construct(
        #[MyAttribute]
        $a,
    ) {
        $this->a = $a;
    }
}

# Not allowed in abstract constructors

I didn't even know abstract constructors were a thing, but here goes! Promoted properties are not allowed in them.

abstract class A
{
    abstract public function __construct(
        public string $a,
    ) {}
}

# Allowed in traits

On the other hand, they are allowed in traits. This makes sense, since the transpiled syntax is also valid in traits.

trait MyTrait
{
    public function __construct(
        public string $a,
    ) {}
}

# var is not supported

Old, I mean, experienced PHP developers might have used var in a distant past to declare class variables. It's not allowed with constructor promotion. Only public, protected and private are valid keywords.

public function __construct(
    var string $a,
) {}

# Variadic parameters cannot be promoted

Since you can't convert to a type that's array of type, it's not possible to promote variadic parameters.

public function __construct(
    public string ...$a,
) {}

Still waiting for generics…


# Reflection for isPromoted

Both ReflectionProperty and ReflectionParameter have a new isPromoted method to check whether the class property or method parameter is promoted.


# Inheritance

Since PHP constructors don't need to follow the declaration of their parent constructor, there's little to be said: inheritance is allowed. If you need to pass properties from the child constructor to the parent constructor though, you'll need to manually pass them:

class A
{
    public function __construct(
        public $a,
    ) {}
}

class B extends A
{
    public function __construct(
        $a,
        public $b,    
    ) {
        parent::__construct($a);
    }
}

That's about it for property promotion! I for sure will use them, what about you? Let me know via Twitter or e-mail!


]]>
2020-06-12T00:00:00+00:00
<![CDATA[ Survey results: type systems in PHP ]]> https://www.stitcher.io/blog/type-system-in-php-survey-results I use PHP's type system as much as possible, though often found resistance with the people I interact with on Twitter and Reddit. After having discussed the topic numerous times, I felt like both "camps" were not really listening to each other, or at least not understanding each others point.

It made me wonder if and how the team we work in, and the kind of projects we work on, might influence our view of type system usage.

I decided to do a little survey, and gather some actual insights in the topic.

Fair warning: I'm no neutral player in this discussion, but I want to make clear that it wasn't my intention to make my own case. I wanted to see whether our seemingly pointless discussions might be caused by the difference in context; I don't want to prove there's one and only true answer.

With all that being said, let's look at the results.


First of all I'd like to thank all 686 people who participated in this survey. I realise this is a small group, though I hope it's representative enough to draw some conclusions. If you think that the results aren't accurate enough, please reach out to me to discuss whether and how we can redo this survey on a larger scale.

Based on the answers in the survey, I made five groups of profiles: A, B, C, D and E. A and B lean (strongly) towards a stricter type system, C is somewhat neutral, and D and E lean (strongly) towards not using type systems.

This "type profile" was determined by mapping the answers to relevant questions to a score: 1 and 2 points were given to answers favorable to strict type systems, 0 to neutral answers and -1 and -2 to answers leaning towards no type systems.

Type profile of all participants

One thing that immediately stood out is the large amount of people who lean towards the use of a strict type system. I did not expect this. From discussions I had on Twitter, I had the feeling that more people would be in group C, D or E.

These are some of the most popular arguments against the use of PHP's type system, at least the ones I heard in my discussions:

  • PHP's type system still fails at runtime, so there's no advantage to using it
  • Types add unnecessary visual overload
  • The flexibility of using PHP's type juggling is preferred

Of course this survey wanted to examine whether there's a correlation between personal preference and team- and project size. Let's look at team size first.

This chart shows the average team size, and for each group the distribution of type profiles within that group.

Type profile distribution, grouped by team size

We'd need to look at relative results to test whether there's a correlation or not. So here goes, but keep in mind that the group with 2-10 people, is by far the largest.

Relative type profile distribution, grouped by team size

As I expected, based on discussions: profiles D and E are more present in smaller teams. Yet I admit I expected that group to be larger again.

Next I looked at project size. I asked participants to describe the size of an average project they work on: small, medium, large or extra large.

Relative type profile distribution, grouped by project size

This chart shows a growth of type A and B, related to the size of the project. Most times, "project size" also translates to "project duration", which is why I also asked participants to rate the project duration of such an average project.

Relative type profile distribution, grouped by project duration

Again we see a preference for stricter type systems in longer projects, but we should of course be aware that there were less participants in these groups. Furthermore, I found it interesting that in this case, there' no linear pattern to discover, as with the previous charts.

# Conclusions

Unfortunately, I think there weren't enough participants distributed across all kinds of projects and team sizes to draw final conclusions here.

Up front, I assumed that the group who preferred not to use type systems would have been larger; but maybe it's simply a more vocal group, even though smaller? I can't say that for sure though.

I do think that, even with a small amount of participants, we can assume there is a correlation between type system usage and project- and team size; but ideally, we'd need a larger participant pool.

My personal takeaway is that when entering type system discussions, we should be wary to compare each others preference: there might be a good case that you're simply working in a completely different kind of project, and there's no way of telling who's right or wrong.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-06-09T00:00:00+00:00
<![CDATA[ PHP 8: Attributes ]]> https://www.stitcher.io/blog/attributes-in-php-8 As of PHP 8, we'll be able to use attributes. The goal of these attributes, also known as annotations in many other languages, is to add meta data to classes, methods, variables and what not; in a structured way.

The concept of attributes isn't new at all, we've been using docblocks to simulate their behaviour for years now. With the addition of attributes though, we now have a first-class citizen in the language to represent this kind of meta data, instead of having to manually parse docblocks.

So what do they look like? How do we make custom attributes? Are there any caveats? Those are the questions that will be answered in this post. Let's dive in!

# Rundown

First things first, here's what attribute would look like in the wild:

use \Support\Attributes\ListensTo;

class ProductSubscriber
{
    #[ListensTo(ProductCreated::class)]
    public function onProductCreated(ProductCreated $event) { /* … */ }

    #[ListensTo(ProductDeleted::class)]
    public function onProductDeleted(ProductDeleted $event) { /* … */ }
}

I'll be showing other examples later in this post, but I think the example of event subscribers is a good one to explain the use of attributes at first.

Also yes, I know, the syntax might not be what you wished or hoped for. You might have preferred @, or @:, or docblocks or, … It's here to stay though, so we better learn to deal with it. The only thing that's worth mentioning on the syntax is that all options were discussed, and there are very good reasons why this syntax was chosen. You can read the whole discussion about the RFC on the internals list.

That being said, let's focus on the cool stuff: how would this ListensTo work under the hood?

First of all, custom attributes are simple classes, annotated themselves with the #[Attribute] attribute; this base Attribute used to be called PhpAttribute in the original RFC, but was changed with another RFC afterwards.

Here's what it would look like:

#[Attribute]
class ListensTo
{
    public string $event;

    public function __construct(string $event)
    {
        $this->event = $event;
    }
}

That's it — pretty simple right? Keep in mind the goal of attributes: they are meant to add meta data to classes and methods, nothing more. They shouldn't — and can't — be used for, for example, argument input validation. In other words: you wouldn't have access to the parameters passed to a method within its attributes. There was a previous RFC that allowed this behaviour, but this RFC specifically kept things more simple.

Back to the event subscriber example: we still need to read the meta data and register our subscribers based somewhere. Coming from a Laravel background, I'd use a service provider as the place to do this, but feel free to come up with other solutions.

Here's the boring boilerplate setup, just to provide a little context:

class EventServiceProvider extends ServiceProvider
{
    // In real life scenarios, 
    //  we'd automatically resolve and cache all subscribers
    //  instead of using a manual array.
    private array $subscribers = [
        ProductSubscriber::class,
    ];

    public function register(): void
    {
        // The event dispatcher is resolved from the container
        $eventDispatcher = $this->app->make(EventDispatcher::class);

        foreach ($this->subscribers as $subscriber) {
            // We'll resolve all listeners registered 
            //  in the subscriber class,
            //  and add them to the dispatcher.
            foreach (
                $this->resolveListeners($subscriber) 
                as [$event, $listener]
            ) {
                $eventDispatcher->listen($event, $listener);
            }       
        }       
    }
}

Note that if the [$event, $listener] syntax is unfamiliar to you, you can get up to speed with it in my post about array destructuring.

Now let's look at resolveListeners, which is where the magic happens.

private function resolveListeners(string $subscriberClass): array
{
    $reflectionClass = new ReflectionClass($subscriberClass);

    $listeners = [];

    foreach ($reflectionClass->getMethods() as $method) {
        $attributes = $method->getAttributes(ListensTo::class);
        
        foreach ($attributes as $attribute) {
            $listener = $attribute->newInstance();
            
            $listeners[] = [
                // The event that's configured on the attribute
                $listener->event,
    
                // The listener for this event 
                [$subscriberClass, $method->getName()],
            ];
        }
    }

    return $listeners;
}

You can see it's easier to read meta data this way, compared to parsing docblock strings. There are two intricacies worth looking into though.

First there's the $attribute->newInstance() call. This is actually the place where our custom attribute class is instantiated. It will take the parameters listed in the attribute definition in our subscriber class, and pass them to the constructor.

This means that, technically, you don't even need to construct the custom attribute. You could call $attribute->getArguments() directly. Furthermore, instantiating the class means you've got the flexibility of the constructor the parse input whatever way you like. All in all I'd say it would be good to always instantiate the attribute using newInstance().

The second thing worth mentioning is the use of ReflectionMethod::getAttributes(), the function that returns all attributes for a method. You can pass two arguments to it, to filter its output.

In order to understand this filtering though, there's one more thing you need to know about attributes first. This might have been obvious to you, but I wanted to mention it real quick anyway: it's possible to add several attributes to the same method, class, property or constant.

You could, for example, do this:

#[
    Route(Http::POST, '/products/create'),
    Autowire,
]
class ProductsCreateController
{
    public function __invoke() { /* … */ }
}

With that in mind, it's clear why Reflection*::getAttributes() returns an array, so let's look at how its output can be filtered.

Say you're parsing controller routes, you're only interested in the Route attribute. You can easily pass that class as a filter:

$attributes = $reflectionClass->getAttributes(Route::class);

The second parameter changes how that filtering is done. You can pass in ReflectionAttribute::IS_INSTANCEOF, which will return all attributes implementing a given interface.

For example, say you're parsing container definitions, which relies on several attributes, you could do something like this:

$attributes = $reflectionClass->getAttributes(
    ContainerAttribute::class, 
    ReflectionAttribute::IS_INSTANCEOF
);

It's a nice shorthand, built into the core.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Technical theory

Now that you have an idea of how attributes work in practice, it's time for some more theory, making sure you understand them thoroughly. First of all, I mentioned this briefly before, attributes can be added in several places.

In classes, as well as anonymous classes;

#[ClassAttribute]
class MyClass { /* … */ }

$object = new #[ObjectAttribute] class () { /* … */ };

Properties and constants;

#[PropertyAttribute]
public int $foo;

#[ConstAttribute]
public const BAR = 1;

Methods and functions;

#[MethodAttribute]
public function doSomething(): void { /* … */ }

#[FunctionAttribute]
function foo() { /* … */ }

As well as closures;

$closure = #[ClosureAttribute] fn() => /* … */;

And method and function parameters;

function foo(#[ArgumentAttribute] $bar) { /* … */ }

They can be declared before or after docblocks;

/** @return void */
#[MethodAttribute]
public function doSomething(): void { /* … */ }

And can take no, one or several arguments, which are defined by the attribute's constructor:

#[Listens(ProductCreatedEvent::class)]
#[Autowire]
#[Route(Http::POST, '/products/create')]

As for allowed parameters you can pass to an attribute, you've already seen that class constants, ::class names and scalar types are allowed. There's a little more to be said about this though: attributes only accept constant expressions as input arguments.

This means that scalar expressions are allowed — even bit shifts — as well as ::class, constants, arrays and array unpacking, boolean expressions and the null coalescing operator. A list of everything that's allowed as a constant expression can be found in the source code.

#[AttributeWithScalarExpression(1 + 1)]
#[AttributeWithClassNameAndConstants(PDO::class, PHP_VERSION_ID)]
#[AttributeWithClassConstant(Http::POST)]
#[AttributeWithBitShift(4 >> 1, 4 << 1)]

# Attribute configuration

By default, attributes can be added in several places, as listed above. It's possible, however, to configure them so they can only be used in specific places. For example you could make it so that ClassAttribute can only be used on classes, and nowhere else. Opting-in this behaviour is done by passing a flag to the Attribute attribute on the attribute class.

It looks like this:

#[Attribute(Attribute::TARGET_CLASS)]
class ClassAttribute
{
}

The following flags are available:

Attribute::TARGET_CLASS
Attribute::TARGET_FUNCTION
Attribute::TARGET_METHOD
Attribute::TARGET_PROPERTY
Attribute::TARGET_CLASS_CONSTANT
Attribute::TARGET_PARAMETER
Attribute::TARGET_ALL

These are bitmask flags, so you can combine them using a binary OR operation.

#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION)]
class ClassAttribute
{
}

Another configuration flag is about repeatability. By default the same attribute can't be applied twice, unless it's specifically marked as repeatable. This is done the same way as target configuration, with a bit flag.

#[Attribute(Attribute::IS_REPEATABLE)]
class ClassAttribute
{
}

Note that all these flags are only validated when calling $attribute->newInstance(), not earlier.

# Built-in attributes

Once the base RFC had been accepted, new opportunities arose to add built-in attributes to the core. One such example is the #[Deprecated] attribute, and a popular example has been a #[Jit] attribute — if you're not sure what that last one is about, you can read my post about what the JIT is.

I'm sure we'll see more and more built-in attributes in the future.

As a final note, for those worrying about generics: the syntax won't conflict with them, if they ever were to be added in PHP, so we're safe!



I've got some use-cases already in mind for attributes, what about you? If you've got some thoughts to share about this awesome new feature in PHP 8, you can reach me on Twitter or via e-mail, or we can discuss it over on Reddit.

]]>
2020-09-03T00:00:00+00:00
<![CDATA[ How to merge multidimensional arrays in PHP? ]]> https://www.stitcher.io/blog/merging-multidimensional-arrays-in-php If you want to join two multidimensional arrays in PHP, you should still use array_merge, and not array_merge_recursive. Confused? So was I. Let's explain what's happening.

Let's first explain what array_merge_recursive does, take for example these two arrays:

$first = [
    'key' => 'original'
];

$second = [
    'key' => 'override'
];

Using array_merge_recursive will result in the following:

array_merge_recursive($first, $second);

// [
//     'key' => [
//         'original',
//         'override',
//     ],
// ]

Instead of overriding the original key value, array_merge_recursive created an array, with the original and new value both in it.

While that looks strange in this simple example, it's actually more useful in cases where one of the values already is an array, and you want to merge another item in that array, instead of overriding it.

$first = [
    'key' => ['original']
];

$second = [
    'key' => 'override'
];

In this case, array_merge_recursive will yield the same result as the first example: it takes the value from the $second array, and appends it to the value in the $first array, which already was an array itself.

array_merge_recursive($first, $second);

// [
//     'key' => [
//         'original',
//         'override',
//     ],
// ]

So if you want to merge multidimensional arrays, you can simply use array_merge, it can handle multiple levels of arrays just fine:

$first = [
    'level 1' => [
        'level 2' => 'original'
    ]
];

$second = [
    'level 1' => [
        'level 2' => 'override'
    ]
];

array_merge($first, $second);

// [  
//     'level 1' => [
//         'level 2' => 'override'
//     ]
// ]

All of that being said, you could also use the + operator to merge multidimensional arrays, but it will work slightly different compared to array_merge.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-06-05T00:00:00+00:00
<![CDATA[ What does array + do in PHP? ]]> https://www.stitcher.io/blog/what-is-array-plus-in-php In PHP it's possible to do array + array. The "plus" sign is a shorthand way of merging arrays, but there's a difference in how they are merged compared to using array_merge.

Let's imagine these two arrays:

$first = [
    'a',
    'b',
];

$second = [
    'c',
];

Merging them using + would result in the following:

$first + $second;

// ['a', 'b']

While using array_merge, would result in this:

array_merge($first, $second);

// ['a', 'b', 'c']

What's happening here is that array_merge will override existing keys, while + will not. In other words: when a key exists in the first array, + will not merge an item with the same key from another array into the first one.

In our example, both arrays actually had numerical keys, like so:

$first = [
    0 => 'a',
    1 => 'b',
];

$second = [
    0 => 'c',
];

Which explains why $first + $second doesn't add 'c' as an element: there already is an item with index 0 in the original.

The same applies for textual keys:

$first = [
    'a' => 'a',
    'b' => 'b',
];

$second = [
    'a' => 'a - override',
];

$first + $second;

// ['a' => 'a', 'b' => 'b']

And finally, + also works with nested arrays:

$first = [
    'level 1' => [
        'level 2' => 'original'
    ],
];

$second = [
    'level 1' => [
        'level 2' => 'override'
    ],
];

Using + will keep the original value, while array_merge would override it.

One more thing to mention is that + will apply the same behaviour when merging multidimensional arrays.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-06-05T00:00:00+00:00
<![CDATA[ Survey: type systems in PHP ]]> https://www.stitcher.io/blog/type-system-in-php-survey Hi there! Thanks for spending 5 minutes of your time on this quick survey. My goal with it is to investigate the correlation between type-systems and project size in the PHP community.

Once this survey has gathered enough results, I will publish my conclusions on this blog. If you want stay updated on that, you can subscribe to my newsletter, follow me on Twitter or subscribe to my RSS feed.

A small update, I've gathered enough results, you can read about them here.

]]>
2020-06-03T00:00:00+00:00
<![CDATA[ Improvements on Laravel Nova ]]> https://www.stitcher.io/blog/improvements-on-laravel-nova # A long time ago in a galaxy far, far away…

Laravel Nova was launched on 22nd August 2018, as the official administration panel for Laravel web applications.

That was Taylor Otwell making the presentation at the Laracon US 2018, and since then Nova has evolved a lot, offering a better a UI experience, faster performance, and at the end giving backend creators a true experience on how to have a seamless integration between a Laravel application and the respective Resources (or Eloquent empowered models) integrated into a nice CRUD interface.

When Nova was created, a lot of expectations were already present in the Laravel community in the way that Laravel Nova should be a real administration back-office system, since this was a niche that was already covered by very nice alternative CRUD platforms like QuickAdminPanel or Laravel BackPack but they weren't an official Laravel product. So, when Nova was launched right after Laracon it went like a sales hit, everybody was talking about it, and everybody that bought a license experienced challenges to using it too. But those are things from the past :)

Nova evolved since then and evolved a lot! It's no longer a simple Resource Management tool and I want to share with you 6 must-have features that might help you when you are developing your Nova projects. Also, if you want deep dive in Nova, I suggest you subscribe to updates in my upcoming course Mastering Nova that will be released this mid-summer!

# 1. Creating your custom CSS theme

The first versions of Nova you weren't able to customize your Theme and to tweak it in a way without having your CSS code being overridden each time a new version of Nova was being published.

Now you can create your CSS theme, like this:

php artisan nova:theme brunocfalcao/masteringnova-theme

After that, you have a CSS class in your new package at resources/css/theme.css where you can then apply all the new Tailwind classes that you want to use in your Nova instance.

If you want to even fully customize the entire Nova classes you can enable it using a custom package, then use the Nova::enableThemingClasses() to fully brand it to your needs.

This feature will prefix the Vue components with the string nova-. For example, Nova will add the class name nova-heading to the top-level of the Heading component so you can then style it from there.

// NovaServiceProvider.php

public function boot()
{
    Nova::enableThemingClasses()
}
// app.js

/**
 * If configured, register a global mixin to add theming-friendly CSS
 * classnames to Nova's built-in Vue components. This allows the user
 * to fully customize Nova's theme to their project's branding.
 */
if (window.config.themingClasses) {
    Vue.mixin(ThemingClasses)
}

# 2. Dynamic Field visibility statuses

in the version 1.x of Nova you would control your Fields visibility using 8 methods:

hideFromIndex()
hideFromDetail()
hideWhenCreating()
hideWhenUpdating()
onlyOnIndex()
onlyOnDetail()
onlyOnForms()
exceptOnForms()

Now, you have the show*() methods that allow you to show your Resource in the respective display context without the dependency of other display contexts. For instance you can have a showOnIndex() and a showOnCreating(), using a callback on the method that should return true.

showOnIndex()
showOnDetail()
showOnCreating()
showOnUpdating()

# 3. New field types to create a better user experience

Since version 1.x that we see being added new field types to Nova. Let me highlight you some of the ones I consider the best additions:

# SparkLine Field

See it a Chart "on-the-fly" directly in your Resource index or detail contexts.

Sparkline::make('Total devices Per Week')
    ->data($data)
    ->asBarChart()
    ->width(300),

# Key-Value Field

Key-value fields are ways for you to interact with JSON data type columns, providing a way to manage Key-Value entries in a CRUD way.

KeyValue::make('Server Data', 'server_data')
    ->keyLabel('Parameter')
    ->valueLabel('Value')
    ->actionText('Add Server Parameter')
    ->rules('json')
    ->nullable(),

// In your model:
protected $casts = [
    'server_data' => 'json'
];

# Hidden Field

At first, it might not be useful, but believe me, it's great to have it since you can apply data computations to be sent to your UI components.

Hidden::make('User', 'user_id')->default(
    fn($request) => $request->user()->id
);

# VaporFile and VaporImage Fields

These are the newest kids on the block since they allow you to upload files or images into your Laravel Vapor instance. They will generate a temporary upload URL for Amazon S3 and will immediately upload the file.

VaporFile::make('Filename'),
VaporImage::make('Avatar')->maxWidth(80)->rounded(false),

# Searchable Select Fields

On the latest Nova version 3.6.0 you can now have a Searchable Select field.

Select::make('Tags', 'tag_id')
    ->searchable()
    ->options(\App\Tag::all()->pluck('name', 'id'))
    ->displayUsingLabels(),

# 4. You can change the Stubs

Since version 3.3.0 it's possible to publish the Nova stubs so you can change them to your own needs.

php artisan nova:stubs [--force]

The stubs are published directly in your app folder in a directory called "stubs".

# 5. Ability to sort your Resources by priority on the Sidebar

This one I think it's undocumented but you can sort your Resources given a specific attribute in your Resource.

// In NovaServiceProvider.php
Nova::sortResourcesBy(function ($resource) {
    return $resource::$priority ?? 9999;
});

// In your Resource
public static $priority = 10; // Or any other number.

The Sidebar Resources will then be sorted by this priority. Neat!

In specific cases, you might want to have your Global Search targeted Resource to go to Edit and not to Detail, or vice-versa. All you have to do is to add this static property on your Resource:

public static $globalSearchLink = 'detail';

Hope you enjoyed, and in case you want to continue learning Laravel Nova you can pre-subscribe my Mastering Nova Course anytime!


Once again thanks to Bruno for writing this post!

]]>
2020-05-26T00:00:00+00:00
<![CDATA[ PHP 7.4 in 7 code blocks ]]> https://www.stitcher.io/blog/php-74-in-7-code-blocks PHP 7.4, the last edition in the 7.* series, brings lots of new and handy changes. This post lists the highlights, though there's much more to this release. You can read all about the full release in this post about what's new in PHP 7.4.


array_map(
    fn(User $user) => $user->id,
    $users
);

Arrow functions, a.k.a. short closures. You can read about them in depth in this post.


class A
{
    public string $name;
    
    public ?Foo $foo;
}

Type properties. There's quite a lot to tell about them.


$data['date'] ??= new DateTime();

The null coalescing assignment operator. If you're unfamiliar with the null coalescing operator, you can read all about shorthand operators in this blog.


class ParentType {}
class ChildType extends ParentType {}

class A
{
    public function covariantReturnTypes(): ParentType
    { /* … */ }
}

class B extends A
{
    public function covariantReturnTypes(): ChildType
    { /* … */ }
}

Improved type variance. If you're not sure what that's about, you should take a look at this post about Liskov and type safety.


$result = [...$arrayA, ...$arrayB];

The array spread operator. There are a few sidenotes to be made about them.


$formattedNumber = 107_925_284.88;

The numeric literal separator, which is only a visual aid.


[preloading]
opcache.preload=/path/to/project/preload.php

Preloading improves PHP performance across requests. It's a complicated topic, but I wrote about it here.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-05-23T00:00:00+00:00
<![CDATA[ The IKEA effect ]]> https://www.stitcher.io/blog/the-ikea-effect # The spark

A few days ago while I was browsing the forums, I stumbled across a discussion that was the spark for this article. In short the OP was having problems with an expensive query and trying his best to optimize it. The thread had a lot of responses suggesting ways of optimizing the query like, index this column, use sub selects instead of joins, chunk the results etc…

What was weird for me was the fact that the OP was trying to load 13k options into a select element. I tried to suggest him that even if he manages to optimize the query to a few milliseconds the user experience would be less than poor. I spent time making my case presenting him with alternatives, articles with ux best practices and mobile device optimizations. The focus didn't change. The dedication to improve his existing solution was absolute. Needless to say that the thread is still open to this day…

# The revelation

The whole thing made me think how many times I encountered this behavior in my working environment including myself. I tried to find a real life example that simulates this behavior while drinking my early morning coffee. I stretched and carefully rested my cup to my poorly constructed coffee table. I purchased this table 2 years ago from IKEA and spend hours trying to build it only to realise at the end that I was left with one extra screw. I messed up and skipped a part of the instructions but I was done. I wasn't gonna build the whole thing again for one screw.

To this day my coffee table is supported by a string attached to the legs and all my friends visiting know that the table needs to be treated gently.

# The bummer

I immediately came up with the name "The IKEA effect" and I congratulated myself for the coolness of it. Sure enough, after googling it I realized that yep that was a thing: there is a very interesting paper from Michael I. Norton, a professor in Harvard, titled The “IKEA Effect”: When Labor Leads to Love and to sum it up here is the wikipedia definition:

The IKEA effect is a cognitive bias in which consumers place a disproportionately high value on products they partially created

# The IKEA Effect revisited

In my career as a programmer I noticed this phenomenon's negative side effects quite often. It manifests itself in different ways and in different levels of an organization.

From the top level management dedicating time and effort to define the next big thing only to get grounded by the technical constrains or the level of effort required to build it. From the designer spending days to come up with a high fidelity design that at the end gets negative feedback and has to go back to the drawing board. Finally for the programmer who gets a code review after spending days working on a feature that basically requires rewriting the whole thing!

# The bigger the effort the greater the blindness

When working on a solution to a problem, the deeper you go, your focus switches to specifics parts of the problem. Many times those parts might require a disproportional amount of effort in order to solve them.

How many times I found myself searching and furiously going through stackoverflow to make this one thing work! When I finally got this little piece of the solution working, I was so attached to it that I wouldn't even consider the thought that it might not be the right way to do it.

I was ready to defend its honor against anyone who dared to challenge it! At this stage the feedback in any kind or form — either this comes from code review or over the water cooler — the result is tension. The worst part is that the feedback is probably right, but you are already too blind to see or accept it.

# The assembly manual

So how do we stop investing more and more into what we have already worked on, rather than striving for better or more efficient alternatives?

This are my takeaways.

Read the instructions: don't jump into the solution without understanding the problem. Invest time and ask for clarifications when needed.

Early feedback: write a summary of your proposed solution and communicate it to your colleagues, make sure that everybody that needs to be involved is aware.

Build a draft: code it, make it work, don't think about abstractions and future maintainability, just make it as simple as possible. Again ask for feedback and iterate.

Rebuild it: you've got some feedback that requires you to heavily refactor? Big deal you were going to rewrite the thing anyhow, no hard feelings.

Know when to stop: if something doesn't feel right, gets weirdly complicated to model it in your solution, then it smells bad. Stop and revisit your approach better now than later. Do you find yourself lost? Struggling to find a way to make it work? Maybe the problem is too hard for your level of expertise. There is no shame in this. Communicate it early with your senior experts ask for tips, pair coding, anything that can help you grow your game to be able to fight the beast.

Wrap it up: don't try to make every little part perfect. Pay attention to the core of the solution and learn to live with the rest. Ship it and after some time when you are emotionally detached revisit and refactor.


Once again thanks to Dimitris, be sure to check him out! If you're interested in discussing your thoughts in this topic, you can share them on HN.

]]>
2020-05-21T00:00:00+00:00
<![CDATA[ Builders and architects: two types of programmers ]]> https://www.stitcher.io/blog/builders-and-architects-two-types-of-programmers You're probably familiar with the feeling: a programmer in your vicinity or online just doesn't seem to get your point of view. Even though there's no doubt they are clearly wrong. These kinds of encounters make you feel misunderstood, frustrated, yes, even angry.

I, too, deal with these feelings on a regular basis. I read a tweet and think to myself: they are so wrong. I get angry because of the misinformation that's spread, the anti-patterns that are promoted, the lack of understanding.

I've had some insights in these kinds of situations though, and I want to share these with you today. I won't be able to tell you who's wrong or right — if there even is such a thing — and it will also take an effort on your part to succeed.

The payoff? A better understanding of why your colleague or that one guy on Reddit thinks such different thoughts than you, as well as a better grip on those circumstances. It allows you to rise above conflicts, become a problem solver instead of a troublemaker.

In this post I'll share a mental framework for programmers to manage these kinds of conflicts. To do that, we'll need to start with a simplified example.

Let's imagine two types of programmers: builders and architects.

If you ever had to work in teams — in school, as your job or in hobby projects — you're probably familiar with these two types of people.

The first ones, the builders, are the programmers who get things done. They work efficiently. Besides their daytime job, they come up with these amazing side projects and to the outside world it looks like writing code comes naturally to them.

On the other hand there are architects. They are concerned about sturdy and structurally sound code. They spend hours, sometimes days, debating the right way to solve a problem.

These two types of programmers seem like opposites. They themselves look at the other group and often think exactly that. They have a different way of handling the same problems. The other group's solutions are so different, they can't possibly be right, or so it seems.

Builders often find architects too rigid and conservative. They follow the rules for the sake of following them, without any practical benefit. Architects in turn, think of builders as careless, too focused on results, instead of thinking about long-term maintainability.

This tension often leads to confrontations between the groups, at the office or online. Interestingly enough, these kinds of discussions focus on trying to change the other person's point of view. I've been guilty of this myself more than I can count.

What's fascinating about these situations though, is that people from both sides almost never realise there's strength in their differences.

Instead of focusing on what they disagree on, let's try another approach: let's, for a minute, try to support the other party. Mind you: not just agree with them for the sake of ending a discussion, but actually trying to help them solve their problem, with your skill set.

Here's how that looks: while architects enjoy abstract thinking, they often have difficulties putting their thoughts into practice: they run into problems they didn't anticipate, or the actual implementation is too complex and they lose their drive. On the other hand, builders enjoy working towards a clear goal, they find energy in a project taking shape. So let's sit them down, together, and combine their strengths.


The illustration is easy enough — right? Though professional programmers still fall in the trap of endless discussions way too often. In fact, many human beings assume that the world around them thinks like they do. When the reality proves that's not the case, conflicts arise.

When you become aware of this personality trait, both in yourself and in others, you can use that knowledge to both your advantage. The reality is that most people won't have the same vision as you do, it's better to embrace that reality instead of fighting it.

One framework which differentiates between all kinds of personality types is called the Myers-Briggs Type Indicator — MBTI for short. It differentiates between 16 types of personalities. Obviously every person is unique, but those 16 types give an indication of how you and your colleagues think, how you'll react to situations, how you'll handle problems.

MBTI will map your personality into four different aspects:

  • Introvert or Extrovert
  • Observant or Intuitive
  • Thinking or Feeling
  • Judging or Prospecting

Like I said, every person is unique, so you're not either introvert or extrovert, you're not only thinking or feeling. It's a scale that balances one way or the other. Based on those aspects though, you can get a better understanding of why other people have such a different approach to the same problem as you do.

For example, builders would lean more towards the intuitive and prospective side, while architects are observant and judging. Builders are imaginative and flexible, architects are organized and practical. One isn't better than the other, rather they should support each other.

Having this knowledge about yourself and your co-workers allows for better team dynamics. At my previous job we mapped our MBTI profiles with around 25 colleagues. We sat down one afternoon to talk about the results. Not only did the majority of people recognise themselves in their profile, it also exposed struggles and hidden frustrations that were never shared before, because no one knew how to deal with them. Now we did.


One thing I want to emphasize at the end of this post, is that my goal was not to promote MBTI. Rather it's to make you think about how people's personalities differ from each other, and how this can result both in conflicts as well as opportunities. MBTI is just an example of how one could visualise these differences.

Understanding there are different kinds of people who think differently than you do, seems like such a simple realisation. Yet the internet and suboptimal or even broken teams show that a principle so easy to understand, is difficult to bring into practice.

If you want to, you can truly change the way you handle inter-personal conflicts. Even though you probably are right, try to think about ways to support your adversary, yes even leveraging them to a higher level. You'll be surprised that you're the one who benefits the most.

If you do want to know your own MBTI profile, you can test it for free online. Again this profile won't tell you who you really are, but it has to power to address the differences in personalities between you and other programmers. There's a great website called 16personalities.com providing a 20-minutes test. I'm interested to see the results! Let me know your profile and check up on others in this poll.

Want to share your own thoughts on the topic? You can send me an email or we can discuss it on HN.

Thanks for reading, until next time!

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-05-20T00:00:00+00:00
<![CDATA[ PHP 8 in 8 code blocks ]]> https://www.stitcher.io/blog/php-8-in-8-code-blocks PHP 8 brings lots of new features, in this list we'll look at the most outstanding ones. If you want a full list and background information, you can read about all things new in PHP 8.


use Support\Attributes\ListensTo;

class ProductSubscriber
{
    #[ListensTo(ProductCreated::class)]
    public function onProductCreated(ProductCreated $event) { /* … */ }

    #[ListensTo(ProductDeleted::class)]
    public function onProductDeleted(ProductDeleted $event) { /* … */ }
}

Attributes — aka annotations — you can read about them in depth in this post.


public function foo(Foo|Bar $input): int|float;

public function bar(mixed $input): mixed;

Union types allows for type hinting several types. There's also a new mixed type which represents several types at once.


interface Foo
{
    public function bar(): static;
}

The static return type is built-in.


[JIT]
opcache.jit=1225

The just-in-time compiler for PHP.


$triggerError = fn() => throw new MyError();

$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');

throw can be used in expressions.


try {
    // Something goes wrong
} catch (MySpecialException) {
    Log::error("Something went wrong");
}

Non-capturing catches: no need to specify an exception variable if you don't need it.


setcookie(
    name: 'test',
    expires: time() + 60 * 60 * 2,
);

Named arguments.


$result = match($input) {
    0 => "hello",
    '1', '2', '3' => "world",
};

The match expression as an improvement to the switch expression.

There's even more. If you want a full list, you can find it on this blog.

What feature are you looking forward to the most? Let me know on Twitter or via email.

]]>
2020-05-15T00:00:00+00:00
<![CDATA[ array_chunk in PHP ]]> https://www.stitcher.io/blog/array-chunk-in-php PHP has a lot of undiscovered native functions for many devs. Usually, we don't discover these functions until there is a true need for them. array_chunk is certainly one of these functions for me personally. In this article, we will discover what array_chunk is, what it does, and see it used in action.

# array_chunk and how it works

array_chunk is a pretty nifty name for slicing an array into chunks. This is essentially what the function does, it reads an array, and splits it into chunks.

Let me explain this in developer terms: we have an array, [1, 2, 3, 4, 5, 6, 7, 8], we use array_chunk on the array, specifying we want chunks of 2 items. The output would look similar to below.

$input = [1, 2, 3, 4, 5, 6, 7, 8];

array_chunk($input, 2);

[
    0 => [1, 2], 
    1 => [3, 4], 
    2 => [5, 6], 
    3 => [7, 8],
]

Pretty cool eh? With this function, you can quickly see how it can be utilized for statistical purposes, especially with segment averaging.

This function accepts 3 parameters as follows:

  • The array
  • The size of the chunks required as an int
  • A boolean to instruct the functions to preserve the keys of the original array or to not. Note: the default is false.

# A real-life problem

Walk with me now through this scenario: my boss wants to know for each working week, the average profits from his shop. Each working week has 5 days.

So, let's say for argument's sake say we have just queried the last 20 days of shop sales from the shop's database. The data returned populate our array with 20 entries and therefore has 4 working weeks.

Now this leaves us with the problem at hand, we need to calculate the average sales across every 5 days for 4 weeks. Follow me through this next section to achieve the result.

# Average segments with array_chunk

We know our data array has 20 entries, and we know that we need an average of each week of sales (5 entries). Let's utilize array_chunk with a little bit of extra native PHP to do the calculations.

$sales = [
   250.70, 220.10, 233, 243.50, 255,
   200, 300, 234, 350, 222,
   237.99, 200.30, 150.98, 201, 209,
   200, 300, 240, 203, 280,
];

// Split the array into groups of five, 
//  representing a 5 days working week.
$salesPerWeek = array_chunk($sales, 5);

// Map all items to their averages, week by week.
$averageSales = array_map(
   fn(array $items) => array_sum($items) / count($items),
   $salesPerWeek
);

Now, if we print the contents of $averageSales we will get something like the following:

[
    240.46,
    261.2,
    199.854,
    244.6,
]

Let's break down the code for complete transparency:

  • First, we have our array with 20 entries of sales data for each day.
  • Next, we use array_chunk to split it into groups of five.
  • Then we use array_map on each chunk, and use array_sum to divide by the count of the chunk to give us the average.

And that is it!

This type of functionality could be used in many statistical applications that require segmentation. The example in this article tries to show you how array_chunk works in layman terms with a bit of a pretend use-case behind it. I hope this was interesting, if you would like to see any more of my content, please check out my blog, https://www.codewall.co.uk/

]]>
2020-05-14T00:00:00+00:00
<![CDATA[ Combining event sourcing and stateful systems ]]> https://www.stitcher.io/blog/combining-event-sourcing-and-stateful-systems In this two-part series, my colleague Freek and I will discuss the architecture of a project we're working on. We will share our insights and answers to problems we encountered along the way. This part will be about the design of the system, while Freek's part will look at the concrete implementation.

Let's set the scene.

This project is one of the larger ones we've worked on. In the end it will serve hundreds of thousands of users, handle large amounts of financial transactions and standalone tenant-specific installations will need to be created on the fly.

One key requirement is that the product ordering flow — the core of the business — can be easily reported on, as well as tracked throughout history.

Besides this front-facing client process, there's also a complex admin panel to manage products. Within this context, there's little to no need for reporting or tracking history of the admin activities; the main goal here is to have an easy-to-use product management system.

I hope you understand that I deliberately am keeping these terms a little vague because obviously this isn't an open-source project, though I think the concepts of "product management" and "orders" is clear enough for you to understand the design decisions we've made.

Let’s first discuss an approach of how to design this system based on my Laravel beyond CRUD series.

In such a system there would probably be two domain groups: Product and Order, and two applications making use of both these domains: an AdminApplication and a CustomerApplication.

A simplified version would look something like this:

Having used this architecture successfully in previous projects, we could simply rely on it and call it a day. There are a few downsides with it though, specifically for this new project: we have to keep in mind that reporting and historical tracking are key aspects of the ordering process. We want to treat them as such in our code, and not as a mere side effect.

For example: we could use our activity log package to keep track of "history messages" about what happened with an order. We could also start writing custom queries on the order and history tables to generate reports.

However, these solutions only work properly when they are minor side effects of the core business. In this case, they are not. So Freek and I were tasked with figuring out a design for this project that made reporting and historical tracking an easy-to-maintain and easy-to-use, core part of the application.

Naturally we looked at event sourcing, a wonderful and flexible solution that fulfills the above requirements. Nothing comes for free though: event sourcing requires quite a lot of extra code to be written in order to do otherwise simple things. Where you'd normally have simple CRUD actions manipulating data in the database, you now have to worry about dispatching events, handling them with projectors and reactors, whilst always keeping versioning in mind.

While it was clear that an event sourced system would solve many of the problems, it would also introduce lots of overhead, even in places where it wouldn't add any value.

Here's what I mean with that: if we decide to event source the Orders module, which relies on data from the Products module, we also need to event source that one, because otherwise we could end up with an invalid state. If Products weren't event sourced, and one was deleted, we couldn't rebuild the Orders state anymore, since it's missing information.

So either we event source everything, or find a solution for this problem.

# Event source all the things?!

From playing around with event sourcing in some of our hobby projects, we were painfully aware that we shouldn't underestimate the complexity it adds. Furthermore, Greg Young stated that event sourcing a whole system is often a bad idea — he has a whole talk on misconceptions about event sourcing and is worth a watch!

It was clear to us that we did not want to event source the whole application; it simply wouldn't make sense to do so. The only alternative was to find a way to combine a stateful system, together with an event sourced system, but surprisingly, we couldn't find many resources on this topic.

Nevertheless, we did some labour intensive research, and managed to find an answer to our question. The answer didn't come from the event sourcing community though, but rather from well-established DDD practices: bounded contexts.

If we wanted the Products module to be an independent, stateful system, we had to clearly respect the boundaries between Products and Orders. Instead of one monolithic application, we would have to treat these two modules as two separate contexts — separate services, which were only allowed to speak with each other in such a way that it be could guaranteed the Order context would never end up in an invalid state.

If the Order context is built whereby it doesn't rely on the Product context directly, it wouldn't matter how that Product context was built.

When discussing this with Freek, I phrased it like this: think of Products as a separate service, accessed via a REST API. How would we guarantee our event sourced application would still work, even if the API goes offline, or makes changes to its data structure.

Obviously we wouldn't actually build an API to communicate between our services, since they would live within the same codebase on the same server. Still it was a good mindset to start designing the system.

The boundary would look like this, where each service has its own internal design.

If you read my Laravel beyond CRUD series, you're already familiar with how the Product context works. There's nothing new going on over there. The Order context deserves a little more background information though.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Event source some parts

So let's look at the event sourced part. I assume you that if you're reading this post, you have at least an interest in event sourcing, so I won't explain everything in detail.

The OrderAggregateRoot will keep track of everything that happens within this context and will be the entry point for applications to talk with. It will also dispatch events, which are stored and propagated to all reactors and projectors.

Reactors will handle side effects which will never be replayed and projectors will make projections. In our case these are simple Laravel models. These models can be read from any other context, though they can only be written to from within projectors.

One design decision we made here was to not split our read and write models, for now we rely on a spoken and written convention that these models are only written to via their projectors. One example of such a projection model would be an Order.

The most important rule to remember is that the whole state of the Order context should be able to be rebuilt only from its stored events.

So how do we pull in data from other contexts? How can the Order context be notified when something happens within the Product context that's relevant to it? One thing is for sure: all relevant information regarding Products will need to be stored as events within the Order context; since within that context, events are the only source of truth.

To achieve this, we introduced a third kind of event listener. There already are projectors and reactors; now we add the concept of subscribers. These subscribers are allowed to listen to events from other contexts, and handle them accordingly within their current context. Most likely, they will almost always convert external events to internal, stored ones.

From the moment events are stored within the Order context, we can safely forget about any dependency on the Product context.

Some readers might think that we're duplicating data by copying events between these two contexts. We're of course storing an Orders specific event, based on when a Product was created, so yes, some data will be copied. There are, however, more benefits to this than you might think.

First of all: the Product context doesn't need to know anything about which other contexts will use its data. It doesn't have to take event versioning into account, because its events will never be stored. This allows us to work in the Product context as if it was any normal, stateful application, without the complexity event sourcing adds.

Second: there will be more than just the Order context that's event sourced, and all of these contexts can individually listen to relevant events triggered within the Product context.

And third: we don't have to store a full copy of the original Product events since each context can cherry-pick and store the data that's relevant for its own use case.

# What about data migrations?

A new question arose.

Say this system has been in production for a year, and we decide to add a new context that's event sourced; one which also requires knowledge about the Product context. The original Product events weren't stored — because of the reasons listed above — so how can we build an initial state for our new context?

The answer is this: at the time of deployment, we'll have to read all product data, and send relevant events to the newly added context, based on the existing products. This one-time migration is an added cost, though it gives us the freedom to work within the Product context without ever having to worry about the outside. For this project that's a price worth paying.

# Final integration

Finally we're able to consume data in our applications gathered from all contexts, by using readonly models. Again, in our case and as of now, these models are readonly by convention; we might change that in the future.

Communication from applications to the Product context is done like any normal stateful application would do. Communication between applications and event sourced contexts such as Orders is done via its aggregate root.

Now, here's a final overview. Some arrows are still missing from this diagram, but I hope that the relevant flow between and inside contexts and applications is clear.


The key in solving our problem was to look at DDD's bounded contexts. They describe strict boundaries within our codebase - ones that we cannot simply cross whenever we want. Sure this adds a layer of complexity, though it also adds the freedom to build each context whatever way we want, without having to worry about supporting others.

The final piece of the puzzle was to solely rely on events as a means of communication between contexts. Once again it adds a layer of complexity, but also a means of decoupling and flexibility.

Now it's time to take a deep dive into how we programmed this within a Laravel project. Here's my colleague Freek with part two.

]]>
2020-04-14T00:00:00+00:00
<![CDATA[ Minor versions, breaking changes ]]> https://www.stitcher.io/blog/minor-versions-breaking-changes When my colleague Sebastian wrote about how bumping major versions isn't a breaking change, I would wholeheartedly agree.

I can't anymore. At least, not with how composer works today.

See yesterday, we stumbled upon a breaking change. And yet we didn't do any major version upgrades. Luckily another colleague of mine, Ruben, discovered the issue before pushing it to production.

Here's what happened.

One of our projects is now nearing its two-year anniversary mark, so suffice to say it has had its fair share of version bumps. After two years of development, these were some packages we used:

{
    "laravel/framework": "^6.5",
    "league/commonmark": "^0.17.5",
    "spatie/laravel-view-components": "^1.2",

    // …
}

First of all, laravel/framework:^6.5. We usually wait a month or two before updating to the next major Laravel version. As of today we're running 7 — which was needed to fix what went wrong.

Next there's league/commonmark:^0.17.5; a very specific dependency, added in May, 2018. At the time, this specific dependency was needed: according to the changelog it Fixed incorrect version constant value (again). If we didn't use this version, it would conflict with other packages.

Two years went by, and league/commonmark has since tagged a first stable release. This is something they should have done way more early — but that's a topic for another day.

Finally there's spatie/laravel-view-components:^1.2. A package that has been archived recently, in favour of Laravel's blade components in version 7. Again, back in the day it made lots of sense to use this package. We might remove it at one point in the future, but this of course requires time, and costs money for our client. It isn't something we can "just do".

With the stage being set, it's time to look into the issue. Yesterday we ran a composer update, and things broke.

More specifically, our spatie/laravel-view-components package simply stopped working. Instead of rendering the view component, it only showed the render tag instead. It turned out it was a known issue as of version 1.3.0, and fixed in 1.3.1. This fix already existed when we ran our disastrous composer update, yet we never received it. Our spatie/laravel-view-components seemed to be locked on 1.3.0, and didn't want to update to 1.3.1.

Whenever you find yourself in such a pickle, don't panic and keep calm: composer can help you. Simply use composer why-not spatie/laravel-view-components:1.3.1 and it will tell you exactly what's wrong.

It turned out that with spatie/laravel-view-components:1.3.1, its laravel/framework dependency version was bumped from ^6.0 to ^6.18.

That in itself shouldn't be a problem, we require laravel/framework:^6.5 in our project, so we should be able to load ^6.18 just fine.

Unfortunately that didn't happen. You see, laravel/framework added a dependency on league/commonmark:^1.1 in version 6.10. In practice, this addition has the same effect as updating the major version of a dependency: from nothing to ^1.1.

Again, that change in itself isn't a breaking change, yet it did prevent laravel/framework in our project from updating higher than 6.9, because of our requirement on league/commonmark:^0.17.5. That in turn prevented spatie/laravel-view-components updating from 1.3.0 to 1.3.1, which contained a much needed bugfix.


So who's to blame? Let's point the finger at myself first: we should have updated league/commonmark sooner. You could also say that combining a bugfix and a dependency version bump, like spatie/laravel-view-components did with 1.3.1, should be avoided. Yet if the version bump is needed for a fix to work, there's little you can do.

You could say that laravel/framework shouldn't have updated one of its (implicit) dependencies, yet it's a perfectly normal thing to do, especially if the update fixes a security issue.

The solution, by the way, consisted of updating laravel/framework to ^7.0 — we had to do this anyway sooner or later — and removing the league/commonmark dependency. So I don't think this change should be avoided by any open source vendors. Open source code should push us towards regular updates, I encourage that myself regularly.

The real problem though, is that composer never notified us that laravel/framework wasn't able to update further than 6.9 because of underlying conflicts. If you're not carefully managing each dependency, you're in danger of getting stuck on an outdated dependency, which might prevent much needed bug fixes being installed.

As far as I know, there's no option that can be passed to composer update which can notify you about such situations and I think that would be a good future addition.

My colleague Freek pointed out that there is an external library that does exactly this: https://github.com/Soullivaneuh/composer-versions-check. It'd be nice to have this functionality built-into composer.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

]]>
2020-03-26T00:00:00+00:00
<![CDATA[ Event driven server in PHP ]]> https://www.stitcher.io/blog/event-driven-php Lately I've been tinkering with a unique kind of architecture for PHP applications. I want to tell you up front that I don't think it will solve any real-life problems soon; still I want to involve you in the thought-process. Who knows what kind of great ideas might arise?

In this post I'll go through the architecture step-by-step and address its benefits as well as its downsides — at least, the ones I can think of right now. I do have a proof-of-concept codebase open sourced, and I'll share insights from it throughout this post.

So, first things first, what the architecture is about. It's a long-running PHP server, with its entire state loaded in memory, built from stored events. In other words: it's event sourcing as we know it in PHP, but all aggregates and projections are loaded in-memory and never stored on disk.

Let's break it down!

# A long-running PHP server

The first pillar of this architecture is a long running server. The modern PHP landscape offers several battle-tested solutions for managing these kinds of processes: frameworks like ReactPHP, Amphp and Swoole allowed the PHP community to venture into another, unexplored world, while day-to-day PHP was most often related to its characterizing fast request/response cycle.

This fast request/response cycle is of course one of the things that made PHP great: you never had to worry about leaking state or keeping everything in sync: when a request comes in, a clean PHP process is started, and your application boots from 0. After the response is sent, the application gets completely destroyed.

I'm not proposing we ditch this battle-tested technique altogether; the fast request/response cycle is actually a critical part of the architecture I'll be describing. On the other hand, always booting the whole application from scratch has its downsides.

In the architecture I'm describing, an application is split into two parts: one part is a regular PHP app, accepting HTTP requests and generating responses, while the other part is a behind-the-scenes backend server that's always running. A server that always has the whole application state loaded in memory, which allows the clients — our regular PHP apps — to connect with it, read data and store events.

Because the whole application state is always loaded in memory, you never need to perform database queries, spending resources on mapping data from the database to objects, or performance issues like circular references between ORM entities.

This sounds nice in theory, but we probably still need to be able to perform complex queries - something that databases are highly optimised for. It's clear that this architecture will require us to rethink certain aspects we're used to in regular PHP applications. I'll come back to this later.

First, let's look at the second pillar: event sourcing.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Event sourcing

Why would I suggest to make event sourcing part of the core of this architecture? You could very well have a long running server with all data loaded in-memory from a normal database.

Let's go down that road for a moment: say a client performs an update and sends it to the backend server. The server will need to store the data in the database, as well as refresh its in-memory state. Such systems will need to take care of updating the application state properly so that everything is correct after an update.

The most naive approach would be to perform the updates in the database and reload the whole application state, which in practice isn't possible due to performance issues. Another approach could be to keep track of everything that needs to happen when an update is received, and the most flexible way to do that is by using events.

If we're naturally leaning towards an event-driven system to keep the in-memory state synchronised, why then add the overhead of storing everything in a database and require an ORM to map the data back to objects? That's why event sourcing is the better approach: it solves all state syncing problems automatically, and offers a performance gain since you don't have to communicate with a database and work with an ORM.

What about complex queries though? How would you search, for example, a product store containing millions of items, when everything is loaded in memory. PHP doesn't particularly excel at these kinds of tasks. But again, event sourcing offers a solution: projections. You're perfectly able to make an optimised projection for a given task, and even store it in a database! This could be a lightweight in-memory SQLite database, or a full-blown MySQL or PostgreSQL server.

Most importantly, these databases aren't part of the application core anymore. No longer are they the source of truth, but rather useful tools living on the edge of the application's core and very much comparable to building optimised search indices like ElasticSearch or Algolia. You can destroy these data sources at any point in time, and rebuild them from the stored events.

That brings us to the final reason why event sourcing is such a great match for this architecture. When the server requires a reboot — because of a server crash or after a deploy — event sourcing offers you a way to rebuild the application's state much faster: snapshots.

In this architecture, a snapshot of the whole application state would be stored once or twice a day. It's a point where the server can be rebuilt from, without needing to replay all events.

As you can see, there are several benefits by building an event sourced system within this architecture. Now we're moving on to the last pillar: the clients.

# Clients

I've mentioned this before: with "clients" I mean server-side PHP applications communicating with the centralised backend server. They are normal PHP applications, only living a short time within the typical request/response cycle.

You can use whatever existing framework you want for these clients, as long as there's a way to use the event-server instead of directly communicating with eg. a database. Instead of using an ORM like Doctrine in Symfony or Eloquent in Laravel, you'd be using a small communication layer to communicate via sockets with the backend server.

Also keep in mind that the backend server and clients can share the same codebase, which means that from a developer's point of view, you don't need to worry about communication between a client and the server, it's done transparently.

Take the example of bank accounts with a balance. With this architecture, you'd write code like this:

final class AccountsController
{
    public function index(): View
    {
        $accounts = Account::all();

        return new View('accounts.index', [
            'accounts' => $accounts,
        ]);
    }
}

Keep in mind that I mainly work in a Laravel context and I'm used to the Eloquent ORM. If you prefer to use a repository pattern, that's also fine.

Behind the scenes, Account::all() or $accountRepository->all() will not perform database queries, rather they will send a small message to the backend server, which will send the accounts, from memory, back to the client.

If we're making a change to the accounts balance, that's done like so:

final class BalanceController
{
    public function increase(Account $account, int $amount): Redirect
    {
        $aggregateRoot = AccountAggregateRoot::find($account);
   
        $aggregateRoot->increaseBalance($amount);

        return new Redirect(
            [AccountsController::class, 'index'], 
            [$account]
        );
    }
}

Behind the scenes, AccountAggregateRoot::increaseBalance() will send an event to the server, which will store it and notify all relevant subscribers.

If you're wondering what such an implementation of AccountAggregateRoot might look like, here's a simplified version:

final class AccountAggregateRootRoot extends AggregateRoot
{
    public function increaseBalance(int $amount): self
    {
        $this->event(new BalanceIncreased($amount));

        return $this;
    }
}

And finally this is what the Account entity looks like. Notice the lack of ORM-style configuration; these are simple in-memory PHP objects!

final class Account extends Entity
{
    public string $uuid;

    public string $name;

    public int $balance = 0;
}

One final note to make: remember that I mentioned PHP's fast request/response cycle would actually be critical? Here's why: if we're sending updates to the server, we don't need to worry about broadcasting those updates back to the clients. Every client generally only lives for a second or two, so there's little to worry about keeping them in sync.

# The downsides

All of this sounds interesting in theory, but what about in practice? What about performance? How much RAM will you need to store everything in memory? Will we be able to optimise reading the state by performing complex queries? How will snapshots be stored? What about versioning?

Lots of questions are still unanswered. The goal of this post was not to provide all answers, but rather share some thoughts and questions with you, the community. Who knows what you can come up with?

I mentioned that the code for this is open source, you can take a look at it over here. I'm looking forward to hearing your feedback, on Reddit, via Twitter or e-mail.

]]>
2020-03-13T00:00:00+00:00
<![CDATA[ Bitwise booleans in PHP ]]> https://www.stitcher.io/blog/bitwise-booleans-in-php In my previous post I wrote about applying enum patterns in PHP, without native enum support.

In that post, I gave the example of a "date range boundaries" enum, one that represents which boundaries are included in the range, and which are not. It had four possible values:

  • Boundaries::INCLUDE_NONE();
  • Boundaries::INCLUDE_START();
  • Boundaries::INCLUDE_END();
  • Boundaries::INCLUDE_ALL();

To represent these boundaries, I stored two boolean flags on the enum value classes: $startIncluded and $endIncluded.

In this post, I want to show another way to store these two boolean flags, using bitmasks.

Here's a quick recap of what (part of) our enum class looked like:

abstract class Boundaries
{
    private bool $startIncluded = false;
    private bool $endIncluded = false;

    public function startIncluded(): bool 
    {
        return $this->startIncluded;
    }

    public function endIncluded(): bool 
    {
        return $this->endIncluded;
    }

    // …
}

In this case, we're using two variables to store two boolean values.

Them being booleans though, means they can only have one of two values: true or false; 1 or 0. Instead of using a whole byte, we only need one bit to store this value.

Hang on though, a whole byte? — It's actually a lot more: 16 bytes to be exact. PHP stores all variables in a structure called a zval, which reserves memory not only for the payload, but also type information, bit flags and what not. You can take a look at it here.

Of those 16 bytes, there's 8 reserved per zval to store a payload in; that's 64 bits!

Now, as a precursor, let's make clear that you probably will never need these kinds of micro-optimisations. Boolean bitmasks are often used in game development, compilers and the like, because they are very memory-efficient. In web applications, though, you can be assured you will probably never need them.

Nevertheless, it's a cool, geeky thing to know, and possible in PHP.

So let's store these two flags in one variable.

abstract class Boundaries
{
    protected int $inclusionMask = 0b00;
}

What's happening here? We're making use of the binary notation of integers to easily work with individual bits. If you ever learned about binary systems in school or somewhere else, you know that 0b00 equals 0, 0b01 equals 1, 0b10 equals 2 and 0b11 equals 3. 0b is a prefix that PHP uses to know you're writing binary, and 00 are the two actual bits.

Now that we've got two bits to work with, it's easy to store two boolean values in them. Let's say that the rightmost bit represents endIncluded, and the leftmost bit represents startIncluded.

So 0b01 means that the start boundary is not included, while the end boundary is; 0b11 means both are included — you get the gist.

Now that we know how to store data in bits, we still need a way of reading the information in our startIncluded() and endIncluded() methods: we don't want to program everything in binary.

Here's where bitwise operators come into play, more specifically the and operator.

Take the following two binary values:

0b0100101;
0b1010101;

What happens when we apply an and operation on both of these values? The result will have all bits set to 1 wherever both bits were 1 in the two original values:

0b0100101;
0b1010101;

This is the end result:

0b0000101;

Back to our boundaries example. How can we know whether the start is included or not? Since the start boundary is represented by the leftmost bit, we can apply a bitmask on our inclusion variable. If we want to know whether the start bit is set, we simply need to do an and operation between the inclusion mask, and the binary value 0b10.

How so? Since we're only interested in knowing the value of the start boundary, we'll make a mask for that bit only. If we apply an and operation between these two values, the result will always be 0b00, unless the start bit was actually set.

Here's an example where the start bit is 0:

0b10; // The mask we're applying
0b01; // The inclusion mask

0b00; // The result

And here's one where the start bit is 1:

0b10; // The mask we're applying
0b10; // The inclusion mask

0b10; // The result

The end bit will always be 0 in this case, because the mask we're applying has it set to 0. Hence, whatever value is stored for the end boundary in the inclusion mask, will always result in 0.

So how to do this in PHP? By using the binary and operator, which is a single &:

public function startIncluded(): bool 
{
    return $this->inclusionMask & 0b10;
}

public function endIncluded(): bool 
{
    return $this->inclusionMask & 0b01;
}

PHP's dynamic type system will automatically cast the result, 0 or a numeric value, to a boolean. If you want to be more explicit though, you can write it like so:

public function startIncluded(): bool 
{
    return ($this->inclusionMask & 0b10) !== 0;
}

public function endIncluded(): bool 
{
    return ($this->inclusionMask & 0b01) !== 0;
}

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

Let's make clear that you shouldn't be doing this for performance motivations in PHP. There might even be edge cases where this approach would be less optimal, because our inclusion mask can't be garbage collected unless there are no reference anymore to any of the boolean flags.

However, if you're working with several boolean flags at once, it might be useful to store them in one variable instead of several, to reduce cognitive load. You could think of "storing the boolean values" as a behind-the-scenes implementation detail, while the public API of a class still provides a clear way of working with them.

So, who knows, there might be cases where this technique is useful. If you have some real-life use cases, be sure to let me know on Twitter or via e-mail.

]]>
2020-02-14T00:00:00+00:00
<![CDATA[ Enums without enums in PHP ]]> https://www.stitcher.io/blog/enums-without-enums Enums are still lacking in PHP — SCRATCH THAT: enums are added in PHP 8.1 — yet there is a clean way to have enum-like behaviour in your code bases, without using external dependencies. Take the example of date range boundaries: its boundaries can be included or excluded. Here's how a Boundaries enum would be used:

$dateRange = DateRange::make(
    '2020-02-01', 
    '2020-03-01', 
    Boundaries::INCLUDE_ALL()
);

This is what the constructor signature of DateRange looks like:

public function __construct($start, $end, Boundaries $boundaries);

That's the first requirement: we want to use the type system to ensure only valid enum values are used.

Next, we want to be able to ask the enum which boundaries are included, like so:

$dateRange->boundaries->startIncluded();
$dateRange->boundaries->endIncluded();

This means that each enum value should support its own implementation of startIncluded and endIncluded.

That's the second requirement: we want our enums to support value-specific behaviour.

On first sight, the easiest solution is to have a Boundaries class, and implement startIncluded and endIncluded like so:

final class Boundaries
{
    private const INCLUDE_NONE = 'none';
    private const INCLUDE_START = 'start';
    private const INCLUDE_END = 'end';
    private const INCLUDE_ALL = 'all';

    private string $value;

    public static function INCLUDE_START(): self
    {
        return new self(self::INCLUDE_START);
    }

    private function __construct(string $value) 
    {
        $this->value = $value;
    }

    public function startIncluded(): bool
    {
        return $this->value === self::INCLUDE_START
            || $this->value === self::INCLUDE_ALL;
    }
    
    public function endIncluded(): bool
    {
        return $this->value === self::INCLUDE_END
            || $this->value === self::INCLUDE_ALL;
    }
}

In short: using conditionals on an enum's value to add behaviour.

For this example, it's a clean enough solution. However: it doesn't scale that well. Imagine our enum needs more complex value-specific functionality; you often end up with large functions containing large conditional blocks.

The more conditionals, the more paths your code can take, the more complex it is to understand and maintain, and the more prone to bugs.

That's the third requirement: we want to avoid using conditionals on enum values.

In summary, we want our enums to match these three requirements:

  • Enum values should be strongly typed, so that the type system can do checks on them
  • Enums should support value-specific behaviour
  • Value-specific conditions should be avoided at all costs

Polymorphism can offer a solution here: each enum value can be represented by its own class, extending the Boundaries enum. Therefore, each value can implement its own version of startIncluded and endIncluded, returning a simple boolean.

Maybe we'd make something like this:

abstract class Boundaries
{
    public static function INCLUDE_NONE(): IncludeNone
    {
        return new IncludeNone();
    }
    
    // …
    
    abstract public function startIncluded(): bool;

    abstract public function endIncluded(): bool;
}

And have a concrete implementation of Boundaries like this — you can imagine what the other three would look like:

final class IncludeNone extends Boundaries
{
    public function startIncluded(): bool
    {
        return false;
    }

    public function endIncluded(): bool
    {
        return false;
    }
} 

While there's more initial work to program these enums, we now meet all requirements.

There's one more improvement to be made. There's no need to use dedicated classes for specific values; they will never be used on their own. So instead of making four classes extending Boundaries, we could use anonymous classes:

abstract class Boundaries
{
    abstract public function startIncluded(): bool;

    abstract public function endIncluded(): bool;

    public static function INCLUDE_NONE(): Boundaries
    {
        return new class extends Boundaries 
        {
            public function startIncluded(): bool {
                return false; 
            }

            public function endIncluded(): bool {
                return false; 
            }
        };
    }

    public static function INCLUDE_START(): Boundaries
    {
        return new class extends Boundaries 
        {
            public function startIncluded(): bool {
                return true; 
            }

            public function endIncluded(): bool {
                return false; 
            }
        };
    }

    public static function INCLUDE_END(): Boundaries
    {
        return new class extends Boundaries 
        {
            public function startIncluded(): bool {
                return false; 
            }

            public function endIncluded(): bool {
                return true; 
            }
        };
    }

    public static function INCLUDE_ALL(): Boundaries
    {
        return new class extends Boundaries 
        {
            public function startIncluded(): bool {
                return true; 
            }

            public function endIncluded(): bool {
                return true; 
            }
        };
    }
}

Ok, I was mistaken: there were two more improvements to be made. This is a lot of repeated code! But again there's a solution for that! Let's simply define two properties on each value-specific class ($startIncluded and $endIncluded) and let's implement their getters on the abstract Boundaries class instead!

abstract class Boundaries
{
    protected bool $startIncluded;
    protected bool $endIncluded;
    
    public function startIncluded(): bool 
    {
        return $this->startIncluded;
    }
    
    public function endIncluded(): bool 
    {
        return $this->endIncluded;
    }

    public static function INCLUDE_NONE(): Boundaries
    {
        return new class extends Boundaries 
        {
            protected bool $startIncluded = false;
            protected bool $endIncluded = false;
        };
    }

    public static function INCLUDE_START(): Boundaries
    {
        return new class extends Boundaries
        {
            protected bool $startIncluded = true;
            protected bool $endIncluded = false;
        };
    }

    public static function INCLUDE_END(): Boundaries
    {
        return new class extends Boundaries
        {
            protected bool $startIncluded = false;
            protected bool $endIncluded = true;
        };
    }

    public static function INCLUDE_ALL(): Boundaries
    {
        return new class extends Boundaries
        {
            protected bool $startIncluded = true;
            protected bool $endIncluded = true;
        };
    }
}

The above is my favourite approach to implement enums in PHP. If there's one downside I can think of, it's that they require a little setup work, though I find that this is a small, one-off cost, that pays off highly in the long run.

]]>
2020-02-01T00:00:00+00:00
<![CDATA[ PHP in 2020 ]]> https://www.stitcher.io/blog/php-in-2020 It's no secret among web developers and programmers in general: PHP doesn't have the best reputation. Despite still being one of the most used languages to build web applications; over the years PHP has managed to get itself a reputation of messy codebases, inexperienced developers, insecure code, an inconsistent core library, and what not.

While many of the arguments against PHP still stand today, there's also a bright side: you can write clean and maintainable, fast and reliable applications in PHP.

In this post, I want to look at this bright side of PHP development. I want to show you that, despite its many shortcomings, PHP is a worthwhile language to learn. I want you to know that the PHP 5 era is coming to an end. That, if you want to, you can write modern and clean PHP code, and leave behind much of the mess it was 10 years ago.

So let's look at how the language has changed, matured even, over the past few years. I want to ask you to set aside any prejudice for just a few minutes, and possibly be surprised by what PHP is today.

Let's dive in.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# History summarized

Before diving into details, let's review how PHP, the language, is developed these days. We're at version 7.4 now, and PHP 8 will be the next version after that, at the end of 2020.

Ever since the late 5.* era, the core team tries to keep a consistent yearly release cycle, and have succeeded in doing so for the past four years.

In general, every new release is actively supported for two years, and gets another year of "security fixes only". The goal is to motivate developers to stay up-to-date as much as possible: small upgrades every year are easier than making the jump between 5.4 to 7.0, for example.

Lastly, PHP 5.6 was the latest 5.* release, with 7.0 being the next one. If you want to know what happened to PHP 6, you can listen to this episode of the PHP Roundtable podcast.

PHP's development these days is done by a group of volunteers, some of them are paid by their employers to work on the core full time. Most discussion of how the language is evolved happens on a mailing list.

With all of that out of the way, let's debunk some common misconceptions about modern PHP.

# PHP's type system

PHP started out as a very weakly and dynamically typed language, which had its benefits at the time. Ever since people started to use PHP for larger projects though, the shortcomings of its type system became clear, and the need for stronger type support arose.

Today, PHP is a rather unique language: it still allows you to write completely dynamically and weakly typed code, but also has a much stronger, opt-in type system. Combined with static analysis, tools like Psalm, Phan and PHPStan, you can write secure, strongly typed and statically analysed code.

Take, for example, a look at this snippet of PHP code, using its modern type system in full:

<?php

declare(strict_types=1);

final class Foo
{
    public int $intProperty = 2;

    public ?string $nullableString = null;

    private Bar $bar;

    public function __construct(Bar $bar) {
        $this->bar = $bar;
    }
    
    public function withInt(int $value): self
    {
        $clone = clone $this;
    
        $clone->intProperty = $value;

        return $clone;
    }
    
    public function unionTypes(int|float $input): void
    {
        // Union types will be added in PHP 8
    }
}

Truth be told, there's one important feature still missing in PHP's type system: generics. There's hope they will be added, but there's nothing concrete yet. In case of typed arrays, you'll need to rely on docblocks to get proper IDE support:

/** @var int[] */
public array $arrayOfInts = [];

And while typed arrays are a common use case for generics, solvable with docblocks, there's a lot more functionality we're missing out on because they are not in the language… yet.

# PHP's syntax

The 7.* era has done many good things in terms of making PHP a more mature language when it comes to syntax. To illustrate this I've made a non-exhaustive list of new things in PHP.

Array destructuring:

[$a, $b] = $array;

The null coalescing operator:

$value = $object->property ?? 'fallback if null';

$value = $array['foo'] ?? "fallback if key doesn't exists"; 

The null coalescing assignment operator:

public function get(string $input): string 
{
    return $this->cache[$input] ??= $this->sanitize($input);
}

Array spreading:

$a = [/* … */];
$b = [/* … */];

$mergedArray = [...$a, ...$b];

Variadic functions:

public function get(Foo ...$foos): void
{
    foreach($foos as $foo) {
        // …
    }
}

Argument unpacking:

$this->get(...$arrayOfFoo);

Typed properties:

public int $intProperty;

Arrow functions, also called short closures:

$ids = array_map(fn(Post $post): int => $post->id, $posts);

Generators:

function make(array $input): Generator
{
    foreach ($input as $item) {
        yield $this->doSomethingWith($item);
    }
}

And quite a lot more. I hope that it's clear from this list that PHP is still evolving today, and you can be sure there's more good stuff to come.

# PHP's performance

Back in the 5.* days, PHP's performance was… average at best. With 7.0 though, large parts of PHP's core were rewritten from the ground up, resulting in two or three times performance increases. Furthermore, each 7.* release has had a positive impact on performance.

Words don't suffice though. Let's look at benchmarks. Luckily other people have spent lots of time in benchmarking PHP performance. I find that Kinsta has a good updated list.

The latest performance related feature is called preloading, which basically allows you to store compiled parts of your PHP code in memory. You can look at some benchmarks over here.

When PHP 8 arrives, we'll also have a JIT compiler at our disposal, promising interesting performance improvements, and allowing PHP to enter new areas besides web development.

# Frameworks and ecosystem

Moving on to what's done by the community with PHP. Let's be clear: PHP isn't just WordPress anymore, on the contrary.

In general there are two major web application frameworks, and a few smaller ones: Symfony and Laravel. Sure there's also Laminas, Yii, Cake, Code Igniter etc. — but if you want to know what modern PHP development looks like, you're good with one of the first two.

Both frameworks have a large ecosystem of packages and products. Ranging from admin panels and CRMs to standalone packages, CI to profilers, numerous services like web sockets servers, queuing managers, payment integrations; honestly there's too much to list.

These frameworks are meant for actual development; if you're instead in need of pure content management, platforms like WordPress, CraftCMS and Statamic are improving more and more.

One way to measure the current state of PHP's ecosystem is to look at Packagist, the main package repository for PHP. It has seen exponential growth. With ±25 million downloads a day, it's fair to say that the PHP ecosystem isn't the small underdog it used to be.

Take a look at this graph, listing the amount of packages and versions over time. It can also be found on the Packagist website.

Besides application frameworks and CMSs, we've also seen the rise of asynchronous frameworks the past years. These are frameworks and servers, written in PHP or other languages, that allow users to run truly asynchronous PHP code. Some major players are Swoole, Amp and ReactPHP.

Since we've ventured into the async world, stuff like web sockets and applications with lots of IO have become actually relevant in the PHP world.

There has also been talk on the internals mailing list to add libuv to the core. For those unaware of libuv: it's the same library Node.js uses to allow all its asynchronicity. Who knows? PHP 8 might be the version adding it to the core!

# In closing

I hope I was able to show you that PHP has evolved tremendously over the past years, and you're perfectly able to write clean and maintainable code with it.

If you're interested in what PHP code looks like in the wild these days, you can check out the source code of one of my own projects, as well as many open source packages we personally maintain.

So while the language definitely has its drawbacks and 20 years of legacy to carry with it; I can say with confidence that I enjoy working with it.

In my experience, I'm able to create reliable, maintainable and quality software. The clients I work for are happy with the end result, as am I. While it's still possible to do lots of messed up things with PHP, I'd say it's a great choice for web development if used wisely and correctly.

Don't you agree? Let me know why! You can reach me via Twitter or e-mail.

]]>
2020-01-17T00:00:00+00:00
<![CDATA[ Preloading benchmarks in PHP 7.4 ]]> https://www.stitcher.io/blog/php-preload-benchmarks After writing about how preloading works, it's time to measure its impact in practice. Before diving into results, we need to make sure we're all on the same page: what we're measuring, and what not.

Because I want to know whether preloading will have a practical impact on my projects, I'll be running benchmarks on a real project, on the homepage of my hobby project aggregate.stitcher.io.

This project is a Laravel project, and will obviously do some database calls, view rendering etc. I want to make clear that these benchmarks don't tell anything about the performance of Laravel projects, they only measure the relative performance gains preloading could offer.

Let me repeat that again, just to make sure no one draws wrong conclusions from these results: my benchmarks will only measure whether preloading has a relative performance impact compared to not using it. These benchmarks say nothing about how much performance gain there is. This will depend on several variables: server load, the code being executed, what page you're on, etc.

Let's set the stage.

# Preloading Setup

Since I don't want to measure how much exactly will be gained by using preloading or not, I decided to run these benchmarks on my local machine, using Apache Bench. I'll be sending 5000 requests, with 50 concurrent requests at a time. The webserver is nginx, using php-fpm. Because there were some bugs in early versions of preloading, we're only able to successfully run our benchmarks as early as PHP 7.4.2.

I'll be benchmarking three scenarios: one with preloading disabled, one with all Laravel and application code preloaded, and one with an optimised list of preloaded classes. The reasoning for that latter one is that preloading also comes with a memory overhead, if we're only preloading "hot" classes — classes that are used very often — we might be able to find a sweet spot between performance gain and memory usage.

# Preloading disabled

We start php-fpm and run our benchmarks:

./php-7_4_2/sbin/php-fpm --nodaemonize

ab -n 5000 -c 50 -l http://aggregate.stitcher.io.test:8080/discover

These were the results: we're able to process 64.79 requests per second, with an average time of 771ms per request. This is our baseline scenario, we can compare the next results to this one.

# Naive preloading

Next we'll preload all Laravel and application code. This is the naive approach, because we're never using all Laravel classes in a request. Because we're preloading many more files than strictly needed, we'll have to pay a penalty for it. In this case 1165 classes and their dependencies were preloaded, resulting in a total of 1366 functions and 1256 classes to be preloaded.

Like I mentioned before,you can read that info from opcache_get_status:

opcache_get_status()['preload_statistics'];

Another metric we get from opcache_get_status is the memory used for preloaded scripts. In this case it's 17.43 MB. Even though we're preloading more code than we actually need, naive preloading already has a positive impact on performance.

requests/second time per request
No preloading 64.79 771ms
Naive preloading 79.69 627ms

You can already see a performance gain: we're able to manage more requests per second, and the average amount of time to process one request has dropped with ±20%.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Optimised

Finally we want to compare the performance gain when we're using an optimised preloading list. For testing purposes I started the server without preloading enabled, and dumped all classes that are used within that request:

get_declared_classes();

Next, I only preloaded these classes, 427 in total. Together with all their dependencies this makes for 643 classes and 1034 functions being preloaded, occupying about 11.76 MB of memory.

These are the benchmark results for this setup:

requests/second time per request
No preloading 64.79 771ms
Naive preloading 79.69 627ms
Optimised preloading 86.12 580ms

That's around a 25% performance gain compared to not using preloading, and an 8% gain compared to using the naive approach. There's a flaw with this setup though, since I generated an optimised preloading list for one specific page. In practice you would probably need to preload more code, if you want all your pages covered.

Another approach could be to monitor which classes are loaded how many times over the period of several hours or days on your production server, and compile a preload list based on those metrics It's safe to say that preloading — even using the naive "preload everything" approach — has a positive performance impact, also on real-life projects built upon a full-blown framework. How much exactly there is to be gained will depend on your code, your server and the framework you're using. I'd say go try it out!

]]>
2020-01-10T00:00:00+00:00
<![CDATA[ Upgrade to PHP 7.4 with Homebrew on Mac ]]> https://www.stitcher.io/blog/php-74-upgrade-mac

# Upgrading with Homebrew

Start by making sure brew is up-to-date:

brew update

Next, upgrade PHP:

brew upgrade php

Check the current version by running php -v:

php -v

Restart Nginx or Apache:

sudo nginx -s reload
sudo apachectl restart

And make sure that your local web server also uses PHP 7.4 by visiting this script:

# index.php, accessible to your web server

phpinfo(); die();

The version should show 7.4.x.

Note: if you're using Laravel Valet, please keep on reading, you need some extra steps in order for the web server to properly work.

# Valet

If you're using Laravel Valet, you should do the following steps to upgrade it:

composer global update

Now run valet install:

valet install

# Extensions

Homebrew doesn't support the installation of PHP extensions anymore, you should use pecl instead. I personally use Imagick, Redis and Xdebug.

They can be installed like so:

pecl install imagick
pecl install redis
pecl install xdebug

You can run pecl list to see which extensions are installed:

pecl list

# Installed packages, channel pecl.php.net:
# =========================================
# Package Version State
# imagick 3.4.4   stable
# redis   5.1.1   stable
# xdebug  2.8.0   stable

You can search for other extensions using pecl search:

pecl search pdf

# Retrieving data...0%
# ..
# Matched packages, channel pecl.php.net:
# =======================================
# Package Stable/(Latest) Local
# pdflib  4.1.2 (stable)        Creating PDF on the fly with the PDFlib library

Make sure to restart your web server after installing new packages:

sudo nginx -s reload
sudo apachectl restart

If you're using Laravel Valet, you should restart it as well.

valet restart

Make sure all extensions are correctly installed and loaded by checking both your PHP webserver and CLI installs:

php -i | grep redis
var_dump(extension_loaded('redis'));

If extensions aren't properly loaded, there are two easy fixes.

First, make sure the extensions are added in the correct ini file. You can run php --ini to know which file is loaded:

Configuration File (php.ini) Path: /usr/local/etc/php/7.4</hljs>
Loaded Configuration File:         /usr/local/etc/php/7.4/php.ini
Scan for additional .ini files in: /usr/local/etc/php/7.4/conf.d
Additional .ini files parsed:      /usr/local/etc/php/7.4/conf.d/ext-opcache.ini,
/usr/local/etc/php/7.4/conf.d/php-memory-limits.ini

Now check the ini file:

extension="redis.so"
extension="imagick.so"
extension="xdebug.so"

Note that if you're testing installed extensions via the CLI, you don't need to restart nginx, apache or Valet.

The second thing you can do, if you're updating from an older PHP version which also used pecl to install extension; is to reinstall every extension individually.

pecl uninstall imagick
pecl install imagick

# Last step

Finally you should test and upgrade your projects for PHP 7.4 compatibility.

]]>
2019-11-28T00:00:00+00:00
<![CDATA[ What's new in PHP 7.4 ]]> https://www.stitcher.io/blog/new-in-php-74 # What's new in PHP 7.4

PHP 7.4 is the latest stable version of PHP. It was released on November 28, 2019 and it's the last version before PHP 8. It brings lots of new features, syntax additions and fixes. In this post you'll find a list with everything that's new and changed to help you prepare for the upgrade. Let's start though with a few highlights, included in PHP 7.4:

# New features

PHP 7.4 comes with a remarkable amount of new features. We'll start with a list of all new features, and then look at changes and deprecations.

A note before we dive in though: if you're still on a lower version of PHP, you'll also want to read what's new in PHP 7.3.


# Arrow functions rfc

Arrow functions, also called "short closures", allow for less verbose one-liner functions.

While you'd previously write this:

array_map(function (User $user) { 
    return $user->id; 
}, $users)

You can now write this:

array_map(fn (User $user) => $user->id, $users)

There are a few notes about arrow functions:

  • They can always access the parent scope, there's no need for the use keyword.
  • $this is available just like normal closures.
  • Arrow functions may only contain one expression, which is also the return statement.

You can read about them in depth here.



# Typed properties rfc

Class variables can be type hinted:

class A
{
    public string $name;
    
    public ?Foo $foo;
}

There's lots to tell about this feature, so I wrote a dedicated post about them.


# Improved type variance rfc

I also wrote about PHP's type system in the past, so it's good to see some improvements are actually arriving in PHP's core.

Type variance is another topic worth its own blog post, but in short: you'll be able use covariant return types –

class ParentType {}
class ChildType extends ParentType {}

class A
{
    public function covariantReturnTypes(): ParentType
    { /* … */ }
}

class B extends A
{
    public function covariantReturnTypes(): ChildType
    { /* … */ }
}

– and contravariant arguments.

class A
{
    public function contraVariantArguments(ChildType $type)
    { /* … */ }
}

class B extends A
{
    public function contraVariantArguments(ParentType $type)
    { /* … */ }
}

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Null coalescing assignment operator rfc

Next is the null coalescing assignment operator, a shorthand for null coalescing operations. Instead of doing this:

$data['date'] = $data['date'] ?? new DateTime();

You can do this:

$data['date'] ??= new DateTime();

You can read more about this operator in my post about PHP shorthands.


# Array spread operator rfc

Next up, it's now possible to use the spread operator in arrays:

$arrayA = [1, 2, 3];

$arrayB = [4, 5];

$result = [0, ...$arrayA, ...$arrayB, 6 ,7];

// [0, 1, 2, 3, 4, 5, 6, 7]

Note that this only works with arrays with numerical keys, as of PHP 8.1 it's also possible to unpack arrays with string keys.


# Numeric Literal Separator rfc

PHP 7.4 allows for underscores to be used to visually separate numeric values. It looks like this:

$unformattedNumber = 107925284.88;

$formattedNumber = 107_925_284.88;

The underscores are simply ignored by the engine.


# Foreign function interface rfc

Moving on to some more core-level features: foreign function interface or "FFI" in short, allows us to call C code from userland. This means that PHP extensions could be written in pure PHP and loaded via composer.

It should be noted though that this is a complex topic. You still need C knowledge to be able to properly use this feature.


# Preloading rfc

Another lower-level feature is preloading. It's is an amazing addition to PHP's core, which can result in some significant performance improvements.

In short: if you're using a framework, its files have to be loaded and linked on every request. Preloading allows the server to load PHP files in memory on startup, and have them permanently available to all subsequent requests.

The performance gain comes of course with a cost: if the source of preloaded files are changed, the server has to be restarted.

Do you want to know more? I wrote a dedicated post about setting up and using preloading and have also done some preloading benchmarks.


# Custom object serialization rfc

Two new magic methods have been added: __serialize and __unserialize. The difference between these methods and __sleep and __wakeup is discussed in the RFC.


# Reflection for references rfc

Libraries like Symfony's var dumper rely heavily on the reflection API to reliably dump a variable. Previously it wasn't possible to properly reflect references, resulting in these libraries relying on hacks to detect them.

PHP 7.4 adds the ReflectionReference class which solves this issue.


# Weak references rfc

Weak references are references to objects, which don't prevent them from being destroyed.


# mb_str_split added rfc

This function provides the same functionality as str_split, but on multi-byte strings.


# Password Hashing Registry rfc

Internal changes have been made to how hashing libraries are used, so that it's easier for userland to use them.

More specifically, a new function password_algos has been added which returns a list of all registered password algorithms.

The RFC was a little unclear about the benefits, luckily Sara was able to provide some more context:

It means that ext/sodium (or anyone really) can register password hashing algorithms dynamically. The upshot of which is that argon2i and argon2id will be more commonly available moving forward


# Changes and deprecations

Besides new features, there are also lots of changes to the language. Most of these changes are non-breaking, though some might have an effect on your code bases.

Note that deprecation warnings aren't per definition "breaking", but merely a notice to the developer that functionality will be removed or changed in the future. It would be good not to ignore deprecation warnings, and to fix them right away; as it will make the upgrade path for PHP 8.0 more easy.


# Left-associative ternary operator deprecation rfc

The ternary operator has some weird quirks in PHP. This RFC adds a deprecation warning for nested ternary statements. In PHP 8, this deprecation will be converted to a compile time error.

1 ? 2 : 3 ? 4 : 5;   // deprecated
(1 ? 2 : 3) ? 4 : 5; // ok

# Exceptions allowed in __toString rfc

Previously, exceptions could not be thrown in __toString. They were prohibited because of a workaround for some old core error handling mechanisms, but Nikita pointed out that this "solution" didn't actually solve the problem it tried to address.

This behaviour is now changed, and exceptions can be thrown from __toString.


# Concatenation precedence rfc

If you'd write something like this:

echo "sum: " . $a + $b;

PHP would previously interpret it like this:

echo ("sum: " . $a) + $b;

PHP 8 will make it so that it's interpreted like this:

echo "sum: " . ($a + $b);

PHP 7.4 adds a deprecation warning when encountering an unparenthesized expression containing a . before a + or - sign.


# array_merge without arguments upgrading

Since the addition of the spread operator, there might be cases where you'd want to use array_merge like so:

$merged = array_merge(...$arrayOfArrays);

To support the edge case where $arrayOfArrays would be empty, both array_merge and array_merge_recursive now allow an empty parameter list. An empty array will be returned if no input was passed.


# Curly brackets for array and string access rfc

It was possible to access arrays and string offsets using curly brackets:

$array{1};
$string{3};

This has been deprecated.


# Invalid array access notices rfc

If you were to use the array access syntax on, say, an integer; PHP would previously return null. As of PHP 7.4, a notice will be emitted.

$i = 1;

$i[0]; // Notice

# proc_open improvements upgrading

Changes were made to proc_open so that it can execute programs without going through a shell. This is done by passing an array instead of a string for the command.


# strip_tags also accepts arrays upgrading

You used to only be able to strip multiple tags like so:

strip_tags($string, '<a><p>')

PHP 7.4 also allows the use of an array:

strip_tags($string, ['a', 'p'])

# ext-hash always enabled rfc

This extension is now permanently available in all PHP installations.


# PEAR not enabled by default externals

Because PEAR isn't actively maintained anymore, the core team decided to remove its default installation with PHP 7.4.


# Several small deprecations rfc

This RFC bundles lots of small deprecations, each with their own vote. Be sure to read a more detailed explanation on the RFC page, though here's a list of deprecated things:

  • The real type
  • Magic quotes legacy
  • array_key_exists() with objects
  • FILTER_SANITIZE_MAGIC_QUOTES filter
  • Reflection export() methods
  • mb_strrpos() with encoding as 3rd argument
  • implode() parameter order mix
  • Unbinding $this from non-static closures
  • hebrevc() function
  • convert_cyr_string() function
  • money_format() function
  • ezmlm_hash() function
  • restore_include_path() function
  • allow_url_include ini directive

# Other changes upgrading

You should always take a look at the full UPGRADING document when upgrading PHP versions.


Here are some changes highlighted:

  • Calling parent:: in a class without a parent is deprecated.
  • Calling var_dump on a DateTime or DateTimeImmutable instance will no longer leave behind accessible properties on the object.
  • openssl_random_pseudo_bytes will throw an exception in error situations.
  • Attempting to serialise a PDO or PDOStatement instance will generate an Exception instead of a PDOException.
  • Calling get_object_vars() on an ArrayObject instance will return the properties of the ArrayObject itself, and not the values of the wrapped array or object. Note that (array) casts are not affected.
  • ext/wwdx has been deprecated.

# RFC voting process improvements

This is technically not an update related to PHP 7.4, though it's worth mentioning: the voting rules for RFC's have been changed.

  • They always need a 2/3 majority in order to pass.
  • There are not more short voting periods, all RFCs must be open for at least 2 weeks.

]]>
2019-11-26T00:00:00+00:00
<![CDATA[ Preloading in PHP 7.4 ]]> https://www.stitcher.io/blog/preloading-in-php-74 With PHP 7.4, support for preloading was added, a feature that could improve the performance of your code significantly.

In a nutshell, this is how it works:

  • In order to preload files, you need to write a custom PHP script
  • This script is executed once on server startup
  • All preloaded files are available in memory for all requests
  • Changes made to preloaded files won't have any effect, until the server is restarted

Let's look at it in depth.

# Opcache, but more

While preloading is built on top of opcache, it's not exactly the same. Opcache will take your PHP source files, compile it to "opcodes", and store those compiled files on disk.

You can think of opcodes as a low-level representation of your code, that can be easily interpreted at runtime. So opcache skips the translation step between your source files and what the PHP interpreter actually needs at runtime. A huge win!

But there's more to be gained. Opcached files don't know about other files. If you've got a class A extending from class B, you'd still need to link them together at runtime. Furthermore, opcache performs checks to see whether the source files were modified, and will invalidate its caches based on that.

So this is where preloading comes into play: it will not only compile source files to opcodes, but also link related classes, traits and interfaces together. It will then keep this "compiled" blob of runnable code — that is: code usable by the PHP interpreter — in memory.

When a request arrives at the server, it can now use parts of the codebase that were already loaded in memory, without any overhead.

So, what "parts of the codebase" are we talking about?

# Preloading in practice

For preloading to work, you — developers — have to tell the server which files to load. This is done with a simple PHP script, there really isn't anything difficult to it.

The rules are simple:

  • You provide a preload script and link to it in your php.ini file using opcache.preload
  • Every PHP file you want to be preloaded should be passed to opcache_compile_file() or be required once, from within the preload script

Say you want to preload a framework, Laravel for example. Your script will have to loop over all PHP files in the vendor/laravel directory, and include them one by one.

Here's how you'd link to this script in php.ini:

opcache.preload=/path/to/project/preload.php

And here's a dummy implementation:

$files = /* An array of files you want to preload */;

foreach ($files as $file) {
    opcache_compile_file($file);
}

# Warning: Can't preload unlinked class

Hang on though, there's a caveat! In order for files to be preloaded, their dependencies — interfaces, traits and parent classes — must also be preloaded.

If there are any problems with the class dependencies, you'll be notified of it on server start up:

Can't preload unlinked class 
Illuminate\Database\Query\JoinClause: 
Unknown parent 
Illuminate\Database\Query\Builder

See, opcache_compile_file() will parse a file, but not execute it. This means that if a class has dependencies that aren't preloaded, itself can also not be preloaded.

This isn't a fatal problem, your server will work just fine; but you won't have all the preloaded files you actually wanted.

Luckily, there's a way to ensure linked files are loaded as well: instead of using opcache_compile_file you can use require_once, and let the registered autoloader (probably composer's) take care of the rest.

$files = /* All files in eg. vendor/laravel */;

foreach ($files as $file) {
    require_once($file);
}

There are some caveats still. If you're trying to preload Laravel for example, there are some classes within the framework that have dependencies on other classes that don't exist yet. For example, the filesystem cache class \Illuminate\Filesystem\Cache has a dependency on \League\Flysystem\Cached\Storage\AbstractCache, which might not be installed in your project if you're never using filesystem caches.

You might run into "class not found" errors trying to preload everything. Luckily, in a default Laravel installation, there's only a handful of these classes, which can easily be ignored. For convenience, I wrote a little preloader class to make ignoring files more easy, here's what it looks like:

class Preloader
{
    private array $ignores = [];

    private static int $count = 0;

    private array $paths;

    private array $fileMap;

    public function __construct(string ...$paths)
    {
        $this->paths = $paths;

        // We'll use composer's classmap
        // to easily find which classes to autoload,
        // based on their filename
        $classMap = require __DIR__ . '/vendor/composer/autoload_classmap.php';

        $this->fileMap = array_flip($classMap);
    }
    
    public function paths(string ...$paths): Preloader
    {
        $this->paths = array_merge(
            $this->paths,
            $paths
        );

        return $this;
    }

    public function ignore(string ...$names): Preloader
    {
        $this->ignores = array_merge(
            $this->ignores,
            $names
        );

        return $this;
    }

    public function load(): void
    {
        // We'll loop over all registered paths
        // and load them one by one
        foreach ($this->paths as $path) {
            $this->loadPath(rtrim($path, '/'));
        }

        $count = self::$count;

        echo "[Preloader] Preloaded {$count} classes" . PHP_EOL;
    }

    private function loadPath(string $path): void
    {
        // If the current path is a directory,
        // we'll load all files in it 
        if (is_dir($path)) {
            $this->loadDir($path);

            return;
        }

        // Otherwise we'll just load this one file
        $this->loadFile($path);
    }

    private function loadDir(string $path): void
    {
        $handle = opendir($path);

        // We'll loop over all files and directories
        // in the current path,
        // and load them one by one
        while ($file = readdir($handle)) {
            if (in_array($file, ['.', '..'])) {
                continue;
            }

            $this->loadPath("{$path}/{$file}");
        }

        closedir($handle);
    }

    private function loadFile(string $path): void
    {
        // We resolve the classname from composer's autoload mapping
        $class = $this->fileMap[$path] ?? null;

        // And use it to make sure the class shouldn't be ignored
        if ($this->shouldIgnore($class)) {
            return;
        }

        // Finally we require the path,
        // causing all its dependencies to be loaded as well
        require_once($path);

        self::$count++;

        echo "[Preloader] Preloaded `{$class}`" . PHP_EOL;
    }

    private function shouldIgnore(?string $name): bool
    {
        if ($name === null) {
            return true;
        }

        foreach ($this->ignores as $ignore) {
            if (strpos($name, $ignore) === 0) {
                return true;
            }
        }

        return false;
    }
}

By adding this class in the same preload script, we're now able to load the whole Laravel framework like so:

// …

(new Preloader())
    ->paths(__DIR__ . '/vendor/laravel')
    ->ignore(
        \Illuminate\Filesystem\Cache::class,
        \Illuminate\Log\LogManager::class,
        \Illuminate\Http\Testing\File::class,
        \Illuminate\Http\UploadedFile::class,
        \Illuminate\Support\Carbon::class,
    )
    ->load();

# Does it work?

That's of course the most important question: were all files correctly loaded? You can simply test it by restarting the server, and dump the output of opcache_get_status() in a PHP script. You'll see it has a key called preload_statistics, which will list all preloaded functions, classes and scripts; as well as the memory consumed by the preloaded files.

# Composer support

One promising feature is probably an automated preloading solution based on composer, which is used by most modern day PHP projects already. People are working to add a preload configuration option in composer.json, which in turn will generate the preload file for you! At the moment, this feature is still a work in progress, but you can follow it here.

Update 2019-11-29: composer support has stopped, as can be read by Jordi's answer.

# Server requirements

There's two more important things to mention about the devops side when using preloading.

You already know that you need to specify an entry in php.ini in order for preloading to work. This means that if you're using shared hosting, you won't be able to freely configure PHP however you want. In practice, you'll need a dedicated (virtual) server to be able to optimise the preloaded files for a single project. So keep that in mind.

Also remember you'll need to restart the server (php-fpm is sufficient if you're using it) every time you want to reload the in-memory files. This might seem obvious for most, but still worth the mention.

# Performance

Now to the most important question: does preloading actually improve performance?

The answer is yes, of course: Ben Morel shared some benchmarks, which can be found in the same composer issue linked to earlier. I also did my own benchmarks within a real-life Laravel project. You can read about them here.

Interestingly enough, you could decide to only preload "hot classes" — classes that are used often in your codebase. Ben's benchmarks shows that only loading around 100 hot classes, actually yields better performance gains than preloading everything. It's a difference of a 13% and 17% performance increase.

Which classes should be preloaded relies of course on your specific project. It would be wise to simply preload as much as possible at the start. If you really need the few percentage increases, you would have to monitor your code while running.

All of this can of course also be automated, and will probably be done in the future.

For now, most important to remember is that composer will add support, so that you don't have to make preload files yourself, and that this feature is very easy to setup on your server, given that you've got full control over it.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

Will you be using preloading once PHP 7.4 arrives? Any remarks or thoughts after reading this post? Let me know via Twitter or e-mail.

]]>
2019-07-05T00:00:00+00:00
<![CDATA[ Arrow functions in PHP 7.4 ]]> https://www.stitcher.io/blog/short-closures-in-php Short closures, also called arrow functions, are a way of writing shorter functions in PHP. This notation is useful when passing closures to functions like array_map or array_filter.

This is what they look like:

// A collection of Post objects
$posts = [/* … */];

$ids = array_map(fn($post) => $post->id, $posts);

Previously, you'd had to write this:

$ids = array_map(function ($post) {
    return $post->id;
}, $posts);

Let's summarize how short closures can be used.

  • They are available as of PHP 7.4
  • They start with the fn keyword
  • They can only have one expression, which is the return statement
  • No return keyword allowed
  • Arguments and return types can be type hinted

A more strictly typed way of writing the example above could be this:

$ids = array_map(fn(Post $post): int => $post->id, $posts);

Two more things to mention:

  • The spread operator is also allowed
  • References are allowed, both for the arguments as the return values

If you want to return a value by reference, the following syntax should be used:

fn&($x) => $x

In short, short closures allow the same functionality you'd expect from normal closures, with the exception of only allowing one expression.

# No multi-line

You read it right: short closures can only have one expression; that one expression may be spread over multiple lines for formatting, but it must always be one expression.

The reasoning is as follows: the goal of short closures is to reduce verbosity. fn is of course shorter than function in all cases. Nikita Popov, the creator of the RFC, however argued that if you're dealing with multi-line functions, there is less to be gained by using short closures.

After all, multi-line closures are by definition already more verbose; so being able to skip two keywords (function and return) wouldn't make much of a difference.

Whether you agree with this sentiment is up to you. While I can think of many one-line closures in my projects, there are also plenty of multi-line ones, and I'll personally miss the short syntax in those cases.

There's hope though: it is possible to add multi-line short closures in the future, but that's an RFC on its own.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Values from outer scope

Another significant difference between short and normal closures is that the short ones don't require the use keyword to be able to access data from the outer scope.

$modifier = 5;

array_map(fn($x) => $x * $modifier, $numbers);

It's important to note that you're not allowed to modify variables from the outer scope. Values are bound by value and not by reference. This means that you could change $modifier within the short closure, though it wouldn't have effect on the $modifier variable in the outer scope.

One exception is of course the $this keyword, which acts exactly the same as normal closures:

array_map(fn($x) => $x * $this->modifier, $numbers);

# Future possibilities

I already mentioned multi-line short closures, which is still a future possibility. Another idea floating around is allowing the short closure syntax in classes, for example for getters and setters:

class Post {
    private $title;
 
    fn getTitle() => $this->title;
}

All in all, short closures are a welcome feature, though there is still room for improvement. The biggest one probably being multi-line short closures.

Do you have any thoughts you'd like to share? Feel free to send a tweet or an email my way!

]]>
2019-05-02T00:00:00+00:00
<![CDATA[ Typed properties in PHP 7.4 ]]> https://www.stitcher.io/blog/typed-properties-in-php-74 Typed class properties have been added in PHP 7.4 and provide a major improvement to PHP's type system. These changes are fully opt-in and non breaking to previous versions.

In this post we'll look at the feature in-depth, but first let's start by summarising the most important points:

  • They are available as of PHP 7.4, which is scheduled to be released in November of 2019
  • They are only available in classes and require an access modifier: public, protected or private; or var
  • All types are allowed, except void and callable

This is what they look like in action:

class Foo
{
    public int $a;

    public ?string $b = 'foo';

    private Foo $prop;

    protected static string $static = 'default';
}

If you're unsure about the added benefit of types, I'd recommend you reading this post first.

# Uninitialized

Before looking at the fun stuff, there's an important aspect about typed properties that's essential to talk about first.

Despite what you might think on first sight, the following code is valid:

class Foo
{
    public int $bar;
}

$foo = new Foo;

Even though the value of $bar isn't an integer after making an object of Foo, PHP will only throw an error when $bar is accessed:

var_dump($foo->bar);

Fatal error: Uncaught Error: Typed property Foo::$bar 
must not be accessed before initialization

As you can read from the error message, there's a new kind of "variable state": uninitialized.

If $bar didn't have a type, its value would simply be null. Types can be nullable though, so it's not possible to determine whether a typed nullable property was set, or simply forgotten. That's why "uninitialized" was added.

There are four important things to remember about uninitialized:

  • You cannot read from uninitialized properties, doing so will result in a fatal error.
  • Because uninitialized state is checked when accessing a property, you're able to create an object with an uninitialized property, even though its type is non-nullable.
  • You can write to an uninitialized property before reading from it.
  • Using unset on a typed property will make it uninitialized, while unsetting an untyped property will make it null.

Especially note that the following code, where an uninitialised, non-nullable property is set after constructing the object, is valid

class Foo
{
    public int $a;
}

$foo = new Foo;

$foo->a = 1;

While uninitialized state is only checked when reading the value of a property, type validation is done when writing to it. This means that you can be sure that no invalid type will ever end up as a property's value.

# Defaults and constructors

Let's take a closer look at how typed values can be initialized. In case of scalar types, it's possible to provide a default value:

class Foo
{
    public int $bar = 4;
    
    public ?string $baz = null;
    
    public array $list = [1, 2, 3];
}

Note that you can only use null as a default if the type is actually nullable. This might seem obvious, but there's some legacy behaviour with parameter defaults where the following is allowed:

function passNull(int $i = null)
{ /* … */ }

passNull(null);

Luckily this confusing behaviour is not allowed with typed properties.

Also note that it's impossible to have default values with object or class types. You should use the constructor to set their defaults.

The obvious place to initialize typed values would of course be the constructor:

class Foo
{
    private int $a;

    public function __construct(int $a)
    {
        $this->a = $a;
    }
}

But also remember what I mentioned before: it's valid to write to an uninitialized property, outside of the constructor. As long as there are nothing is reading from a property, the uninitialized check is not performed.

# Types of types

So what exactly can be typed and how? I already mentioned that typed properties will only work in classes (for now), and that they need an access modifier or the var key word in front of them.

As of available types, almost all types can be used, except void and callable.

Because void means the absence of a value, it makes sense that it cannot be used to type a value. callable however is a little more nuanced.

See, a "callable" in PHP can be written like so:

$callable = [$this, 'method'];

Say you'd have the following (broken) code:

class Foo
{
    public callable $callable;
    
    public function __construct(callable $callable)
    { /* … */ }
}

class Bar
{
    public Foo $foo;
    
    public function __construct()
    {
        $this->foo = new Foo([$this, 'method'])
    }
    
    private function method()
    { /* … */ }
}

$bar = new Bar;

($bar->foo->callable)();

In this example, $callable refers to the private Bar::method, but is called within the context of Foo. Because of this problem, it was decided not to add callable support.

It's no big deal though, because Closure is a valid type, which will remember the $this context where it was constructed.

With that out of the way, here's a list of all available types:

  • bool
  • int
  • float
  • string
  • array
  • iterable
  • object
  • ? (nullable)
  • self & parent
  • Classes & interfaces

# Coercion and strict types

PHP, being the dynamic language we love and hate, will try to coerce or convert types whenever possible. Say you pass a string where you expect an integer, PHP will try and convert that string automatically:

function coerce(int $i)
{ /* … */ }

coerce('1'); // 1

The same principles apply to typed properties. The following code is valid and will convert '1' to 1.

class Bar
{
    public int $i;
}

$bar = new Bar;

$bar->i = '1'; // 1

If you don't like this behaviour you can disabled it by declaring strict types:

declare(strict_types=1);

$bar = new Bar;

$bar->i = '1'; // 1

Fatal error: Uncaught TypeError: 
Typed property Bar::$i must be int, string used

# Type variance and inheritance

Even though PHP 7.4 introduced improved type variance, typed properties are still invariant. This means that the following is not valid:

class A {}
class B extends A {}

class Foo
{
    public A $prop;
}

class Bar extends Foo
{
    public B $prop;
}

Fatal error: Type of Bar::$prop must be A (as in class Foo)

If the above example doesn't seem significant, you should take a look at the following:

class Foo
{
    public self $prop;
}

class Bar extends Foo
{
    public self $prop;
}

PHP will replace self behind the scenes with the concrete class it refers to, before running the code. This means that the same error will be thrown in this example. The only way to handle it, is by doing the following:

class Foo
{
    public Foo $prop;
}

class Bar extends Foo
{
    public Foo $prop;
}

Speaking of inheritance, you might find it hard to come up with any good use cases to overwrite the types of inherited properties.

While I agree with that sentiment, it's worth noting that it is possible to change the type of an inherited property, but only if the access modifier also changes from private to protected or public.

The following code is valid:

class Foo
{
    private int $prop;
}

class Bar extends Foo
{
    public string $prop;
}

However, changing a type from nullable to non-nullable or reverse, is not allowed.

class Foo
{
    public int $a;
    public ?int $b;
}

class Bar extends Foo
{
    public ?int $a;
    public int $b;
}

Fatal error: Type of Bar::$a must be int (as in class Foo)

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# There's more!

Like I said at the start of this post, typed properties are a major addition to PHP. There's lots more to say about them. I'd suggest you reading through the RFC to know all the neat little details.

If you're new to PHP 7.4, you probably want to read the full list of changes made and features added. To be honest, it's one of the best releases in a long time, and worth your time!

Finally, if you have any thoughts you want to share on the topic, I'd love to hear from you! You can reach me via Twitter or e-mail.

Until next time!

]]>
2019-06-30T00:00:00+00:00
<![CDATA[ Relationship issues ]]> https://www.stitcher.io/blog/laravel-custom-relation-classes

Or in other words, dealing with complex database relations and Laravel models.

Recently I had to deal with a complex performance issue in one of our larger Laravel projects. Let me quickly set the scene.

We want an admin user to see an overview of all people in the system in a table, and we want a column in that table to list which contracts are active at that moment for each person.

The relation between Contract and Person is as follows:

Contract > HabitantContract > Habitant > Person

I don't want to spend too much time going into details as to how we came to this relationship hierarchy. It's important for you to know that, yes, this hierarchy is important for our use cases: a Contract can have several Habitants, which are linked via a pivot model HabitantContract; and each Habitant has a relation to one Person.

Since we're showing an overview of all people, we'd like to do something like this in our controller:

class PeopleController
{
    public function index() 
    {
        $people = PersonResource::collection(Person::paginate());

        return view('people.index', compact('people'));
    }
}

Let's make clear that this is an oversimplified example, though I hope you get the gist. Ideally, we'd want our resource class to look something like this:

/** @mixin \App\Domain\People\Models\Person */
class PersonResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'name' => $this->name,

            'active_contracts' => $this->activeContracts
                ->map(function (Contract $contract) {
                    return $contract->contract_number;
                })
                ->implode(', '),

            // …
        ];
    }
}

Notice especially the Person::activeContracts relation. How could we make this work?

A first thought might be by using a HasManyThrough relation, but remember that we're 4 levels deep in our relation hierarchy. Besides that, I find HasManyThrough to be very confusing.

We could query the contracts on the fly, one-by-one per person. The issue with that is that we're introducing an n+1 issue since there'll be an extra query per person. Imagine the performance impact if you're dealing with more than just a few models.

One last solution that came to mind was to load all people, all contracts, and map them together manually. In the end that's exactly what I ended up doing, though I did it in the cleanest possible way: using custom relations.

Let's dive in.

# Configuring the Person model

Since we want our $person->activeContracts to work exactly like any other relation, there's little work to be done here: let's add a relation method to our model, just like any other.

class Person extends Model
{
    public function activeContracts(): ActiveContractsRelation
    {
        return new ActiveContractsRelation($this);
    }
}

There's nothing more to do here. Of course we're only starting, since we haven't actually implemented ActiveContractsRelation!

# The custom relation class

Unfortunately there's no documentation on making your own relation classes. Luckily you don't need much to learn about them: some code-diving skills and a little bit of time gets you pretty far. Oh an IDE also helps.

Looking at the existing relation classes provided by Laravel, we learn that there's one base relation that rules them all: Illuminate\Database\Eloquent\Relations\Relation. Extending it means you need to implement some abstract methods.

class ActiveContractsRelation extends Relation
{
    /**
     * Set the base constraints on the relation query.
     *
     * @return void
     */
    public function addConstraints() { /* … */ }

    /**
     * Set the constraints for an eager load of the relation.
     *
     * @param array $models
     *
     * @return void
     */
    public function addEagerConstraints(array $models) { /* … */ }

    /**
     * Initialize the relation on a set of models.
     *
     * @param array $models
     * @param string $relation
     *
     * @return array
     */
    public function initRelation(array $models, $relation) { /* … */ }

    /**
     * Match the eagerly loaded results to their parents.
     *
     * @param array $models
     * @param \Illuminate\Database\Eloquent\Collection $results
     * @param string $relation
     *
     * @return array
     */
    public function match(array $models, Collection $results, $relation) { /* … */ }

    /**
     * Get the results of the relationship.
     *
     * @return mixed
     */
    public function getResults() { /* … */ }
}

The doc blocks get us on the way, though it's not always entirely clear what needs to happen. Again we're in luck, Laravel still has some existing relation classes where we can look to.

Let's go through building our custom relation class step by step. We'll start by overriding the constructor and adding some type hints to the existing properties. Just to make sure, the type system will prevent us from making stupid mistakes.

The abstract Relation constructor requires both specifically for an eloquent Builder class, as well as the parent model the relationship belongs to. The Builder is meant to be the base query object for our related model, Contract, in our case.

Since we're building a relation class specifically for our use case, there's no need to make the builder configurable. Here's what the constructor looks like:

class ActiveContractsRelation extends Relation
{
    /** @var \App\Domain\Contract\Models\Contract|Illuminate\Database\Eloquent\Builder */
    protected $query;

    /** @var \App\Domain\People\Models\Person */
    protected $parent;

    public function __construct(Person $parent)
    {
        parent::__construct(Contract::query(), $parent);
    }

    // …
}

Note that we type hint $query both with the Contract model as well as the Builder class. This allows IDEs to provide better autocompletion, such as custom scopes defined on the model class.

We've got our relation constructed: it will query Contract models, and use a Person model as its parent. Moving on to building our query.

This is where the addConstraints method come in. It will be used to configure the base query. It will set up our relation query specifically to our needs. This is the place where most business rules will be contained:

  • We only want active contracts to show up
  • We only want to load active contracts that belong to a specified person (the $parent of our relation)
  • We might want to eagerly load some other relations, but more on that later.

Here's what addConstraints looks like, for now:

class ActiveContractsRelation extends Relation
{
    // …

    public function addConstraints()
    {
        $this->query
            ->whereActive() // A query scope on our `Contract` model
            ->join(
                'contract_habitants', 
                'contract_habitants.contract_id', 
                '=', 
                'contracts.id'
            )
            ->join(
                'habitants', 
                'habitants.id', 
                '=', 
                'contract_habitants.habitant_id'
            );
    }
}

Now I do assume that you know how basic joins work. Though I will summarize what's happening here: we're building a query that will load all contracts and their habitants, via the contract_habitants pivot table, hence the two joins.

One other constraint is that we only want active contracts to show up; for this we can simply use an existing query scope provided by the Contract model.

With our base query in place, it's time to add the real magic: supporting eager loads. This is where the performance wins are: instead of doing one query per person to load its contracts, we're doing one query to load all contracts, and link these contracts to the correct people afterwards.

This is what addEagerConstraints, initRelation and match are used for. Let's look at them one by one.

First the addEagerConstraints method. This one allows us to modify the query to load in all contracts related to a set of people. Remember we only want two queries, and link the results together afterwards.

class ActiveContractsRelation extends Relation
{
    // …

    public function addEagerConstraints(array $people)
    {
        $this->query->whereIn(
            'habitants.contact_id', 
            collect($people)->pluck('id')
        );
    }
}

Since we joined the habitants table before, this method is fairly easy: we'll only load contracts that belong to the set of people provided.

Next the initRelation. Again this one is rather easy: its goal is to initialise the empty activeContract relationship on every Person model, so that it can be filled afterwards.

class ActiveContractsRelation extends Relation
{
    // …

    public function initRelation(array $people, $relation)
    {
        foreach ($people as $person) {
            $person->setRelation(
                $relation, 
                $this->related->newCollection()
            );
        }

        return $people;
    }
}

Note that the $this->related property is set by the parent Relation class and it's a clean model instance of our base query so in other words, an empty Contract model:

abstract class Relation
{
    public function __construct(Builder $query, Model $parent)
    {
        $this->related = $query->getModel();
    
        // …
    }
    
    // …
}

Finally we arrive at the core function that will solve our problem: linking all people and contracts together.

class ActiveContractsRelation extends Relation
{
    // …

    public function match(array $people, Collection $contracts, $relation)
    {
        if ($contracts->isEmpty()) {
            return $people;
        }

        foreach ($people as $person) {
            $person->setRelation(
                $relation, 
                $contracts->filter(function (Contract $contract) use ($person) {
                    return $contract->habitants->pluck('person_id')->contains($person->id);
                })
            );    
        }

        return $people;
    }
}

Let's walk through what's happening here: on the one hand we've got an array of parent models, the people; on the other hand we've got a collection of contracts, the result of the query executed by our relation class. The goal of the match function is to link them together.

How to do this? It's not that difficult: loop over all people, and search all contracts that belong to each one of them, based on the habitants linked to that contract.

Almost done? Well… there's one more issue. Since we're using the $contract->habitants relation, we need to make sure it is also eagerly loaded, otherwise we just moved the n+1 issue instead of solving it. So it's back to the addEagerConstraints method for a moment.

class ActiveContractsRelation extends Relation
{
    // …

    public function addEagerConstraints(array $people)
    {
        $this->query
            ->whereIn(
                'habitants.contact_id', 
                collect($people)->pluck('id')
            )
            ->with('habitants')
            ->select('contracts.*');
    }
}

We're adding the with call to eagerly load all habitants, but also note the specific select statement. We need to tell Laravel's query builder to only select the data from the contracts table, because otherwise the related habitant data will be merged on the Contract model, causing it to have the wrong ids and what not.

Finally we need to implement the getResults method, which simply executes the query:

class ActiveContractsRelation extends Relation
{
    // …

    public function getResults()
    {
        return $this->query->get();
    }
}

And that's it! Our custom relation can now be used like any other Laravel relation. It's an elegant solution to solving a complex problem the Laravel way.

]]>
2019-11-09T00:00:00+00:00
<![CDATA[ Laravel's HasManyThrough cheatsheet ]]> https://www.stitcher.io/blog/laravel-has-many-through - The current model Country has a relation to Post via User - The intermediate model is linked to the current model via users.country_id - The target model is linked to the intermediate model via posts.user_id - users.country_id maps to countries.id - posts.user_id maps to users.id
countries
    id - integer
    name - string

users
    id - integer
    country_id - integer
    name - string

posts
    id - integer
    user_id - integer
    title - string
class Country extends Model
{
    public function posts()
    {
        return $this->hasManyThrough(
            'App\Post',
            'App\User',
            'country_id', // Foreign key on users table...
            'user_id', // Foreign key on posts table...
            'id', // Local key on countries table...
            'id' // Local key on users table...
        );
    }
}
]]>
2019-11-08T00:00:00+00:00
<![CDATA[ Can I translate your blog? ]]> https://www.stitcher.io/blog/can-i-translate-your-blog Yes. Yes you can. I do ask you to keep a few rules in mind.

# Host on your own

Your translations should be hosted on your own blog. Maybe it's helpful for you to have access to the markdown source files, they can be found here.

# Proper attribution

You're required to add a link to the original post at the start of your translation. Furthermore, please add a link tag in the of your page to also point to the original post.

<head>
    <!-- … -->

    <link 
        rel="alternate" 
        hreflang="en"
        href="https://stitcher.io/blog/can-i-translate-your-blog" />
</head>

# Notify me about translations

I'd like to know about when you put a translation online. I plan on listing all translations on my blog somewhere at some point in the future, so it'd be handy if I could add yours.

You can notify me via Twitter, e-mail or by making a GitHub issue

# Translate, don't interpret

I expect translations to be as close as possible to the original. You can add your own thoughts, but only if properly addressed. For example you can add a final section with additions to the original post, as long as you make it clear that it's not part of the original.


One final remark: I appreciate it very much when people want to translate my content, so thank you very much!

]]>
2019-11-07T00:00:00+00:00
<![CDATA[ Guest posts ]]> https://www.stitcher.io/blog/guest-posts I've been thinking about the idea of allowing other people to write on my blog for a while now. Guest posting is popular in the broader blogging community, and I wanted to give it some careful thought.

I regularly get requests from people to write a guest post on this blog, but these requests often involve low-quality content just to boost SEO stats. This is something I want to stay far away from.

I realised though that there might be people out there who have a great idea for a blog post, but don't want to spend time maintaining a blog, and don't want to post to a network like Medium. Quality content might never be written because of this, and I realised that independent blogs like mine can offer a solution.

I think of it as a win-win: content creators get an outlet for their ideas, my audience gets more quality content to read, and this blog can keep on growing.

# Quality assurance

Regular readers of my blog know that I try my best to only write quality content. I don't keep a schedule which forces me to write every week or month, I only write when I feel like I've got something to say that's worth sharing.

I want guest posts on this blog to be the same. That's why I will personally review every one of them, and will only post them when I think they meet a quality level my audience would appreciate.

I believe a set of rules and agreements is in place. If you're thinking about pitching a guest post, please read these carefully.

# Your content, not mine

Guest posts will always stay your property, not mine. This means that if you ever want to move your content to your own blog, you're free to do so.

This of course also means that you're responsible for writing quality content. I'll be happy to review and help improve your content, but you'll be the one who has to do it.

This also means that guest posts will always be properly attributed. You'll be able to provide links to your social media or personal website.

Lastly, every one of my own blogposts has one ad in it, which helps keep this blog going. You're free to ask me to remove it in your content.

# What to expect from me?

I will help you in reviewing your content and provide feedback to improve it. I'm by no means an expert writer or copy editor myself, but I have been doing this for a few years now, so I might be able to help you if you're a beginning writer.

Reviewing your content will either happen via mail or directly on GitHub, whichever you prefer. I will make sure to always discuss any changes I want to be made to the post, and will never alter your content without you knowing it.

Finally, I will also help promote your content once it's launched, and provide feedback regarding traffic afterwards. I cannot guarantee any hard numbers on what kind of traffic your post will get, but I can give you some numbers on the monthly traffic on my blog:

  • Between 40k and 60k users per month, with 60k-80k sessions per month
  • My blog posts have on average between 1k and 6k sessions per month

# Now what?

Since this is new for me too, I've got no idea where it'll go from here on out. If you're interested in the idea and would like to talk about something concrete, feel free to send me an email and we'll see whether we're a good match. Make sure to provide the following information:

  • If you're a blogger, links to your existing content
  • Your motivation about why you want to write a guest post
  • An abstract about what you want to write about
]]>
2019-09-16T00:00:00+00:00
<![CDATA[ re: re: A letter to the PHP team ]]> https://www.stitcher.io/blog/a-letter-to-the-php-team-reply-to-joe Thanks you Joe for taking the time to reply to my letter, I really appreciate it! I'll be happy to reply here.

Your reply started by addressing the P++ shenanigans:

A lot of the discussion is based on an assertion made during the P++ discussion that there are two camps of developers

As a matter of fact, I started writing my letter before the whole P++ galaxy discussion. Life got in the way though (my son was born at the beginning of August), which is why it took almost a month to publish it.

I just wanted to make clear that I already had many thoughts on the topic before P++ was ever mentioned.

You can tell by looking at the history of RFCs that these factions do not in fact exist

Whether we call it factions or not, if you take a look at recent RFCs, they have almost always gone off topic for to discuss the future of PHP on a broader scale. Sure there might only a few people, but loud voices are heard nevertheless.

A few examples:

This is how most of these discussion go:

  • Nikita tries to move the language forward
  • Zeev and/or Stas advocate for backwards compatibility, resulting in the same long conversation over and over again
  • Sara tries to keep the middle ground
  • Dmitry is working on PHP 8 somewhere in the background, and steers away from these discussions

The rules were written many years ago - arguably for a totally different, pre social coding world - we mostly do a good job of following the rules as they are written.

I think you addressed the essence of the problem: the way PHP internals work is outdated and inefficient in these modern times.

It's important to point out that the rules are not exhaustive, a lot of how we behave is determined by convention. You can argue against this and say that we should try to exhaustively enumerate every possible action

I'd argue we need a sensible and modern day rule set, which can be flexible.

Recently an RFC was conducted to deprecate and remove short PHP tag syntax

While I do have my opinions on maintaining backwards compatibility — which I addressed in the first part of my letter — I think the most important thing to take away from the short syntax RFC is that the process is clearly broken, and needs fixing.

What we have here is a failing of our processes and nothing more. I and likely others are considering how we might avoid this very same failure in the future. It seems desirable at this time to introduce a formal deprecation policy, this both achieves the goal of avoiding this very same failure, and can potentially increase confidence when it comes to adopting new versions of PHP.

Glad we're on the same page on this one, and I know from your comments on internals that you're also looking for a balanced solution. My question is whether this is possible within the current system. It feels like we're going around in circles and very little progress is made.

First, for the sake of clarity. You must be careful how you determine something to be controversial. Loud, is not the same as controversial

That's true, though a few loud voices can impact the development of PHP significantly. There aren't many core contributors, and they have to spend a lot of time reading and replying through the same discussions. I try to keep up-to-date with the internals list myself, so I know this is an exhaustive task.

The time and effort it takes to change our processes is considerable, and only becomes a priority when it's obvious that our processes are failing, or have the potential to fail and do damage.

I think PHP is only slowly evolving because there's so much needless discussions happening over and over again, I'd call that a failing process.

I'm sure that you have a sample of data that shows you this, or you surely wouldn't have made this claim.

Yes, I linked some recent examples previously.

It's a matter of fact that some people can't seem to behave themselves on the internet, while I'm sure (read: must believe) they are reasonable people in real life. These people make themselves obvious very quickly and prove they have nothing much to say.

I think some kind of moderation would be in place, as these people keep coming back, and there's no way to stop them. This is where a mailing list just doesn't suffice.

I can't argue that mailing lists are a good way to communicate, but it's what we have. However, it's not all we have:

True, though the channels you list still don't seem to bridge the gap between core- and userland developers. This is evident by the amount of userland developers voicing their opinions on all kinds of social media. I think a public forum is the way to go here.

This is regularly mentioned, and I think I'll be the first one to point out that to the extent which that is true, it is the communities fault.

There are two groups here:

  • People who are very much invested in PHP, but don't want anything to do with the internal discussions, because of offtopic and exhausting conversations
  • People who would actually want to contribute in the form of votes and input, but don't know how.

The wiki isn't very clear on this:

People with php.net VCS accounts that have contributed code to PHP

Representatives from the PHP community, that will be chosen by those with php.net VCS accounts Lead developers of PHP based projects (frameworks, cms, tools, etc.) regular participant of internals discussions

Personally, I think of myself in this second group, though it's unclear to me whether my perception is correct. And I can think of several other developers who would be an accurate representation of the PHP community.

Should I ask? As a matter of fact, I did ask a core member personally, but didn't get a reply. Should I ask this question on the internals list? I'm unsure.

This is again an example of failing communication between core- and userland developers. There's a barrier that's perceived as uncrossable.

Now I'm not saying there is an actual barrier, I just say that it's perceived by many userland developers this way, myself included.


Once again, thank you very much for your reply Joe. I highly appreciate it and am looking forward to read your comments on mine.

Kind regards
Brent

]]>
2019-08-29T00:00:00+00:00
<![CDATA[ A letter to the PHP team ]]> https://www.stitcher.io/blog/a-letter-to-the-php-team To whomever contributes to PHP, from a userland developer.

Let me start by thanking those who actively work on the PHP project. Those who contribute to the core, extensions, maintain the docs or vote on RFCs: thank you for a language that I can use every day both in my professional and personal life. PHP has been a very useful tool to me for many years, and it's good to see lots of contributors help making it better every day.

I also want to mention that I, as everyone, am subject to confirmation bias. When I address one or two thoughts in this letter, I'll try my best to be as objective as possible, though I realise I'm looking through my lens, and not someone else's.

Since the goal of this letter is to start a conversation, I'm open to hear your thoughts. Also if they don't align with mine, please feel free to disagree.


I could continue by listing lots of good things — there are many. Though because I want to keep this letter on topic, I won't be doing that. Don't take this as me being a disgruntled developer, I simply want to be efficient in conveying what I want to say.

I want to write about how PHP is shaped and developed these days. I feel that I, as a userland developer, know a thing or two about using PHP in real projects. I believe I have an informed and relevant opinion on the matter.

Recently we've seen several discussions regarding the RFC voting process. Besides recent changes to the voting rules, there have also been a few controversial RFCs which passed the vote, and caused some — in some cases, lots of — controversy.

Two recent RFCs come to mind: the deprecation of the short open tags, as well as several small deprecations for PHP 7.4.

Both RFCs caused discussion on whether these changes are actually beneficial to the language, whether they should be allowed with only a 2/3 majority vote, and whether they should be considered harmful to the PHP community.

The basis for most of these discussions is the fact that PHP tries to maintain backwards compatibility as much as possible. One of the main thoughts behind this is that we want users to stay up-to-date with modern PHP versions, so we should give them as little problems as possible to upgrade.

Lessons were, rightfully, learned from the 5.* era. I too share the opinion that all PHP developers and ecosystems should strive to stay up-to-date. It's a message that companies and developers should tell their clients at the start of every project: keeping it secure and up-to-date will take time, cost money, and there's no responsible way to avoid it.

It's a characteristic of professionalism.

On the other hand: if we want to achieve this professionalism with our clients, we are also allowed to spend reasonable amounts of time on upgrades. It is not the end of the world if there's a backwards incompatible change. We can deal with it.

As a day-by-day user of PHP, I have also had my share of legacy projects that needed updating. Let me tell you this: I much more prefer PHP to move forward and mature, rather than me spending less time on upgrades.

In a maturing language, it's evident that some old legacy stuff is cleaned up. It means that the language sometimes removes two ways to do the same thing. It means that, for example, short open tags are deprecated and removed. It means that sometimes my code will break. And as long as the language evolves in a good and healthy way, I don't mind.

If you're one of the enthusiastic guards of backwards compatibility: I know you mean well. But I don't think it's that big a deal you make out of it. The world will not end because there's a breaking change. We, userland developers, will manage.

Let's not waste too much time with seemingly endless discussion over and over again. Let's move forward in balanced way.


Speaking of how we spend time. Internals have been discussing voting mechanics and what to do with controversial RFCs for months now.

Shouldn't we start looking at how other communities do this? For sure PHP can't be the only open source language out there?

Let's call the current way of PHP's development for what it really is: the same discussions happen over and over again on a weekly or monthly basis without any progress; people are personally attacking others regularly; an insignificant RFC takes months of discussion and requires a re-vote after being accepted; there aren't any good ways to share constructive feedback apart from the big mailing list; the group of voters doesn't seem to be an accurate representation the actual PHP community.

Am I fair to call this system, at least partially, broken?

I believe our system should be thoroughly evaluated, and I think we should look at how open source communities outside of PHP manage to keep moving their project forward in a healthy way.

One example is TC39, the committee that manages ECMAScript, aka JavaScript. Dr. Axel Rauschmayer wrote a great post about how the TC39 process works. Now, you might love or hate JavaScript, but it's clear that they are doing something right over there, given the lasting success of the language over the years.

One of the things they get right is an open communication channel with their community. Communication that is transparent and accessible between contributors and users via GitHub. Another language that does this is Rust, which provides an open forum to discuss how the language is shaped.

An open place like GitHub or a forum diminishes the barrier most userland developers experience with the internals mailing list. Many of us read it, though very little userland developers actually feel they can voice their opinion on it. I think there's two reasons for this:

  • The mailing list is difficult to navigate compared to forums and threads
  • It's often a hostile place, lacking basic decency and proper moderation

Better communication will close the current disconnect between the two groups, it will allow PHP to become what the actual majority of PHP users want it to be.

Besides communication, there's the matter of what features should be added to the language. TC39 provides a clear framework on how the language can evolve; it's a system that's superior to and less confusing than PHP's current RFC process.

I already mentioned that the RFC process has been an on-and-off hot debate for the past months; it's at the point where many RFC proposals on the mailing list spark the same discussion over and over again, with no results. Let's again look at committees like TC39, and fix it once and for all.


There's more things to do to fix the current broken process of PHP's development, but I can't possibly list everything here today. So I think it'd be good to keep the conversation going. My suggestion would be to go over to Reddit where we can discuss it further, or send me an email.

Kind regards
Brent


Update August 29: Joe Watkins was kind enough to write a reply. You can read it here.

You can read my reply to Joe's here.

]]>
2019-08-28T00:00:00+00:00
<![CDATA[ Things dependency injection is not about ]]> https://www.stitcher.io/blog/things-dependency-injection-is-not-about If you're using any modern framework, chances are you're heavily relying on dependency injection. But do you know what dependency injection actually is about — or better: what it's not?

# The dependency container

While every modern framework ships with a dependency container — a big box that knows how to construct objects for you — it doesn't guarantee you'll actually be using the dependency injection pattern the way it's supposed to be.

The container can make it much easier to have dependencies injected into a class, but it can also be abused quite a lot.

# Service location

One way to (ab)use the container is to pull objects from it, instead of having them injected into the current context. This pattern is called "service location", and is the opposite of dependency injection. It looks like this:

class MyController
{
    public function indexAction()
    {
        $service = app(Service::class);

        // …        
    }
}

Service location will ask the container for a specific object. This makes the context you're pulling this service from a difficult point to test, as well as a black box to the outside: you're unable to know what kind of external dependencies MyController uses, without looking at all of the code.

Some frameworks promote this use of the container, because it can be simple and fast at the start of a project. In projects with hundreds, maybe even thousands of classes registered in the container, the use of service location can and will become a mess; one that proper use of dependency injection would solve.

I also recommend you to read my post on why service location is an anti-pattern.

# Shared dependencies

Moving on to some more positive vibes: making use of the container in a good way.

When dependency injection is properly used, the outside context — in many cases the container — has control over the concrete dependency that it's injecting into a class. This means that the same object can be injected into several other contexts, without those contexts having to know anything about them being "singletons" or "shared dependencies".

Even though sharing dependencies can be a good and powerful thing to do, it is still not what dependency injection is about, but rather a beneficial side effect.

# Auto wiring

Finally, another useful feature that, again, isn't what dependency injection is about: autowiring.

To give developers more flexibility, some containers allow for smart, automatically determined, class definitions. This means you don't have to manually describe how every class should be constructed. These containers will scan your code, and determine which dependencies are needed by looking at type hints and doc blocks.

A lot of magic happens here, but auto wiring can be a useful tool for rapid application development.


If by now, you want a refresher on the basics of what dependency injection is about. You can go read up on it here.

]]>
2019-07-30T00:00:00+00:00
<![CDATA[ Tests and types ]]> https://www.stitcher.io/blog/tests-and-types Imagine a simple function: rgbToHex. It takes three arguments, integers between 0 and 255; and converts it to a hexadecimal string.

Here's what this function's definition might look like in a dynamic, weakly typed language:

rgbToHex(red, green, blue) {
    // …
}

I think we all agree that "program correctness" is essential. We don't want any bugs, so we write tests.

assert(rgbToHex(0, 0, 0) == '000000')

assert(rgbToHex(255, 255, 255) == 'ffffff')

assert(rgbToHex(238, 66, 244) == 'ee42f4')

Because of our tests, we can be sure our implementation works as expected. Right?

Well… We're actually only testing three out of the 16,777,216 possible colour combinations. But human reasoning tells us that if these three cases work, all probably do.

What happens though if we pass doubles instead of integers?

rgbToHex(1.5, 20.2, 100.1)

Or numbers outside of the allowed range?

rgbToHex(-504, 305, -59)

What about null?

rgbToHex(null, null, null)

Or strings?

rgbToHex("red", "green", "blue")

Or the wrong amount of arguments?

rgbToHex()

rgbToHex(1, 2)

rgbToHex(1, 2, 3, 4)

Or a combination of the above?

I can easily think of five edge-cases we need to test, before there's relative certainty our program does what it needs to do. That's at least eight tests we need to write — and I'm sure you can come up with a few others given the time.

These are the kind of problems a type system aims to partially solve. And note that word partially, we'll come back to it.

If we filter input by a type — you can think of it as a subcategory of all available input — many of the tests become obsolete.

Say we'd only allow integers:

rgbToHex(Int red, Int green, Int blue) 
{
    // …
}

Let's take a look at the tests that aren't necessary anymore thanks to the Int type:

  • Whether the input is numeric
  • Whether the input is a whole number
  • Whether the input isn't null

To be honest, we can do better than this: we still need to check whether the input number is between 0 and 255.

Unfortunately at this point, we run against the limitations of many type systems. Sure we can use Int, though in many cases (as with ours) the category described by this type is still too large for our business logic. Some languages have a UInt or "unsigned integer" type; yet this still too large a subset of "numeric data".

Luckily, there are ways to address this issue.

One approach could be to use "configurable" or generic types, for example Int. The concept of generics is known in many programming languages, though I'm unaware of any language that let's you configure scalar types such as integers.

Edit: one of my readers let me know this is possible in Ada. Thanks, Adam!

Nevertheless in theory, a type could be preconfigured in such a way that it's smart enough to know about your business logic.

Languages that lack these kinds of generic types, often need to build custom types. Being an OO programmer myself, I would use classes to do this.

class MinMaxInt
{
    public MinMaxInt(Int min, Int max, Int value)
    {
        assert(min <= value <= max)
        
        this.value = value
    }
}

If we're using an instance of MinMaxInt, we can be sure its value is constrained within a subset of integers.

Still, this MinMaxInt class is too generic for our case. If we were to type rgbToHex with it, we're still not sure what the exact boundaries are:

rgbToHex(MinMaxInt red, MinMaxInt green, MinMaxInt blue) 
{
    // …
}

We need a more specific type: RgbValue. Adding it depends, again, on the programming language and personal preference. I would extend MinMaxInt, but feel free to do whatever fits you best.

class RgbValue extends MinMaxInt
{
    public RgbValue(Int value)
    {
        parent(0, 255, value)
    }
}

Now we've arrived at a working solution. By using the RgbValue type, most of our tests become redundant.

rgbToHex(RgbValue red, RgbValue green, RgbValue blue) 
{
    // …
}

We can now have one test to test the business logic: "given three RGB-valid colors, does this function return the correct HEX value?" — a great improvement!

# Caveats

Close readers can already think of one or two counter arguments. Let's address them.

# Tests are just moved

If we're building custom types, we still have to test those. That's true in my example, which is influenced by the languages I work in.

It depends on the capabilities of the language though. Given a language that allows this:

rgbToHex(
    Int<0, 255> red, 
    Int<0, 255> green, 
    Int<0, 255> blue
) {
    // …
}

You'd need zero extra tests, as the features are baked into the language itself.

But even if we're stuck with having to build custom types and testing them: don't forget they are reusable throughout the code base.

Chances are you'll be able to re-use most of the types you're making; as these custom categories most likely apply to your business, and are used throughout it.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Verbosity

Next, many would consider my solution too verbose when actually using it:

rgbToHex(
    new RgbValue(60),
    new RgbValue(102),
    new RgbValue(79)
);

While I personally don't mind this verbosity — I know the benefits of a stronger type system — I'd like to ask you to think out of your box for a moment. This argument isn't against stronger types, it's one against your programming language.

The verbosity is caused by the lack of proper syntax provided by the language. Fortunately I can think of ways the problem could be solved.

One solution is type juggling. Dynamic languages are actually pretty good at it. Say you'd pass a simple integer as the input, the compiler can try and cast that integer to an object of RgbValue. It could even be aware of possible types which could be cast to RgbValue, so you'd still have compile-time error detection.

# Example in isolation

Another objection might be that your real-life code base obviously differs from a simple rgbToHex function.

I want to argue the opposite though: the reasoning behind this example can be applied to any part of your code. The actual difficulty lies in the languages and frameworks used: if strong types aren't built in from the ground up, you're gonna have a hard time getting the most use out of them.

This is where I should recommend you to watch this talk by Gary Bernhardt, it's less than 30 minutes long. In it, he takes the topic of type systems and confronts us with our own prejudices and ideologies about them.

Afterwards you can apply this thinking on the current frameworks and languages you're using.

While my example is an example in isolation, the underlying problems solved by stronger types can easily be scaled, if the infrastructure supports it.

So am I suggesting you should ditch your whole stack, or that you're a bad programmer for using a weakly typed language? Definitely not!

I myself program in PHP every day, it's not as bad as it used to be. PHP introduced an opt-in type system, so it's possible to write fairly strongly typed code, even though the language wasn't originally built for it. Another example coming to mind is JavaScript with TypeScript.

So it is possible to leverage a type system, even in many languages that weren't originally built for it. But it will require a mind shift from your side. In my experience, it's worth the effort.

# Limitations

Finally, let's address the elephant in the room when it comes to type systems. I hope it's clear that, while many tests may be omitted thanks to a strong type system, some still need to be written.

People claiming you don't have to write tests in strongly typed languages are wrong.

Remember the partially I mentioned earlier?

In an ideal world, the perfect type system would be able to account for all specific categories required by your business. This is impossible to do though, as computers and programming languages only have limited resources to work with.

So while strong types can help us to ensure program correctness, some tests will always be a necessity to ensure business correctness. It's a matter of "both and", not "either or".

# Closing Remarks

I mentioned several concepts in this posts, but I also mentioned I didn't know of programming languages using some of the concepts I described. I'd love to give some concrete examples though.

So if you're working in a language that should be mentioned in this post, please let me know via Twitter, e-mail, or wherever you read this post on social media.

You can of course also reach out to share other thoughts on this topic, I'd love to hear from you!

]]>
2019-06-07T00:00:00+00:00
<![CDATA[ What are objects anyway? - Rant With Brent 02 ]]> https://www.stitcher.io/blog/what-are-objects-anyway-rant-with-brent In this second episode, I take a look at the original definition of "Object Oriented Programming", and compare it to what we call OOP today.

You can download the episode here or listen on Apple Podcasts, Stitcher Radio and Spotify

]]>
2019-06-05T00:00:00+00:00
<![CDATA[ A project at Spatie ]]> https://www.stitcher.io/blog/a-project-at-spatie The month of May marks the first year anniversary of a client project I've been working on at Spatie. I thought it useful to share some statistics with the community, and give you a feeling of what a "real life web project" might look like.

Let's start with a general overview. The project, a web application, features an admin interface to manage inventories, contacts and contracts; bookings, automatic invoicing and about ten third party integrations.

In the future we'll be exposing several of these features to the outside via an API, its main goal to power a mobile app for clients of the platform. The admin panel is already in use in production.

The project is built in Laravel, a PHP framework. We use Blade as a templating engine in combination with many VueJS components. Tailwind is the CSS framework used.

# Some numbers

So, how much code have we written the past year? Here's a summary, gathered with the phploc package:

  • 2,062 files
  • 126,736 lines of code
  • 97,423 logical lines of code

Let's zoom into statistics about the backend code, my area:

  • 1,086 classes; 32 interfaces; 28 traits
  • Average LLOC per class: 8
  • Maximum LLOC per class: 139
  • 3,245 public methods

The amount of lines split per file type looks like this:

Let's further dive into how the backend code is structured, by using Stefan's laravel-stats package.

To start with, I should explain something about our big Laravel projects. Instead of using the default Laravel project structure, our code is split into two namespaces: "application code" and "domain code".

Domain code holds all business logic and is used by the application layer. If you want to dive further into this topic, you can read about it here.

The following graph shows how application and domain code relate to each other:

By splitting business and application code, we're able to provide a flexible, maintainable and highly testable core. Application code makes use of this core and looks very much like your average Laravel project.

The bulk of our domain code consists of three types of classes:

  • Models — 80 classes
  • Actions — 205 classes
  • DTOs — 63 classes

While the application layer mostly consists of:

  • Controllers — 130 classes and 309 routes
  • ViewModels — 82 classes
  • Blade views — 313 files; these are not included in the chart above

Because of the lifecycle of the project, there's room for improvement. For example, we're not using DTOs everywhere, as they were added at a later time.

As with all things: we learn as we go. After a year, it's normal that some parts of the code can be considered "old". We have a rule that states that when we work in these old parts of the codebase, we refactor them.

A big advantage of moving code into domains is testability. While our domain code is heavily unit tested, our application code is mostly only integration tested. In our experience, it's a workable balance between highly tested code and being able to meet deadlines.

At the moment we have 840 tests doing 1,728 assertions. Our test suite could always be improved, but I am very confident deploying new features and refactors without the fear of breaking stuff — thanks to our test suite.

# Code structure

I'm a big proponent of clean code. We try to keep our code clean and maintainable, by setting a few rules of thumb:

  • Classes should be small, 50 lines of code should be the maximum
  • Methods should also be small and easy to reason about
  • We prefer clear names over short and cryptic names

You probably noticed that we don't always keep these rules. There are some classes that are longer and more complex.

These classes are the result of making choices: sometimes some technical debt is allowed to meet deadlines — as long as we're aware of it.

I've made a little tool in the past which I use to generate "heat maps" of the codebase. It will take all code in a folder, and generate an image by overlaying the code structure on top of it.

I can use this tool to locate large files, and refactor them when there's time. We have done this in the past, and it works very well.

Here's part of this image of a subdomain in our project:

The darker the image, the more code across all files in that position. You can see that while some files are longer, most of the code lives in the upper 50 lines, something we strive for.

We ensure these short classes and consistent code by using a few tools and methods:

  • Internal PRs and code reviews; despite what you might think, this saves time
  • We use static analysis, more specifically PhpStan; to prevent subtle bugs
  • We use PHP CS fixer to ensure consistent code style

Like I said before: I'm a firm proponent of clean code. When you're working with several people in the same codebase, it's a must to keep your code clean and clear, to secure its future.

# In closing

Finally, I'd like to show the GIT history of the project visualised with Gource. We've been working on this project with, in total, 7 contributors, and now have more than 4,000 commits listed.

You can clearly see the different "branches" I talked about earlier: application- and domain code; but this overview also includes Blade, JavaScript and CSS files.


So what about your projects? Are you able to share your own stats? Feel free to send me a tweet or an email!

]]>
2019-05-30T00:00:00+00:00
<![CDATA[ A programmer's cognitive load ]]> https://www.stitcher.io/blog/a-programmers-cognitive-load As a professional programmer, I'm reading and writing code on a daily basis. I'm working on new projects, doing code reviews, working with legacy code, reading through documentation etc. Based on my own experience and that of colleagues, being a programmer often involves a lot more reading than actually writing code.

Whether it's your own code or that of others, when you open a file you have to take it all in. You need to wrap your head around what's going on, before you're able to write your code, do your thing.

Having to deal with code almost every day, it's important to find ways to make this process easy. To try and reduce the cognitive load it puts on your brain as much as possible.

Making code easier to read will allow you to work faster and better, and also improve your mental state and mood.

In cognitive psychology, cognitive load refers to the total amount of mental effort being used in the working memory - wikipedia

Today I want to share some techniques that can help you reduce cognitive load while coding. In contrast to some recent advocates of "visual debt", I won't talk about stripping away pieces of your codebase. We'll look purely into the visual aspect: what makes code hard to read and reason about, and how to make it easier.

# Fonts and spacing

Fonts have an influence on our mood. The people at Crew wrote an interesting piece about how fonts make us feel. The font you choose has a big impact on how much load is put on your brain to process the text on your screen. It's not just the font by the way, but also its size, line height and letter spacing plays a role.

Typography itself is a topic books are written about. I encourage you to think about your current font choice, and how it influences the way you read code.

Here's a comparison between a not-so-good and better font configuration, in my experience.

# Folding

Ever worked with a controller providing some CRUD actions? A class with a few methods? Folding your method bodies by default gives you a much clearer overview of the class when opening a file. It makes it easier to decide where you want to go to, instead of scrolling and searching. Take a look at the following example.

IDEs like IntelliJ can fold code by default: Settings > Editor > General > Code Folding. I was a bit hesitant to enable it by default, but I can assure you this is an amazing feature once you're used to it.

It's also more convenient compared to the file structure navigator many IDEs and editors provide. This approach allows you to see the visual structure, color and indentation of the class.

You'll probably want to learn the keybinds associated with folding too. On Mac with IntelliJ, these are the defaults: ⌘⇧+, ⌘⇧-, ⌘+ and ⌘-.

# Documentation

Documentation and comments are good tool to clarify what code actually does. Furthermore, some languages and IDEs rely on comment meta data to provide proper static analysis. We shouldn't overdo it though: docblocks and comments often state the obvious things, which are already known by reading the code.

After several years of programming, I can safely say that about 80-90% of comments are redundant. They should be removed to clear visual overload, but you also need to provide something in their place:

  • Clear naming of methods, variables, constants, etc.
  • Use proper type annotations and definitions

Self documented code is better than relying on comments. My rule of thumb when adding a comment is asking the following question: "Does this comment actually add more information than already available through the code?". If the answer is no, the comment shouldn't be there.

Removing these comment frees up your code, giving you visual "space to breath".

# Naming things

Another important thing to keep in mind: how to name things? It's better to give a variable a longer, descriptive name; rather than make them as short as possible.

Short, cryptic names make sense at the moment of writing the code; but even a few days after you've written them, they already become vague and meaningless.

Better to write a little more, than to read ten times as much to understand what you've written in the past.

Here are a few examples from an project of mine:

  • I renamed createPage() to createPaginatedPage().
  • $process became $pageRenderProcess.
  • testStitcher() changed to multiple methods, one of which called test_stitch_multiple_routes().

# Colours

This might be a sensitive topic for many people; though I want to ask you to give me the benefit of the doubt for a minute. We all like our dark, cool looking colour schemes, but there's a problem with them.

Research shows that the human eye is better equipped to read dark text on light backgrounds, than the other way around. Back in the 80's, when personal computing was growing in popularity, a guy called Etienne Grandjean did an extensive study on how text is best read on screens.

It turned out that light colour schemes are, for almost all people, the better choice.

Now your first thought might be that light colour schemes actually hurt your eyes, but this has more to do with the brightness of your screen, than the colour scheme itself.

It's true that a light colour scheme can cause headaches and painful eyes if you don't turn down the brightness of your screen. On the other hand, a less bright screen in combination with a light colour scheme will put less load on your eyes, which makes reading code for long periods of time less exhaustive.

I can say what I want of course, but the best thing I could do is challenge you to a one-week tryout. I've challenged many programmers before, and most of them were actually convinced of a light theme after a few days.

It's up to you! Here's how my code looks like these days:

Be sure to let me know how the challenge went! You can reach me on Twitter or via e-mail.


The points I listed today have almost nothing to do with how you write real code (programming logic, patterns used, etc.). But they have an impact on the cognitive load put on your brain day by day.

They take away some of the pain points when writing code. They allow you to enjoy programming more.

]]>
2017-06-25T00:00:00+00:00
<![CDATA[ I'm starting a podcast ]]> https://www.stitcher.io/blog/starting-a-podcast It's only been two weeks since I announced I'm starting a newsletter, and here I am again with something new: a podcast.

People who know me IRL know I listen to lots of podcasts every day. I did some internet radio a few years ago, and since then always wanted to pick it up again.

Ever since my wife and I bought our house, I've slowly been working on my amateur "studio", and I've come to the point where I feel comfortable recording in it. Here's my view when recording:

Anyway, I just wanted to let you know that I'm starting this thing. Just like the newsletter it'll be sporadic. I prefer quality over quantity, so don't expect anything weekly.

I called it "Rant With Brent", and if you'd ask me to summarize, I'd say it's a no-nonsense, straight to the point podcast about programming. I don't want to waste 30 minutes of your time, when the same could be said in 10.

I do my best to make an episode highly informative and worth your time. If you want to give it a chance, you can find the podcast on iTunes, Stitcher and Spotify.

]]>
2019-05-14T00:00:00+00:00
<![CDATA[ PHP in 2019 ]]> https://www.stitcher.io/blog/php-in-2019 Do you remember the popular "PHP: a fractal of bad design" blog post? The first time I read it, I was working in a crappy place with lots of legacy PHP projects. This article got me wondering whether I should just quit and go do something entirely different than programming.

Luckily for me I was able to switch jobs shortly thereafter and, more importantly, PHP managed to evolve quite a bit since the 5.* days. Today I'm addressing the people who are either not programming in PHP anymore, or are stuck in legacy projects.

Spoiler: some things still suck today, just like almost every programming language has its quirks. Many core functions still have their inconsistent method signatures, there are still confusing configuration settings, there are still many developers out there writing crappy code — because they have to, or because they don't know better.

Today I want to look at the bright side: let's focus on the things that have changed and ways to write clean and maintainable PHP code. I want to ask you to set aside any prejudice for just a few minutes.

Afterwards you're free to think exactly the same about PHP as you did before. Though chances are you will be surprised by some of the improvements made to PHP in the last few years.

# TL;DR

  • PHP is actively developed with a new release each year
  • PHP 7.4 is one of the most feature-packed releases ever
  • Performance since the PHP 5 era has doubled, if not tripled
  • There's a extremely active eco system of frameworks, packages and platforms
  • PHP has had lots of new features added to it over the past few years, and the language keeps evolving
  • Tooling like static analysers has matured over the past years, and only keeps growing

Update: people asked me to show some actual code. I'm happy to say that's possible! Here's the source code of one of my hobby projects, written in PHP and Laravel; and here is a list of a few hundred OSS packages we maintain at our office. Both are good examples of what modern PHP projects look like.

Let's start.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# History summarized

For good measure, let's quickly review PHP's release cycle today. We're at PHP 7.4 now, and PHP 8.0 will be the next version after that.

Ever since the late 5.* era, the core team tries to keep a yearly release cycle, and have succeeded in doing so for the past four years.

In general, every new release is actively supported for two years, and gets one more year of "security fixes only". The goal is to motivate PHP developers to stay up-to-date as much as possible: small upgrades every year are way more easy than making the jump between 5.4 to 7.0, for example.

An active overview of PHP's timeline can be found here.

Lastly, PHP 5.6 was the latest 5.* release, with 7.0 being the next one. If you want to know what happened to PHP 6, you can listen to the PHP Roundtable podcast.

With that out of the way, let's debunk some common misconceptions about modern PHP.

# PHP's performance

Back in the 5.* days, PHP's performance was… average at best. With 7.0 though, large parts of PHP's core were rewritten from the ground up, resulting in two or three times performance increases.

Words don't suffice though. Let's look at benchmarks. Luckily other people have spent lots of time in benchmarking PHP performance. I find that Kinsta has a good updated list.

Ever since the 7.0 upgrade, performance only increased. So much that PHP web applications have comparable — in some cases better — performance than web frameworks in other languages. Take a look at this extensive benchmark suite.

Sure PHP frameworks won't outperform C and Rust, but they are equally performant as Django, Rails or Express.

# Frameworks and ecosystem

Speaking of frameworks: PHP isn't just WordPress anymore. Let me tell you something as a professional PHP developer: WordPress isn't in any way representative of the contemporary ecosystem.

In general there are two major web application frameworks, and a few smaller ones: Symfony and Laravel. Sure there's also Zend, now called Laminas; Yii, Cake, Code Igniter etc. — but if you want to know what modern PHP development looks like, you're good with one of the first two.

Both frameworks have a large ecosystem of packages and products. Ranging from admin panels and CRMs to standalone packages, CI to profilers, numerous services like web sockets servers, queuing managers, payment integrations; honestly there's too much to list.

These frameworks are meant for actual development though. If you're in need of pure content management, platforms like WordPress and CraftCMS are only improving more and more.

One way to measure the current state of PHP's ecosystem is to look at Packagist, the main package repository for PHP. It has seen exponential growth. With ±25 million downloads a day, it's fair to say that the PHP ecosystem isn't the small underdog it used to be.

Take a look at this graph, listing the amount of packages and versions over time. It can also be found on the Packagist website.

Besides application frameworks and CMSs, we've also seen the rise of asynchronous frameworks the past years. These are frameworks and servers, written in PHP or other languages, that allow users to run truly asynchronous PHP. A few examples include Swoole, Amp and ReactPHP.

Since we've ventured into the async world, stuff like web sockets and applications with lots of IO have become actually relevant in the PHP world.

There has also been talk on the internals mailing list — the place where core developers discuss the development of the language — to add libuv to the core. For those unaware of libuv: it's the same library Node.js uses to allow all its asynchronicity.

# The language itself

While async and await are not available yet, lots of improvements to the language itself have been made over the past years. Here's a non-exhaustive list of new features in PHP:

While we're on the topic of language features, let's also talk about the process of how the language is developed today. There's an active core team of volunteers who move the language forward, though the community is allowed to propose RFCs.

Next, these RFCs are discussed on the "internals" mailing list, which can also be read online. Before a new language feature is added, there must be a vote. Only RFC with at least a 2/3 majority are allowed in the core.

Recently there has been an effort to move RFC discussions to GitHub, where the code actually lives. It seems like the small group of developers who's actively invested in PHP, are on board. You can read about the process here. It's still a work in progress because some people don't like GitHub. It's clear though that the most active contributors are on board with the decision, so it's fair to say the RFC discussion process will be moved to GitHub entirely within the next year or so.

Once an RFC is discussed, it goes into the voting phase. There are probably around 100 people allowed to vote, though you're not required to vote on each RFC. Members of the core team are of course allowed to vote, they have to maintain the code base. Besides them, there's a group of people who have been individually picked from the PHP community. These people include maintainers of the PHP docs, contributors to the PHP project as a whole, and prominent developers in the PHP community.

While most of core development is done on a voluntary basis, one of the core PHP developers, Nikita Popov, has recently been employed by JetBrains to work on the language full time. Another example is the Linux foundation who recently decided to invest into Zend framework, now called Laminas. Employments and acquisitions like these ensure stability for the future development of PHP.

# Tooling

Besides lots of features and improvements added to PHP's core, we've seen an increase in tools around it the past few years. What comes to mind are static analysers like Psalm, created by Vimeo; Phan and PHPStan.

These tools will statically analyse your PHP code and report any type errors, possible bugs etc. In some way, the functionality they provide can be compared to TypeScript, though for now the language isn't transpiled, so no custom syntax is allowed.

Even though that means we need to rely on docblocks, Rasmus Lerdorf, the original creator of PHP, did mention the idea of adding a static analysis engine to the core. While there would be lots of potential, it is a huge undertaking.

Speaking of transpiling, and inspired by the JavaScript community; there have been efforts to extend PHPs syntax in user land. A project called Pre does exactly that: allow new PHP syntax which is transpiled to normal PHP code. While the idea has proven itself in the JavaScript world, it could only work in PHP if proper IDE- and static analysis support was provided. It's a very interesting idea, but has to grow before being able to call it "mainstream".

# In closing

All of that being said, feel free to still think of PHP as a crappy language. While the language definitely has its drawbacks and 20 years of legacy to carry with it; I can say in confidence that I enjoy working with it.

In my experience, I'm able to create reliable, maintainable and quality software. The clients I work for are happy with the end result, as am I. While it's still possible to do lots of messed up things with PHP, I'd say it's a great choice for web development if used wise and correct.

Don't you agree? Let me know why! You can reach me via Twitter or e-mail.

]]>
2019-05-10T00:00:00+00:00
<![CDATA[ SOLID, interfaces and final - Rant With Brent 01 ]]> https://www.stitcher.io/blog/solid-interfaces-and-final-rant-with-brent I'm giving podcasting a try. Let me know what you think of it via Twitter or e-mail.

In this episode I talk about why I think final helps you write better maintainable code.

You can download the episode here or listen in iTunes, Stitcher and Spotify

]]>
2019-05-07T00:00:00+00:00
<![CDATA[ I'm starting a newsletter ]]> https://www.stitcher.io/blog/starting-a-newsletter I'm going to give it a try: newsletters. I'm always on the lookout for connecting more with my audience, and feel like newsletters are a more personalised way of communication: people can hit reply to engage in a one-on-one conversation. For me, email is the perfect platform to do that.

# The concept

What to expect if you subscribe? First of all I'll try to send out one newsletter every month, more or less. I won't spam you every day or week. This also gives me ample time to write, review and rewrite the mail. That's the same way I write my blog posts, and works very well for me.

My goal is to write a mini-blog post for every mail, about a topic I've encountered the past month. Next I'll also give some useful php-related tips and tricks. Finally I'll share one or two, maybe three; links to interesting reads on the web.

That's what I envision the newsletter to look like, at least for now.

So, if you're interested, here's the signup link: https://stitcher.io/signup. The first edition will be sent sometime in the next two weeks.

]]>
2019-04-26T00:00:00+00:00
<![CDATA[ Unsafe SQL functions in Laravel ]]> https://www.stitcher.io/blog/unsafe-sql-functions-in-laravel I recently learned that not all query builder functionality in Laravel is "safe". This means that user input shouldn't be passed directly to it, as it might expose your application to SQL injection vulnerabilities.

The past few days it became clear that there is little community knowledge about these unsafe functions. Many developers assume, as did I, that the Laravel query builder completely prevents SQL injection attacks.

This blog post aims to raise awareness about what's safe, and what's not.

# An SQL injection vulnerability?

Let's start by mentioning that this vulnerability has been fixed as of Laravel 5.8.11. While technically we could call this a "vulnerability", Laravel developers should know that they also play a role in preventing these kinds of issues.

Let's examine the issue.

Laravel has the ability to manually specify which columns to select on a query. It also offers the shorthand notation to query JSON data:

<hljs type>Blog</hljs>::<hljs prop>query</hljs>()
    -><hljs prop>addSelect</hljs>('<hljs green>title</hljs>-><hljs blue>en</hljs>');
<hljs keyword>SELECT</hljs> <hljs prop>json_extract</hljs>(`<hljs green>title</hljs>`, '$."<hljs blue>en</hljs>"') <hljs keyword>FROM</hljs> blogs;

Instead of manually writing json_extract, we can use the simplified -> syntax, which Laravel will convert to the correct SQL statement.

Be careful though: Laravel won't do any escaping during this conversion. Consider the following example:

<hljs type>Blog</hljs>::<hljs prop>query</hljs>()
    -><hljs prop>addSelect</hljs>('<hljs green>title</hljs>-><hljs blue>en</hljs><hljs red>'#</hljs>');

By inserting '# in our input, we can manually close the json_extract function, and ignore the rest of the query:

<hljs keyword>SELECT</hljs> <hljs prop>json_extract</hljs>(`<hljs green>title</hljs>`, '$."<hljs blue>en</hljs><hljs red>'#</hljs><hljs textgrey>"') FROM blogs;</hljs>

This query will fail because of syntax errors, but what about the next one?

<hljs keyword>SELECT</hljs> <hljs prop>json_extract</hljs>(
    `<hljs green>title</hljs>`, 
    '$."<hljs blue>en</hljs><hljs red>"')) 
FROM blogs RIGHT OUTER JOIN users ON users.id <> null
#</hljs>
    <hljs textgrey>"') FROM blogs;</hljs>

We're adding an outer join on the users table. Essentially selecting all data in it.

For reference, this is the URL encoded version of the malicious code:

%22%27%29%29+FROM+blogs+RIGHT+OUTER+JOIN+users+ON+users.id+%3C%3E+null%23

Say we have the following endpoint in our application, to query blog posts from a public API:

Route::get('/posts', function (Request $request) {
    $fields = $request->get('fields', []);

    $users = Blog::query()->addSelect($fields)->get();

    return response()->json($users);
});

Consumers of this API might only be interested in a few fields, that's why we added a fields filter. Something similar to sparse fieldsets from the JSON api spec.

The endpoint can now be used like this:

/blog?fields[]=url&fields[]=title

Now we insert our malicious code instead:

/blog?fields[]=%22%27%29%29+FROM+blogs+RIGHT+OUTER+JOIN+users+ON+users.id+%3C%3E+null%23

It will be added to the query. And by returning the query result as JSON, we'll see the full contents of the users table.

Blog::query()->addSelect([
    '%22%27%29%29+FROM+blogs+RIGHT+OUTER+JOIN+users+ON+users.id+%3C%3E+null%23'
])->get();

Two things need to be in place for this attack to be possible:

  • An accessible API endpoint, which allows an attacker to pass his malicious code to select or addSelect. Chances are you're not doing this manually in your project. Though there are popular packages which provide this functionality for easy API endpoints and URL filtering.
  • The entry point table must have a column with JSON data. Otherwise the json_extract function will fail, stopping our query. From the entry point though, you can access all data.

# Prevention?

As mentioned before, this particular vulnerability has been fixed as of Laravel 5.8.11. It's always good to keep up to date with the latest Laravel version.

More importantly though, developers should never allow user input directly to specify columns, without a whitelist. In our previous example, you could prevent this attack by only allowing certain fields to be requested, this would prevent the issue completely.

Next, one of our widely-used packages, spatie/laravel-querybuilder, opened up addSelect by design. This meant that websites using our package, were vulnerable to the underlying issue. We immediately fixed it and Freek wrote about it in depth. If you're using our package and unable to update to the latest Laravel version, you should immediately update the package.

Finally, the Laravel docs have also been updated to warn developers not to pass user input directly to columns when using the query builder.

]]>
2019-04-10T00:00:00+00:00
<![CDATA[ Array destructuring in PHP ]]> https://www.stitcher.io/blog/array-destructuring-with-list-in-php # List or []

In PHP, list or [] is a so called "language construct", just like array(). This language construct is used to "pull" variables out of an array. In other words: it will "destructure" the array into separate variables.

Note that the word is "destructure", not "destruction" — that's something different 😉

Here's what that looks like:

$array = [1, 2, 3]; 

// Using the list syntax:
list($a, $b, $c) = $array;

// Or the shorthand syntax:
[$a, $b, $c] = $array;

// $a = 1
// $b = 2
// $c = 3

Whether you prefer list or its shorthand [] is up to you. People might argue that [] is ambiguous with the shorthand array syntax, and therefor prefer list. I'll be using the shorthand version in code samples though.

So what more can list do?

# Skip elements

Say you only need the third element of an array, the first two can be skipped by simply not providing a variable.

[, , $c] = $array;

// $c = 3

Also note that list will always start at index 0. Take for example the following array:

$array = [
    1 => 'a',
    2 => 'b',
    3 => 'c',
];

The first variable pulled out with list would be null, because there's no element with index 0. This might seem like a shortcoming, but luckily there are more possibilities.

# Non-numerical keys

PHP 7.1 allows list to be used with arrays that have non-numerical keys. This opens a world of possibilities.

$array = [
    'a' => 1,
    'b' => 2,
    'c' => 3,
];
['c' => $c, 'a' => $a] = $array;

As you can see, you can change the order however you want, and also skip elements entirely.

# In practice

One of the uses of list are functions like parse_url and pathinfo. Because these functions return an array with named parameters, we can use list to pull out the information we'd like:

[
    'basename' => $file,
    'dirname' => $directory,
] = pathinfo('/users/test/file.png');

As you can see, the variables don't need the same name as the key. Also note that destructuring an array with an unknown key will trigger a notice:

[
    'path' => $path, 
    'query' => $query,
] = parse_url('https://stitcher.io/blog');

// PHP Notice:  Undefined index: query

In this case, $query would be null.

One last detail: trailing commas are allowed with named destructs, just like you're used to with arrays.

# In loops

You can also use the list construct in loops:

$array = [
    [
        'name' => 'a',
        'id' => 1
    ],
    [
        'name' => 'b',
        'id' => 2
    ],
];

foreach ($array as ['id' => $id, 'name' => $name]) {
    // …
}

This could be useful when parsing for example a JSON or CSV file. Be careful though that undefined keys will still trigger a notice.

In summary, there are some pretty good cases in which list can be of help!

]]>
2019-04-01T00:00:00+00:00
<![CDATA[ Upgrade to PHP 7.3 with Homebrew on Mac ]]> https://www.stitcher.io/blog/php-73-upgrade-mac

# Upgrading with Homebrew

Start by making sure brew is up-to-date:

brew update

Next, upgrade PHP:

brew upgrade php

Check the current version by running php -v:

php -v

# PHP 7.3.3 (cli) (built: Mar  8 2019 16:42:07) ( NTS )
# Copyright (c) 1997-2018 The PHP Group
# Zend Engine v3.3.3, Copyright (c) 1998-2018 Zend Technologies
#     with Zend OPcache v7.3.3, Copyright (c) 1999-2018, by Zend Technologies

Restart Nginx or Apache:

sudo nginx -s reload
sudo apachectl restart

And make sure that your local web server also uses PHP 7.3 by visiting this script:

# index.php, accessible to your web server

phpinfo(); die();

The version should show 7.3.x.

Note: if you're using Laravel Valet, please keep on reading, you need some extra steps in order for the web server to properly work.

# JIT compilation failed error

You might notice this error showing up when running PHP scripts, for example: composer global update.

PHP Warning: preg_match(): JIT compilation failed

This is due to a PHP 7.3 bug, and can easily be solved by making a change in your PHP ini file.

If you don't know which ini file is used, you can run the following:

php --ini

# Configuration File (php.ini) Path: /usr/local/etc/php/7.3
# Loaded Configuration File:         /usr/local/etc/php/7.3/php.ini
# Scan for additional .ini files in: /usr/local/etc/php/7.3/conf.d
# Additional .ini files parsed:      /usr/local/etc/php/7.3/conf.d/ext-opcache.ini,
# /usr/local/etc/php/7.3/conf.d/php-memory-limits.ini

Solving the above error can be done by manually disabling the pcre.jit option in our ini file.

# /usr/local/etc/php/7.3/php.ini

- ;pcre.jit=1
+ pcre.jit=0

# Extensions

You may have heard of Homebrew dropping support for PHP extensions, this should now be done with PECL. I personally use Imagick, Redis and Xdebug.

They can be installed like so:

pecl install imagick
pecl install redis
pecl install xdebug

You can run pecl list to see which extensions are installed:

pecl list

# Installed packages, channel pecl.php.net:
# =========================================
# Package Version State
# imagick 3.4.3   stable
# redis   4.3.0   stable
# xdebug  2.7.0   stable

You can search for other extensions using pecl search:

pecl search pdf

# Retrieving data...0%
# ..
# Matched packages, channel pecl.php.net:
# =======================================
# Package Stable/(Latest) Local
# pdflib  4.1.2 (stable)        Creating PDF on the fly with the PDFlib library

Make sure to restart your web server after installing new packages:

sudo nginx -s reload
sudo apachectl restart

# Valet

If you're using Laravel Valet, you should do the following steps to upgrade it:

composer global update

Now run valet install:

valet install

Note that if you're upgrading Valet from 2.0 to 2.1, your Valet config folder will automatically be moved from ~/.valet to ~/.config/valet.

If you have any paths pointing to this folder, you'll have to update them. I, for example, have a custom Nginx config file for one of my local sites. This config file contained absolute paths to the Valet socket. These had to be manually changed.

If you're running into problems with Nginx, you can check out the errors in the logs:

cat /usr/local/var/log/nginx/error.log

If any changes were made to your Valet config, you should restart it:

valet restart

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Last step

Finally you should test and upgrade your projects for PHP 7.3 compatibility.

]]>
2019-03-21T00:00:00+00:00
<![CDATA[ Queueable actions in Laravel ]]> https://www.stitcher.io/blog/laravel-queueable-actions People who follow my work or Spatie's, might have come across a pattern we use in some of our projects. We call them "actions", and simply put they are classes to encapsulate business logic.

You can read up on how we structure projects by domains and actions here, and find examples of actions in the code of my aggregate project.

Let's give one example using actions: creating a contract. A contract creation not only saves a model in the database, but also generates a PDF of that contract.

Here's how we'd program this action:

class CreateContractAction
{
    public function __construct(
        GeneratePdfAction $generatePdfAction
    ) { /* … */ }
    
    public function execute(
        ContractData $contractData
    ): Contract {
        $contract = Contract::createFromDTO($contractData);
        
        $this->generatePdfAction->execute($contract);
        
        return $contract->refresh();
    }
}

If you know DDD, actions can be thought of as a command and its handler combined. There are projects where this approach doesn't suffice, but there are also cases where they are very helpful.

We use this pattern a lot, because of the three benefits it offers:

  • It's very easy to unit test individual action classes.
  • Actions can be composed out of other actions via the dependency container.
  • Actions allow us to think in well-defined context, they reduce cognitive load and decrease maintenance cost.

One detail: there are some cases where we want actions to be executed asynchronously.

In the case of our example: we want to create the contract immediately, but we don't want our users to wait until the PDF is generated. This should be done asynchronously.

In the past, we used to wrap actions into jobs. It would look something like this:

class GeneratePdfJob implements ShouldQueue
{
    use Dispatchable, 
        InteractsWithQueue, 
        Queueable, 
        SerializesModels;

    public function __construct(
        Contract $contract 
    ) { /* … */ }
    
    public function handle(
        GeneratePdfAction $generatePdfAction
    ) {
        $generatePdfAction
            ->execute($this->contract);
    }
}

Instead of directly calling the action within another action, we dispatch a new job.

class CreateContractAction
{
    public function execute(
        ContractData $contractData
    ): Contract {
        // …
        
        dispatch(new GeneratePdfJob($contract));
        
        // …
    }
}

This works fine, but manually wrapping an action in a job started to be kind of tedious in our larger projects.

That's why we started looking into ways of automating this. And sure thing: we can!

Here's what the GeneratePdfAction would look like, using our package:

use Spatie\QueueableAction\QueueableAction;

class GeneratePdfAction
{
    use QueueableAction;

    public function __construct(
        Renderer $renderer,
        Browsershot $browsershot
    ) { /* … */ }
    
    public function execute(Pdfable $pdfable): void
    {
        $html = $this->renderer->render($pdfable);
        
        $this->browsershot
            ->html($html)
            ->save($pdfable->getPath());
    }
}

By using QueueableAction, this action can now be executed asynchronously. Here's how it's used:

class CreateContractAction
{
    // …
    
    public function execute(
        ContractData $contractData
    ): Contract {
        // …
        
        $this->generatePdfAction
            ->onQueue()
            ->execute($contract);
            
        // …
    }
}

It's important to note that the above will still have auto completion of the execute method, as well as DI support; just like normal actions:

# What's the difference with Jobs?!?

Actions allow for constructor injection, which means you can use actions within actions within actions, and so forth.

Jobs on the other hand get container injection in their handle method. This means you cannot compose jobs of of other jobs via the dependency container.

It's obvious why Laravel cannot provide constructor injection in jobs: job-specific data, like our contract, needs to be serialised on order for jobs to be queueable, and the constructor is required to ensure the job has valid data.

By introducing the concept of actions, we're able to separate responsibilities between classes better: jobs are used for data serialisation and executing tasks asynchronously; but they are not concerned with business logic anymore.

If you're concerned with difficult to debug actions when they're queued, you can put your mind at ease. ActionJob classes that are dispatched to for example, Horizon, have their name changed to the action class they wrap:

The underlying details of making actions queueable are hidden to the developer using them, making it very easy to work with them, even in an asynchronous context.

Like I said: we made this into a small package. It consists of a simple trait and an ActionJob. If you want to give it a try, you can check it out here: spatie/laravel-queueable-action.

]]>
2019-03-11T00:00:00+00:00
<![CDATA[ Craftsmen know their tools ]]> https://www.stitcher.io/blog/craftsmen-know-their-tools When programmers call themselves craftsmen or artisans, I can agree that we are. At the same time though, some of these programmers underestimate what craftsmanship actually means.

Let's make a comparison.

My younger brother is an apprentice wood worker, here's a picture of his shop:

To me this looks like chaos, but my brother manages to find his way around. Even better: he actually makes pretty and useful things.

Granted, he is still an apprentice, but I've been happily surprised by the things he has crafted up until now. I can imagine his skills will improve only more and more over time.

For example, take a look at this display thingy he made:

Or this clock:

He also made me some nice sound absorbing panels for my podcast studio at home:

The same way my brother is crafting things with wood, we are building websites and programs with code. I wonder if we have mastered our toolset like he has.

See, he isn't building all this stuff with his bare hands. He's using the right tools for the job. Have a look at some of the machines he's working with:

Or this one:

I've got no clue what these monstrosities do or how to use them — but my brother does.

He had to learn to work these tools. Not only did he need to know which button does what; he had learn how to apply specific techniques to these machines in order to cut and saw the wood as he wants.

He has a mentor and spends a considerate amount of time learning and improving his tools and techniques. It takes patience: he couldn't just dive in at the beginning, there is a learning curve to creating quality.

What is a programmer's equivalent? As we call ourselves craftsmen and artisans, can I assume we also know our tools inside and out?

Can I assume we dedicate time to properly learn how to work with those tools, how to improve our skillset on a day-by-day basis?

Does it seem like a detail to us? Do we think a simple saw gets us equally far as the big monster machine my brother is using?

Can I assume we know the value of our tools? Do we realise they help us be efficient; they simplify mundane tasks so that we can focus on core problems; they are a safeguard when we tend to make stupid mistakes?

Are those fair assumptions when we call ourselves craftsmen?

All these questions pop into my head when I hear people complain about how learning to use an IDE, setting up your local environment, properly learning version control or learning a framework; takes too much time.

What did you expect? In order to write good code, maintainable code, code you can be proud of; in order to do that, you need to master the right tools.

There are no shortcuts to this, you need to spend time learning.

I want to challenge you to think about how you use your tools. There might be opportunities, areas you can improve on. Be willing to spend time improving your skillset.

It's time well invested.

PS: if you're into wood working, be sure to check out my brother's Instagram!

]]>
2019-02-21T00:00:00+00:00
<![CDATA[ PHP 8: The JIT ]]> https://www.stitcher.io/blog/php-jit

Heads up! I've done some benchmarks on the JIT in real-life web applications. Be sure to check them out.

Dmitry Stogov recently opened an RFC to add a JIT compiler to PHP. So, what is that about? Does "JIT" mean "instantly better PHP", or is this a more nuanced topic? Today we'll briefly look at what the "JIT" actually does, and more importantly: the difficulties and opportunities it brings to the PHP world.

# What is JIT?

"JIT" stands for "just in time". You probably know that PHP is an interpreted language: it's not compiled like a C, Java or Rust program. Instead it is translated to machine code — stuff the CPU understands — at runtime.

"JIT" is a technique that will compile parts of the code at runtime, so that the compiled version can be used instead.

Think of it like a "cached version" of the interpreted code, generated at runtime.

How does that work, you ask?

There's a so called "monitor" that will look at the code as it's running. When this monitor detects parts of your code that are re-executed, it will mark those parts as "warm" or "hot", depending on the frequency. These hot parts can be compiled as optimised machine code, and used on the fly instead of the real code.

You can imagine there's a lot more complexity to this topic. If you want to know a little more, you can check out Mozilla's crash course in JIT compilers. For the purpose of this blog post, it's enough to understand that a JIT compiler may improve the performance of your program significantly, but it's a difficult thing to get right.

Zeev, one of the PHP core developers, showed a demo with fractal generation a while back:

Fancy, right? Hold on though…

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# It's not all fun and games

Let's address the elephpant in the room: PHP is seldom used to generate fractal animations.

Knowing that the JIT compiler tries to identify hot parts of your code, you can guess why it has such an impact on the fractal example: there's a lot of the same calculations happening over and over again. However, since PHP is most often used in a web context, we should also measure the JIT's impact there.

It turns out that, unfortunately, there's a lot less hot code while handling a web request. This doesn't mean the JIT couldn't improve web performance at all, but we also won't see similar improvements like with the fractal example.

Is this a reason to ditch the JIT? Definitely not! There are good arguments to add it, even though it might not have the performance impact we'd hope for.

  • It opens the door for PHP to be used as a very performant language outside of the web.
  • The JIT can be improved upon over time, as well could our code.

These are valid arguments in favour of the JIT. Unfortunately though, there are also more arguments against it.

# A complexity to maintain

Because the JIT generates machine code, you can imagine it's complex material for a "higher level programmer" to understand.

For example: having machine code as output, it will be harder to debug possible bugs in PHP's JIT compiler. Luckily there are tools to help debugging. But still, it is machine code. Say there is a bug in the JIT compiler, you need a developer who knows how to fix it. Dmitry is the one who did most of the coding up until now, and remember that PHP core development is done on a voluntary basis.

With just a few people being able to maintain such a code base today, the question whether the JIT compiler can be maintained properly seems justified. Of course people can learn how the compiler works. But it is complex material nevertheless. The pull request right now counts around 50k lines of added code. And mind you: this is not your average client-web-application-codebase. This is almost-as-close-as-you-can-get-to-the-CPU-programming.

Again this should not be a reason to ditch the JIT, but the cost of maintenance should be carefully considered. In first place by the ones who have to maintain the code; but also by the userland community, who should also be aware that some bugfixes or version updates might take longer than what we're used to right now.

Cross platform?

As of newer versions of the JIT, it now also works Windows and Mac! A big step forward, and definitely worth mentioning.

# So why would you want it?

If right now you're thinking that the JIT offers little short-term benefits for your web applications, you might be right. It's difficult to tell what impact it will have on production applications, before actually using it.

The JIT RFC proposed to enable it in PHP 8, but also to add an experimental version in PHP 7.4. Unfortunately the RFC has passed for PHP 8, but not for 7.4. This means we'll have to wait until PHP 8 before being able to try it out on real projects. You can of course compile PHP 8 from source, if you already want to take a look.

Even though the JIT might not offer any significant short-term improvements, we should remember that it will open a lot of possibilities for PHP to grow, both as a web language and a more generally purposed language. So the question that needs answering: is this possibly bright future worth the investment today?

What do you, the userland programmer think? Let's discuss it on Hacker News. If you want to personally reach out, you can find me on Twitter or via e-mail!

]]>
2019-02-06T00:00:00+00:00
<![CDATA[ I'm building something ]]> https://www.stitcher.io/blog/announcing-aggregate Being a blogger myself, I realise how much quality content is written on the internet every day; content that gets lost in an endless stream of online information.

I know how much you want to be able to share your content with an audience, but it's so difficult to do. Twitter is very momentarily; Reddit suffers from spam and angry people; and Facebook is, well, Facebook.

I want a platform where I don't have to feel guilty about sharing my content, and where readers can easily discover new things to read.

So I took one of the oldest blogging concepts and made a community driven platform out of it: RSS.

# Community driven?

I realise that starting a project on my own, won't get me far. We live in a day and age where every possible app has been invented at least twice. Trying to do this myself won't get me far.

That's why I decided on two things.

The code is open source. I want to encourage people to contribute, help build the platform they want it to be.

The content is provided by the community, and the platform will always redirect to the source of origin. It will never try to host your content, it's merely a portal.

# How does it work?

Content creators can add their RSS feed to the platform. Their posts are synced and tagged according to the content and RSS meta data.

Readers can explore new content daily in their feed. I've decided to not try and do anything fancy while building this feed. Platforms like Facebook and Twitter try to be smart and build a feed based on "your interests". They never succeed.

So the feed is a simple chronological list, which you can filter on tags. That's it. There are no votes, no comments. You're encouraged to go the blogs themselves, and share your thoughts over there.

The goal of the platform is simple: help readers discover new content, and get them to that original content as fast as possible. Meanwhile, content creators get a platform where they are allowed to share their own stuff, and an audience hungry for more.

# Get in touch

Even though the basic concept is stable and up and running, there's still lots of things to do. There's improvements to be made to the tagging system, there are some convenience features that need to be added and more.

If you're a web programmer yourself who's interested in open source, feel free to take a look around the project board.

Oh and, of course, here's a link to the platform: aggregate.stitcher.io.

]]>
2019-01-29T00:00:00+00:00
<![CDATA[ Analytics for developers ]]> https://www.stitcher.io/blog/analytics-for-developers I've been running this blog for almost three years now. I've used Google Analytics, not only to track the amount of users, but also to actively improve my blog.

I'm no marketeer, just a simple developer. Today I want to share from my technical experience, how I use traffic data and react to it.

# Filtering data

First and foremost, the boring part. It takes less than five minutes to set up, but will improve the correctness of your data: filters.

As you know, Google Analytics works with a tracking code which you can place everywhere. That tracking code can easily be used somewhere else, polluting your data. I'm thinking of local development environments or, more malicious: people who steal your tracking code as a deliberate attack.

Luckily this can easily be prevented with filters. So take five minutes to set up some filters in the admin section:

With filters, you can build a whitelist of traffic you want to include. I usually make a separate view called "Filtered", and keep the "All Web Site Data" view as is, for testing.

Here's an example of a filter I set up, to only include traffic from the "stitcher.io" domain:

You can also filter out local domains, whitelist IP addresses and more. If you want to be really safe, you could whitelist the IP address of your host, in combination with the host name.

With the correct filters set up, it's time to interpret data.

# Decreasing bounce rate

Here's a good question to start with: "should I want to decrease my bounce rate"? The corporate marketeer will of course scream "YES".

I say: it depends. Take for example this blog, or almost any other blog. When I share a post on social media, most visitors come to read that post, and that post only.

It's not that weird when you think about it: don't we all do the same when browsing?

A high bounce rate means that not a lot of visitors click through after their first page visit. In case of a blog, there's nothing wrong with that: many of these people will return the next time you share a link on social media.

Let's take a look at the traffic in November of 2018, on this blog.

That's a rather high bounce rate. Though over the last year, I see a consistent 20-ish percent of returning visitors. These people also add to the bounce rate, though they are the ones who visit this site over and over again, albeit only one page at a time.

You can see how relative this data is, and how you cannot simply say "decrease the bounce rate".

But what can we learn from it though?

If you're a regular reader of my blog, first of all: thanks, you're in the 20% ! Secondly: you know that I place an "up next" link down at the bottom of each post.

In the past, these links were automatically generated and just showed the post before the current one.

When analysing the bounce rate of individual pages though, I noticed that some pages had a way lower bounce rate than others.

Looking at these low bounce pages, they were the pages where I deliberately put another link at the bottom, one to a post of which I thought was a little related to the current one.

So how do you analyse this?

The "Content Drilldown" page is great to analyse per-page statistics. You can find it under Behavior > Site Content.

Also note how the "advanced filter" is applied to only show pages with lower bounce rates. We can use this data to learn what we're doing right, and target pages that might need optimisation.

# Top referrals

I know most of my traffic comes from links that are shared. I've got some posts that show up high in Google, but more about that later.

The "Referrals" page under Acquisition > All Traffic is an important one to know where your traffic is coming from.

You can see peaks in traffic during the day, by using the real time overview. I often check the referrals at these moments, to know where traffic is coming from.

I believe that, as an author, it's important to engage with your readers. When content is shared on social media, reactions often show up there; so it's only natural that you reply to them there.

A quick Google search on the blog title and website of the referral, often gets me to the right place in no time.

# Average session duration

Now that we know people are visiting our blog, we also want to know whether they are actually reading the content.

Again, the "Content Drilldown" page gets us this data. By adding a simple regex filter ^\/[a-z\-]+$, we filter out pages with query parameters. We're also not interested in pages with very low pageviews.

What we're actually interested in, are the pages with the lowest session duration, to see whether some things can be improved.

Some of these blog posts are very short ones, so nothing strange there. Though it also shows what posts were less interesting to my audience. It's a good metric to know what kind of content I shouldn't focus on.

# Analysing behaviour flow

The "Behavior Flow" chart under the Behavior menu is one that helps visualising how visitors browse your site.

This is what it looks like.

As with the bounce rate optimisations, this overview can help identifying pages that encourage people to click through, and pages that don't.

I use this overview in combination with the "Content Drilldown" page, to analyse where people come from, where they go, and whether I can improve my content to help them read what they are actually interested in.

I know that most of my traffic comes from links that are shared over the internet, though I also want to know how much comes through search sites like Google. These pages get a more constant amount of monthly visitors, and might pose a good opportunity to introduce readers to my blog.

By going to Acquisition > All Traffic > Channels, you can click through to the "Organic Search" channel.

By default, this overview shows what keywords were searched on, which isn't very useful to us right now. You'll also notice that most of the time, this keyword data is simply missing.

You can however specify a "Secondary Dimension", on the "Landing Page". Now the overview will be grouped per page the visitor landed on, which is exactly what we want to know!

Now we know what pages are good candidates to optimise, but we still don't know what keywords people actually searched for.

If you link your Search Console to Analytics, you'll get the data you need. On Acquisition > Search Console > Queries, you'll see an overview of these keywords.

Here's an example, for this past week in January, 2019:

# Measuring site speed

Lastly, something I'm very proud of: my blog's performance. When building this blog I really wanted it to be fast, everywhere.

Analytics also helps with that. Under Behavior > Site Speed > Page Timings, you can monitor the performance of individual pages. Take, for example, the most visited pages of November, 2018.

The red lines are the interesting ones: these pages load slower than the site's average. This can be because of many reasons: lots of visitors with a bad connection to the host, images that should be better optimised, maybe a problem with a script I wrote for a post?

This view allows me to find performance problems early on, and fix them.

# In closing

This was of course nothing close to all the features Analytics offers. But I hope that I did show you the mindset I have when analysing data. These are real people visiting my site, and I want to do good for my audience.

Google Analytics is a great tool to help you with that, but in the end, it all starts with good content.

]]>
2019-01-22T00:00:00+00:00
<![CDATA[ Share a blog: sebastiandedeyne ]]> https://www.stitcher.io/blog/share-a-blog-sebastiandedeyne-com Sebastian's blog has seen many forms the past few years. From his own personal blog to an aggregator newsletter; he now settled on a combination of both.

He writes and shares content about web development, design and programming in general. In short: a good blog for every programmer to follow!

Check it out!

]]>
2019-01-15T00:00:00+00:00
<![CDATA[ Comparing dates ]]> https://www.stitcher.io/blog/comparing-dates Here's a simple question:

Does the date range "2019-01-01 – 2019-01-31" contain the date "2019-01-31"?

The answer is yes, right?

… Right?

What if the range ends at 10 o'clock, while our test date starts at 11 o'clock? Now they don't overlap.

How can we reliably compare dates, if there's always a smaller unit of time we might not know about? There's two solutions.

# Excluding boundaries

Here a little mathematics refresher, ranges can be written like so:

[start, end]

Obviously, this notation can be applied to date periods:

[2019-01-01, 2019-01-31]

The square brackets indicate that the boundary is included in the range, round brackets mean a boundary is excluded:

(0, 3)

This notation tells us this range contains all numbers between 0 and 3, namely 1 and 2.

Using exclusive boundaries, we can compare dates with 100% certainty of correctness.

Instead of testing whether [2019-01-01, 2019-01-31] contains the date 2019-01-31, why don't we test whether [2019-01-01, 2019-02-01) contains it?

An excluded end boundary allows us to say that "all dates before 2019-02-01" are contained within this range. The times of our date and period don't matter anymore, we're always sure that a date before 2019-02-01 will fall within our range.

# Ensuring the same precision

While the above solution mathematically works, it gets awkward in a real world context. Say we want to note "the whole month of January, 2019" as a range. It looks like this:

[2019-01-01, 2019-02-01)

This is a little counter intuitive, at least it's not the way we humans think about "January". We'd never say "from January 1, until February 1, with February 1 excluded".

As it goes in programming, we often sacrifice the "common way of human thinking" to ensure correctness.

But there is a way to ensure program correctness, with the notation that makes sense to humans:

[2019-01-01, 2019-01-31]

Our problem originated because we weren't sure about the time of the dates we're working with. My suggestion is to not work around the problem by excluding boundaries, but to eliminate it for good.

Let's fix the root of the problem instead of working our way around it. Shouldn't that always be the mindset of every programmer?

Let me say that again, because it's oh so important:

Let's fix the root of the problem instead of working our way around it.

The solution? When you're comparing days, make sure you're only comparing days; not hours, minutes or seconds.

When programming, this means you'll have to store the precision of a date range within that range. It also means you'll have to disallow comparing dates who have different precisions.

What's your opinion? Let me know via Twitter or e-mail.

]]>
2018-12-21T00:00:00+00:00
<![CDATA[ Have you thought about casing? ]]> https://www.stitcher.io/blog/have-you-thought-about-casing

I've made the argument many times before: programmers shouldn't underestimate the value of code readability. Today I'll argue once more against a common practice we all seem to take for granted: casing.

It seems like such a minor detail, right? Using camels or snakes or kebabs to string words together, who cares? There's more to this question though.

Code readability shouldn't be neglected. It impacts how easy you can navigate and understand your code. We should talk about the casing conventions we use.

# A little history

Early programming languages didn't have the same conventions we have today. Languages like Lisp and COBOL originated before widespread ASCII support, explaining the difference. Upper- and lower case, and special characters like underscores simply weren't supported by compilers back in the 50s and early 60s.

Both Lisp and COBOL allowed for hyphens to split words. Lisp's parser was smart enough to detect whether a hyphen was between two words, or whether it should be used as the subtraction operator. COBOL only has full words as operators, eliminating the problem altogether. Here's a subtraction in COBOL:

SUBTRACT data-item-1 FROM data-item-2

Because the hyphen isn't a reserved keyword, it can be used to split words.

When programming languages matured in the 80s and 90s, it became clear that the hyphen should be reserved for mathematical operations. Another issue with Lisp's smart approach was that it didn't scale in modern languages, it slowed down tokenisation significantly.

Spaces obviously could never be used, as almost every programming language uses them as the boundary between tokens. So what's left? How could we write multiple words as one, while keeping these words readable?

# Conventions today

This is why we're left with two major conventions today: camel case, either lower- or upper; and snake case. As a sidenote: upper camel case is also called pascal case.

Most of the time, a language tends to favourite one of the two casings. We could say it's a matter of community guidelines, and be done with it.

It's my opinion that there's more to it. There is one better, more readable way of writing words. You can probably guess which one, based on the start of this blog post. camel case makes text more difficult to read, compared to snake case.

Given the word user id, compare the two ways of writing it:

userId
user_id

It's true: camel case is more compact: you don't have to write as much. But which style is the closest to how the human brain actually reads a text?

This is the only argument that matters to me in this discussion:

How can we make it as easy as possible for our brain to read and understand code?

Readable code, reduces cognitive load. Less cognitive load means more memory space for humans to think about other things, things like writing business logic.

"All of that just by using underscores?" No, not just because of underscores. There's much more to writing readable code than naming conventions. But all small things help in getting a bigger solution.

]]>
2018-12-20T00:00:00+00:00
<![CDATA[ Share a blog: betterwebtype ]]> https://www.stitcher.io/blog/share-a-blog-betterwebtype-com My colleague Willem always amazes me: the backend developer; with his insights into design. It's he who recommended following a blog — it's more like a newsletter — about web typography.

This course broadened the way I think about fonts, and it's the primary reason my own blog, this blog; looks the way it does. Whether you're a frontend or backend dev, everyone should have basic knowledge about typography, and I'd recommend following the course over at betterwebtype.com to do so.

]]>
2018-12-15T00:00:00+00:00
<![CDATA[ New in PHP 7.3 ]]> https://www.stitcher.io/blog/new-in-php-73 # New in PHP 7.3

# is_countable rfc

PHP 7.2 added a warning when counting uncountable objects. The is_countable function can help prevent this warning.

$count = is_countable($variable) ? count($variable) : null;

# array_key_first and array_key_last rfc

These two functions basically do what the name says.

$array = [
    'a' => '…',
    'b' => '…',
    'c' => '…',
];

array_key_first($array); // 'a'
array_key_last($array); // 'c'

The original RFC also proposed array_value_first and array_value_last, but these were voted against by the majority of people.

Another idea for array_first and array_last was proposed which would return a tuple [$key => $value], but opinions were mixed. For now we only have two functions to get the first and last key of an array.

# Flexible Heredoc syntax rfc

Heredoc can be a useful tool for larger strings, though they had an indentation quirk in the past.

// Instead of this:

$query = <<<SQL
SELECT * 
FROM `table`
WHERE `column` = true;
SQL;

// You can do this:

$query = <<<SQL
    SELECT * 
    FROM `table`
    WHERE `column` = true;
    SQL;

This is especially useful when you're using Heredoc in an already nested context.

The whitespaces in front of the closing marker will be ignored on all lines.

An important note: because of this change, some existing Heredocs might break, when they are using the same closing marker in their body.

$str = <<<FOO
abcdefg
    FOO
FOO;

// Parse error: Invalid body indentation level in PHP 7.3

# Trailing commas in function calls rfc

What was already possible with arrays, can now also be done with function calls. Note that it's not possible in function definitions!

$compacted = compact(
    'posts',
    'units',
);

# Better type error reporting

TypeErrors for integers and booleans used to print out their full name, it has been changed to int and bool, to match the type hints in the code.

Argument 1 passed to foo() must be of the type int/bool

In comparison to PHP 7.2:

Argument 1 passed to foo() must be of the type 
integer/boolean

# JSON errors can be thrown rfc

Previously, JSON parse errors were a hassle to debug. The JSON functions now accept an extra option to make them throw an exception on parsing errors. This change obviously adds a new exception: JsonException.

json_encode($data, JSON_THROW_ON_ERROR);

json_decode("invalid json", null, 512, JSON_THROW_ON_ERROR);

// Throws JsonException

While this feature is only available with the newly added option, there's a chance it'll be the default behaviour in a future version.

# list reference assignment rfc

The list() and its shorthand [] syntax now support references.

$array = [1, 2];

list($a, &$b) = $array;

$b = 3;

// $array = [1, 3];

# Undefined variables in compact rfc

Undefined variables passed to compact will be reported with a notice, they were previously ignored.

$a = 'foo';

compact('a', 'b'); 

// Notice: compact(): Undefined variable: b

# Case-insensitive constants rfc

There were a few edge cases were case-insensitive constants were allowed. These have been deprecated.

This change not only adds a new parameter, it also changes the way the setcookie, setrawcookie and session_set_cookie_params functions work in a non-breaking manner.

Instead of one more parameters added to already huge functions, they now support an array of options, whilst still being backwards compatible. An example:

bool setcookie(
    string $name 
    [, string $value = "" 
    [, int $expire = 0 
    [, string $path = "" 
    [, string $domain = "" 
    [, bool $secure = false 
    [, bool $httponly = false ]]]]]] 
)

bool setcookie ( 
    string $name 
    [, string $value = "" 
    [, int $expire = 0 
    [, array $options ]]] 
)

// Both ways work.

# PCRE2 migration rfc

PCRE — short for "Perl Compatible Regular Expressions" — has been updated to v2.

The migration had a focus on maximum backwards compatibility, though there are a few breaking changes. Be sure to read the RFC to know about them.

# String search functions README

You can no longer pass a non-string needle to string search functions. These are the affected functions:

strpos()
strrpos()
stripos()
strripos()
strstr()
strchr()
strrchr()
stristr()

# MBString updates README

MBString is PHP's way of handling complex strings. This module has received some updates in this version of PHP. You can read about it here.

# Several deprecations rfc

Several small things have been deprecated, there's a possibility errors can show up in your code because of this.

  • Undocumented mbstring function aliases
  • String search functions with integer needle
  • fgetss() function and string.strip_tags filter
  • Defining a free-standing assert() function
  • FILTER_FLAG_SCHEME_REQUIRED and FILTER_FLAG_HOST_REQUIRED flags
  • pdo_odbc.db2_instance_name php.ini directive

Please refer to the RFC for a full explanation of each deprecation.

]]>
2018-12-06T00:00:00+00:00
<![CDATA[ Share a blog: codingwriter.com ]]> https://www.stitcher.io/blog/share-a-blog-codingwriter-com When asking on Twitter for writing advice, I was suggested to check out Sarah Mischinger's blog: codingwriter.com. This is a great blog to follow for the technical bloggers out there, myself included.

To me, blogging is more than just shouting my opinion in some corner of the web; I hope people may learn, the same way I do, by reading other people's blogs. Writing good content is essential, and I was happy to discover a blog focused on this exact topic.

]]>
2018-11-14T00:00:00+00:00
<![CDATA[ Structuring unstructured data ]]> https://www.stitcher.io/blog/structuring-unstructured-data # Have you ever…

…worked with an array in PHP that was actually more than just an array? Did you use the array keys as fields? And did you feel the pain of not knowing exactly what was in that array? Not being sure whether the data in it is actually what you expect it to be, or what fields are available?

Let's visualise what I'm talking about:

$line = // Get a line from a CSV file

import($line['id'], $line['name'], $line['amount']);

Another example: what about validated request data?

function store(PostRequest $request, Post $post) 
{
    $data = $request->validated();
    
    $post->title = $data['title'];
    $post->author_id = $data['author_id'];
    
    // …
}

Arrays in PHP are a powerful and versatile data structure. At some point though, one should wonder whether there are better solutions for their problems.

# Know what you're writing

Regular readers of this blog may know that I've written about type theory in the past. I won't revisit the pros and cons on strong type systems; but I do want to say that arrays are a terrible choice if they are meant to be used as anything else but lists.

Here's a simple question for you: what's in this array?

function doSomething(array $blogPost)
{
    $blogPost[/* Now what?? */];
}

In this case, there are several ways of knowing what data we're dealing with:

  • Read the source code.
  • Read the documentation.
  • Dump $blogPost to inspect it.
  • Or use a debugger to inspect it.

I simply wanted to use this data, but next thing I know, I'm deep into debugging what kind of data I'm actually dealing with. Are these really the things a programmer should be focused on?

Eliminating this uncertainty can reduce your cognitive load significantly. This means you can focus on things that really matter: things like application and business logic. You know, that's what most clients pay you to do.

It turns out that strongly typed systems can be a great help in understanding what exactly we're dealing with. Languages like Rust, for example, solve this problem cleanly:

struct BlogPost {
    title: String,
    body: String,
    active: bool,
}

A struct is what we need! Unfortunately PHP doesn't have structs. It has arrays and objects, and that's it.

However, we can do something like this:

class BlogPost
{
    public string $title;
    public string $body;
    public bool $active;
}

Hang on, I know; we can't really do this, not yet. PHP 7.4 will add typed properties, but they are still a long way away.

Imagine for a minute though that typed properties are already supported; we could use the previous example like so, which our IDE could auto complete:

function doSomething(BlogPost $blogPost)
{
    $blogPost->title;
    $blogPost->body;
    $blogPost->active;
}

We could even support relations:

class BlogPost
{
    public Author $author;
    
    // …
}
function doSomething(BlogPost $blogPost)
{
    $blogPost->author->name;
}

Our IDE would always be able to tell us what data we're dealing with. But of course, typed properties don't exist in PHP yet. What does exist… are docblocks.

class BlogPost
{
    /** @var string */
    public $title;
    
    /** @var string */
    public $body;
    
    /** @var bool */
    public $active;
    
    /** @var \Author */
    public $author;
}

Docblocks are kind of a mess though: they are quite verbose and ugly; but more importantly, they don't give any guarantees that the data is of the type they say it is!

Luckily, PHP has its reflection API. With it, a lot more is possible, even today. The above example can actually be type validated with a little reflection magic, as long as we don't write to the properties directly.

$blogPost = new BlogPost([
    'title' => 'First',
    'body' => 'Lorem ipsum',
    'active' => false,
    'author' => new Author()
]);

That seems like a lot of overhead, right? Remember the first example though! We're not trying to construct these objects manually, we're reading them from a CSV file, a request or somewhere else:

$blogPost = new BlogPost($line);

That's not bad, right? And remember: a little reflection magic will ensure the values are of the correct type. I'll show you how that works later.

I prefer this approach. It enables auto completion on what would otherwise be a black box. While it requires a little more setup: you'll have to write definitions of data; the benefits in the long run are worth it.

Sidenote: when I say "in the long run", I mean that this approach is especially useful in larger projects, where you're working in the same code base with multiple developers, over a longer timespan.

# Reflecting types

So, how can we assure that our properties are of the correct type? Simple: read the @var docblock declaration, validate the value against that type, and only then set it. If the value is of a wrong type, we simply throw a TypeError.

Doing this extra check means we cannot write to the properties directly. At least not if they are declared public. And in our case public properties are something we really want, because of when we're using these objects. We want to be able to easily read data from them; we don't care as much on making writes easy, because we should never write to them after the object is constructed.

So we need a "hook" to validate a value against its type, before setting it. There are two ways to do this in PHP. Actually there are more, but these two are relevant.

# With a magic setter

A magic setter in combination with private or protected properties would allow us to run type validation before setting the value.

However, as mentioned before, we want a clean and public API to read from; so magic setters are, unfortunately, a no go.

# Via the constructor

Like in the previous example, we pass an array of data to the constructor, and the constructor will map that data onto the properties of its class. This is the way to go.

Here's a simplified way of doing this:

public function __construct(array $parameters)
{
    $publicProperties = $this->getPublicProperties();
   
    foreach ($publicProperties as $property) {
        $value = $parameters[$property->getName()];
        
        if (! $this->isValidType($property, $value) {
            throw new TypeError("…");
        }
        
        $this->{$property->getName()} = $value;
    }
}

Maybe you're curious as to what isValidType exactly does? Here is, again a simplified, implementation:

protected function isValidType(ReflectionProperty $property, $value): bool
{
    $type = $this->getTypeDeclaration($property);
    
    return $value instanceof $type
        || gettype($value) === $type;
    }
}

Of course, there are some things missing here:

  • Union types: @var string|int
  • @var mixed support
  • Generic collections: @var \Foo[]
  • Nullable support: @var int|null

But it is very easy to add these checks to our isValidType method. And that's exactly what we did by the way, we made this into a package: spatie/data-transfer-object.

# What about immutability?

How to handle immutability is the last question to answer. If we use these objects to represent data from the outside, are there any valid use cases for changing these objects once they are constructed?

In 98% of the cases, the answer should be plain and simple: no. We'll never be able to change the data source, hence we shouldn't be able to change the object representing that source.

Real life projects are often not as black and white as I portray it here. While there might be some use cases, I think the mindset of "construct once, and never change" is a good one.

So how to enforce this in PHP?

Unfortunately: we don't. There has been talk of so called "read only" properties in PHP's core, but it's a difficult thing to get right. Then what about our userland type system? Unless we're giving up the ease of reading, the auto completion part; there will be no way to achieve this goal in PHP.

See, we need magic getters to support this behaviour; at the same time we don't want them. They would negate one of the goals we're trying to achieve: easy discoverability.

So for now, unfortunately, our package will allow writes to an object's properties after it is constructed. We are just careful not to do it.

I hope this post inspired you to think about your own code bases, and that you might be prompted to try this pattern out in your projects; with our package or your own implementation.

If there are any thoughts that come to your mind or if you want to discuss this further, I'd love to here from you! You can reach me via Twitter or e-mail.

]]>
2018-11-11T00:00:00+00:00
<![CDATA[ PhpStorm OSX performance: October 2018 ]]> https://www.stitcher.io/blog/phpstorm-performance-october-2018 PhpStorm has had performance issues on OSX for a very long time now, sometimes to the point of being unusable.

I've written about these issues before, but it's good to keep a regularly updated list of what's going on. So without further ado: if you're on OSX (Sierra, High Sierra or Mojave); if you're experiencing PhpStorm performance issues, this post might help you.

# External monitor resolution

Do you have an external monitor plugged into your MacBook? There's an issue in Java Swing, the UI framework that PhpStorm uses under the hood. In short: if you're using a non-default resolution, Java has to do a lot of calculations to deal with half pixels and such.

In case of 4k monitors, we've seen good results with 1080p and 4k resolutions, as they are natively supported. All other resolutions can cause massive performance issues.

Default resolutions work fine.

Scaled resolutions not so much…

# Font antialiasing

In your settings, under Editor > Appearance & Behaviour > Appearance, you'll find the editor font antialiasing options.

By default, antialiasing is set to subpixel, to render very smooth fonts. Again, because of Java graphical issues, there can be a big performance hit.

It's better to set the antialiasing setting to greyscale, or disable it altogether.

Your font choice might also impact performance. I know this might take some time to get used to, but try using another font. I always used Ubuntu Mono, but switched to Monaco, and had noticeable improvements.

# JavaFX enabled plugins

Some plugins make use of JavaFX, that may cause rendering issues. As an easy way to know if you're running such plugins, you can do the following.

Get the PID of the running PhpStorm process:

> top | grep phpstorm

82912  phpstorm         …

Next, run jstack with PhpStorm's process ID, and grep for "quantum":

> jstack 82912 | grep quantum

at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)

If you see any output (as above), it means that plugins are using JavaFX. Using these plugins will increase performance issues over time, especially if you're running PhpStorm as a maximized window.

The only way to know which plugins are using JavaFX is by disabling plugins, one by one; restarting PhpStorm and doing the above jstack test again. One very popular plugin depending on JavaFX is the Markdown plugin.

# JDK versions

The last thing you can do is download a new Java JDK, another version, and use that one to run PhpStorm.

You can configure the JDK PhpStorm is using by opening the command palette and search for Switch Boot JDK….

Boot JDKBoot JDK

It's important to note that IntelliJ products won't run on all JDKs! At the time of writing, Java 10 won't work yet.

If you've configured a JDK that broke PhpStorm, you can still fix it though. There's a file in your preferences folder which contains the JDK you're using:

~/Library/Preferences/IntelliJIdea<VERSION>/idea.jdk

You can change the JDK path there. More information on switching JDKs can be found here.

# In closing:

Software development is hard.

It's understandable why JetBrains chooses Java as a platform for their IDEs. Unfortunately Java Swing, an older UI framework, doesn't play well with modern OSX platforms.

Whose fault is this? Should JetBrains fix it? Will they be able to? There's no clear answer to those questions. There's an active issue here, where you can follow the progress; though I doubt there will be any solutions soon.

For now, we'll have to deal with these performance issues, because — even though they are annoying — PhpStorm is still the best PHP IDE out there, by far.

]]>
2018-10-26T00:00:00+00:00
<![CDATA[ Share a blog: assertchris.io ]]> https://www.stitcher.io/blog/share-a-blog-assertchris-io Christopher Pitt is one of the inspirations why I got into blogging and conference talks. Being an excellent writer — he used to blog on Sitepoint and Medium — he now decided to host his content on his own website. A wise decision!

I'd highly recommend adding his blog to your RSS reader, or bookmark it somewhere: assertchris.io. There's lots of good content to look forward to!

]]>
2018-10-25T00:00:00+00:00
<![CDATA[ array_merge or + in PHP ]]> https://www.stitcher.io/blog/array-merge-vs+ PHP has several ways of combining two arrays into one. You can use array_merge or the + operator. There's a subtle difference between these two methods though, a difference worth knowing.

Let's take a look at how these two methods compare:

array_merge($first, $second);

// vs.

$first + $second;

Let's say these are the two arrays we're working with:

$first = [
    'a',
    'b',
];

$second = [
    'c',
];

This would be the result of a simple array_merge call:

array_merge($first, $second);

[
    'a',
    'b',
    'c',
]

While the + operator gives us this result:

$first + $second;

[
    'a',
    'b',
]

Switching the operands while using the + operator, gives a different result:

$second + $first;

[
    'c',
    'b',
]

Confused? So was I.

Let's write out the $first and $second arrays in full, with their indices. This will make things more clear:

$first = [
    0 => 'a',
    1 => 'b',
];

$second = [
    0 => 'c',
];

By now you can probably guess what's going on: the + operator will only add the elements of the rightside operand, if their key doesn't exist in the leftside operand, while array_merge will override existing keys.

By that definition, we can also determine that + can never be used to recursively merge arrays, as it will leave existing elements untouched:

$first = [
    'A' => [
        'B' => true,
        'C' => true,
    ],
];

$second = [
    'A' => [
        'B' => false,
        'C' => false,
    ],
];

$first + $second;

Here's the result:

[
    'A' => [
        'B' => true,
        'C' => true,
    ],
]

While using array_merge, would give this result:

[
    'A' => [
        'B' => false,
        'C' => false,
    ],
]

"Hang on", I hear you say, "isn't that what array_merge_recursive is supposed to do?".

Here we have a case of unfortunate naming. Please don't be surprised — it's PHP after all.

See, array_merge will merge matching elements by overriding them. array_merge_recursive on the other hand will keep both elements, and merge them in a new array, keeping both values.

This is what our previous example would look like, using array_merge_recursive:

[
    'A' => [
        'B' => [
            true,
            false,
        ],
        'C' => [
            true,
            false,
        ],
    ],
]

What about merging multiple arrays? You can probably guess the outcome by now:

$first = ['a'];
$second = ['b'];
$third = ['c'];

Here's what array_merge results in:

array_merge($first, $second, $third)
[
    'a',
    'b',
    'c',
]

Chaining the + operator also works, with the following result:

$first + $second + $third
[
    'a',
]

With this little refresher, I hope that you won't find yourself confused anymore when you're deep into your code and need to merge arrays.

I found it to be a cognitive burden when I had to stop and think about "hang on, what is the correct way to do this?". Luckily now, we know!

]]>
2018-10-24T00:00:00+00:00
<![CDATA[ Laravel view models vs. view composers ]]> https://www.stitcher.io/blog/laravel-view-models-vs-view-composers Update: I've written a new version of this post, as part of my Laravel beyond CRUD series. You can read it here.

Last month I wrote about view models in Laravel. I received a lot of good reactions on the post, but also the same question over and over again: how do view models differ from view composers in Laravel?

Time to clarify this question once and for all.

# View composers

Let's look at how view composers are used in Laravel. View composers are a way of binding data to a view from global configuration.

The Laravel documentation explains it like this:

View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location.

View composers are registered like this, the example is taken from the Laravel docs.

class ComposerServiceProvider extends ServiceProvider
{
    public function boot()
    {
        View::composer(
            'profile', ProfileComposer::class
        );

        View::composer('dashboard', function ($view) {
            // …
        });
    }
    
    // …
}

As you can see you can both use a class and a closure which you can use to add variables to a view.

Here's how view composers are used in controllers.

class ProfileController
{
    public function index()
    {
        return new view('profile');
    }
}

Can you see them? Nope, of course not: view composers are registered somewhere in the global state, and you don't know which variables are available to the view, without that implicit knowledge.

Now I know that this isn't a problem in small projects. When you're the single developer and only have 20 controllers and maybe 20 view composers, it'll all fit in your head.

But what about a project with three or four developers, with hundreds of controllers? What if you're taking over a legacy project where you don't have this implicit knowledge?

This is why at Spatie, we use view models in our larger projects. They make everything much more explicit, which helps us keep the code maintainable.

Here's what we do:

class ProfileController
{
    public function index(User $user)
    {
        return new view(
            'profile', 
            new ProfileViewModel($user)
        );
    }
}

Now it's clear now from the controller itself what variables are available to the view. We can also re-use the same view for multiple contexts. An example would be the same form view used in the create and edit actions.

One last added benefit, one you might not have thought about, is that we can pass data into the view model explicitly. If you want to use a route argument or bound model to determine data passed to the view, it is done explicitly.

In conclusion: managing global state is a pain in large applications, especially when you're working with multiple developers on the same project. Also remember that just because two means have the same end result, that doesn't mean that they are the same!

I hope this quick writeup answers all the questions about the difference between view models and -composers. If you want to know more about view models in particular, be sure to read the blog post about them here.

]]>
2018-10-16T00:00:00+00:00
<![CDATA[ Organise by domain ]]> https://www.stitcher.io/blog/organise-by-domain In this post we'll look at a different approach of structuring large code bases into separate domains. The name "domain" is derived from the popular DDD paradigm, or also: domain driven design.

Since I wrote this post, I actually started a whole blog series on how to organise larger Laravel projects. I'd recommend to check it out instead.

While many concepts in this post are inspired by DDD principles, they don't strictly follow domain driven design. In our context, "domain" could also be named "module". A "domain" simply refers to a category of related stuff, that's it.

It's also important to note is that this approach isn't a silver bullet. At Spatie we choose a different project structure based on the needs of that specific project. It is possible that your project isn't a good fit for what we'll be reviewing today.

In our experience, today's principles are mostly beneficial in larger projects:

  • Long running projects with an initial development timespan of half a year to one year or more, with several years of maintenance and extensions after that.
  • Around fifty to hundred models representing the business.
  • Several hundred routes exposing functionality to the outside.

# So, what is a "domain" ?

If you've worked on these kinds of large projects before, you know that "the business logic" never is just one thing. Often during development, you'll identify "sub-systems" within the larger domain; that is: the collection of problems you're trying to solve with your code.

To name a few examples: user management, inventory management, invoicing and contracts. I'm sure you can think of many others.

Most likely, every sub-system has one or several models. But it doesn't stop there: models can be interacted with, actions can be performed with them, there can be system-specific validation rules, ways of passing data between systems, and more.

Looking at a standard Laravel application, the code describing a single system is often spread across multiple directories:

app/
├── Enums/
│   ├── ContractDurationType.php
│   └── ContractType.php
├── Exceptions/
│   └── InvalidContractDate.php
├── Models/
│   └── Contract.php
└── Rules/
    ├── ContractAvailabilityRule.php
    └── ContractDurationRule.php

This structure was the first struggle that prompted me to look for a better solution. I often found myself searching through several places in order to work one thing, one system.

So why not group sub-systems together? It looks something like this:

Domain/
├── Contracts/
├── Invoicing/
└── Users/

You can see the name Domain here. According to Oxford Dictionary, a "domain" can be described like so:

A specified sphere of activity or knowledge.

We're grouping code together based on their sphere of activity, their domain. Let's zoom into one specific domain folder:

Contracts/
├── Actions/
├── Enums/
├── Exceptions/
├── Models/
├── Rules/
├── Status/
└── ValueObjects/

Modern PHP developers are most likely familiar with most of these folder names. Though some deserve a little more attention.

# Actions

Actions are probably the most powerful tool within this whole setup. An action is a class that performs an operation within its domain. This might be a simple task like creating or updating a model, or something more complex following one or several business rules like approving a contract.

Because a single action only focuses on one task, they are extremely flexible: an action can be composed out of other actions and they can be injected wherever you want.

Here's an example of two actions working together: CreateOrUpdateContractLine and ResolveContractLines. The first one will do as its name says: create or update a single contract line. The second one will loop over a collection of user input, and resolve the lines one by one.

Here's what ResolveContractLines will do:

  • Loop over the user input and create or update existing lines.
  • Keep a list of contract lines which are currently added to the contract.
  • Remove all lines that don't exist anymore, the user has removed them.

Here's the code:

class ResolveContractLines
{
    public function __construct(
        CreateOrUpdateContractLine $createOrUpdateContractLine,
        RemoveContractLine $removeContractLine
    ) { /* … */ }

    public function execute(
        Contract $contract,
        ContractLinesCollection $contractLinesCollection
    ) {
        $lineIds = [];

        foreach ($contractLinesCollection as $contractLineData) {
            $contractLine = $this->createOrUpdateContractLine
                ->execute($contractLineData);

            $lineIds[] = $contractLine->id;
        }

        $contractLinesToRemove = ContractLine::query()
            ->whereContract($contract)
            ->whereNotIn('id', $lineIds)
            ->get();

        foreach ($contractLinesToRemove as $contractLine) {
            $this->removeContractLine->execute($contractLine);
        }
    }
}

Besides composing actions together, they are also great for testing. Because of an action's small size and single responsibility, it can be unit tested very efficiently.

Actions also encapsulate most of the business logic for the app: generating contract numbers, changing statuses, handling side-effects in an explicit way,… This makes it easier for developers to reason about what the application does, as most of its business is encapsulated as actions.

If you're into DDD, you're probably thinking of commands right now. Actions are a simpler version of them. There's no command bus and actions may directly return values. For the scope of our projects, it's a very manageable approach.

# ValueObjects

You're probably wondering how this domain stuff ties together with controllers or CLI commands. That's of course the place where you'll use them. There's one more abstraction we need to understand though: value objects.

Update: since writing this blog post there has been an interesting discussion on the name of "value object". We've changed the name to "data transfer object". You can read more about this naming here.

Have you noticed the ContractLinesCollection passed to the ResolveContractLines action in the previous example? That's a value object.

Working with user input isn't always straight forward. For example, in Laravel applications you'll get an array of form data or an array of CLI arguments, the rest is up to you.

Value objects are a representation of that user data, in a structured way. Because we want don't want to concern our actions with input validation, we pass them a value object. There's one rule applied to value objects: if they exist, they are valid.

Most of the time, value objects are a simple mapping between validated request data, and properties that can be used by actions.

Here's an example of a value object:

class ContractLineData
{
    public $price;
    public $dateFrom;
    public $dateTo;
    public $article;

    public static function fromArray(
        array $input
    ): ContractLineData {
        return new self(
            $input['price'],
            Carbon::make($input['date_from']),
            Carbon::make($input['date_to']),
            Article::find($input['article_id'])
        );
    }

    public function __construct(
        int $price,
        Carbon $dateFrom,
        Carbon $dateTo,
        Article $article
    ) { /* … */ }
}

Because of convenience, we're using public properties. You can imagine why we're looking forward to strongly typed and readonly properties in PHP.

Value objects allow actions to only focus on the actual action, and not be concerned whether input is valid or not. Furthermore, it's easy to fake a value object, making tests simpler once more.

# Tying it together

Up until this point, I've said almost nothing about controllers or CLI commands, and how they fit into this picture. That's intentional.

See, because our domains are split into separate areas, we're able to develop a whole domain, without ever writing a single controller or view. Everything in the domain is easily testable, and almost every domain can be be developed side by side with other domains.

In larger projects, this is a highly efficient approach. We've got two or three backend developers working on one project, and each of them has a domain they are working on next to each other.

Also, because every domain is tested, we're very certain that all business logic required by the client works as intended, before writing a single form and integration tests.

Once a domain is done, it can be consumed. The domain itself doesn't care when or where it is used, its usage rules are clear to the outside.

This means we're able to build one or more applications, using the existing domains. In one of our projects, there's an admin HTTP application and a REST API. Both of them use the same domains; their actions, models, rules, etc. You can see how this approach is not only efficient during development, but also enables for much better scaling.

Here's an example of how a controller in the admin HTTP application looks:

class ContractsController
{
    public function index() { /* … */ }

    public function edit(Contract $contract) { /* … */ }

    public function update(
        Contract $contract,
        UpdateContract $updateContract,
        UpdateContractRequest $updateContractRequest
    ) {
        $contract = $updateContract->execute(
            $contract,
            ContractData::fromRequest($updateContractRequest)
        );
        
        return new ContractViewModel($contract);
    }
}

Almost all our controllers actions are as simple as this:

  • Validate the request data and parse it into a value object.
  • Execute the action, we don't care anymore what happens underneath at this point.
  • Return the result, in our case using view models.

# In closing

Structuring code in domains increases efficiency between developers on a single project. Furthermore, it decreases the complexity of maintenance, because sub-systems are separated and well tested.

By using actions and value objects, you're able to communicate with the domain in a controlled and testable way. While it takes longer to initially write, this approach pays off very quickly, even during early development.

Maybe the most important reason for structuring our code this way, is that it's easier to understand. We humans don't think in abstracts like "models", "actions" and "rules"; we categorize complex business processes into sub-systems. Things like "contracts" and "invoicing".

I've been structuring complex code bases like this for two years, and can say from experience that it's significantly more easy to reason about them now. In end, I believe developer experience is equally important as theoretical knowledge and paradigms to succeed.


👋 Hi, thanks for reading! I hope this post can help you in one way or another.

If you want to talk more about this topic –I do– you can always send me a Tweet or e-mail. Here's my Twitter, and here my e-mail.

]]>
2018-10-16T00:00:00+00:00
<![CDATA[ Laravel view models ]]> https://www.stitcher.io/blog/laravel-view-models Update: I've written a new version of this post, as part of my Laravel beyond CRUD series. You can read it here.

View models are an abstraction to simplify controller and model code. View models are responsible for providing data to a view, which would otherwise come directly from the controller or the model. They allow a better separation of concerns, and provide more flexibility for the developer.

In essence, view models are simple classes that take some data, and transform it into something usable for the view. In this post I'll show you the basic principles of the pattern, we'll take a look at how they integrate in Laravel projects, and finally I'll show you how we use the pattern in one of Spatie's, our company, projects.

Let's get started. Say you have a form to create a blog post with a category. You'll need a way to fill the select box in the view with category options. The controller has to provide those.

public function create()
{
    return view('blog.form', [
        'categories' => Category::all(),
    ]);
}

The above example works for the create method, but let's not forget we should also be able to edit existing posts.

public function edit(Post $post)
{
    return view('blog.form', [
        'post' => $post,
        'categories' => Category::all(),
    ]);
}

Next there's a new business requirement: users should be restricted in which categories they are allowed to post in. In other words: the category selection should be restricted based on the user.

return view('blog.form', [
    'categories' => Category::allowedForUser(
        current_user()
    )->get(),
]);

This approach doesn't scale. You'll have to change code both in the create and edit method. Can you imagine what happens when you need to add tags to a post? Or if there's another special admin form for creating and editing posts?

The next solution is to have the post model itself provide the categories, like so:

class Post extends Model
{
    public static function allowedCategories(): Collection 
    {
        return Category::query()
            ->allowedForUser(current_user())
            ->get();
    }
}

There are numerous reasons why this is a bad idea, though it happens often in Laravel projects. Let's focus on the most relevant problem for our case: it still allows for duplication.

Say there's a new model News which also needs the same category selection. This causes again duplication, but on the model level instead of in the controllers.

Another option is to put the method on the User model. This makes the most sense, but also makes maintenance harder. Imagine we're using tags as mentioned before. They don't rely on the user. Now we need to get the categories from the user model, and tags from somewhere else.

I hope it's clear that using models as data providers for views also isn't the silver bullet.

In summary, wherever you try to get the categories from, there always seems to be some code duplication. This makes it harder to maintain and reason about the code.

This is where view models come into play. They encapsulate all this logic so that it can be reused in different places. They have one responsibility and one responsibility only: providing the view with the correct data.

class PostFormViewModel
{
    public function __construct(
        User $user, 
        Post $post = null
    ) {
        $this->user = $user;
        $this->post = $post;
    }
    
    public function post(): Post
    {
        return $this->post ?? new Post();
    }
    
    public function categories(): Collection
    {
        return Category::allowedForUser($this->user)->get();
    }
}

Let's name a few key features of such a class:

  • All dependencies are injected, this gives the most flexibility to the outside.
  • The view model exposes some methods that can be used by the view.
  • There will either be a new or existing post provided by the post method, depending on whether your creating or editing a post.

This is what the controller looks like:

class PostsController
{
    public function create()
    {
        $viewModel = new PostFormViewModel(
            current_user()
        );
        
        return view('blog.form', compact('viewModel'));
    }
    
    public function edit(Post $post)
    {
        $viewModel = new PostFormViewModel(
            current_user(), 
            $post
        );
    
        return view('blog.form', compact('viewModel'));
    }
}

And finally, it can be used in the view like so:

<input value="{{ $viewModel->post()->title }}" />
<input value="{{ $viewModel->post()->body }}" />

<select>
    @foreach ($viewModel->categories() as $category)
        <option value="{{ $category->id }}">
            {{ $category->name }}
        </option>
    @endforeach
</select>

These are the two benefits of using view models:

  • They encapsulate the logic
  • They can be reused in multiple contexts

# View models in Laravel

The previous example showed a simple class with some methods. This is enough to use the pattern, but within Laravel projects, there are a few more niceties we can add.

For example, you can pass a view model directly to the view function if the view model implements Arrayable.

public function create()
{
    $viewModel = new PostFormViewModel(
        current_user()
    );
    
    return view('blog.form', $viewModel);
}

The view can now directly use the view model's properties like $post and $categories. The previous example now looks like this:

<input value="{{ $post->title }}" />
<input value="{{ $post->body }}" />

<select>
    @foreach ($categories as $category)
        <option value="{{ $category->id }}">
            {{ $category->name }}
        </option>
    @endforeach
</select>

You can also return the view model itself as JSON data, by implementing Responsable. This can be useful when you're saving the form via an AJAX call, and want to repopulate it with up-to-date data after the call is done.

public function update(Request $request, Post $post)
{
    // Update the post…

    return new PostFormViewModel(
        current_user(),
        $post
    );
}

You might see a similarity between view models and Laravel resources. Remember that resources map one-to-one on a model, when view models may provide whatever data they want.

In one of our projects, we're actually using resources in view models!

class PostViewModel
{
    // …
    
    public function values(): array
    {
        return PostResource::make(
            $this->post ?? new Post()
        )->resolve();
    }
}

Finally, in this project we're working with Vue form components, which require JSON data. We've made an abstraction which provides this JSON data instead of objects or arrays, when calling the magic getter:

abstract class ViewModel
{
    // …
    
    public function __get($name): ?string
    {
        $name = Str::camel($name);
    
        // Some validation…
    
        $values = $this->{$name}();
    
        if (! is_string($values)) {
            return json_encode($values);
        }
    
        return $values;
    }
}

Instead of calling the view model methods, we can call their property and get a JSON back.

<select-field
    label="{{ __('Post category') }}"
    name="post_category_id"
    :options="{{ $postViewModel->post_categories }}"
></select-field>

# Wait, what about view composers?

I hear you! There's a whole separate blog post on that topic. You can read it here.


In summary, view models can be a viable alternative to working with the data directly in a controller. They allow for better reusability and encapsulate logic that doesn't belong in the controller.

You're also not confined to forms when using them. At Spatie we also use them to populate facet filter options, based on a complex context the user is currently working in.

I'd recommend trying this pattern out. You don't need anything to get started by the way. All Laravel gimmicks listed above are optional and can be added depending on your use case.

And just in case you'd like to use Laravel gimmicks, we've got a package for it: spatie/laravel-view-models 🤗.

]]>
2018-09-10T00:00:00+00:00
<![CDATA[ Eloquent MySQL views ]]> https://www.stitcher.io/blog/eloquent-mysql-views MySQL views are a way of storing queries on the database level, and producing virtual tables with them. In this post we'll look at why you want to use them and how they can be integrated in Laravel with Eloquent models.

If you're already convinced of the power of MySQL views, or just want to know how to implement them in Laravel, you're free to skip ahead.

# Benefits of MySQL views

A view in MySQL stores the result of a query in a table-like structure. You're able to query this view just like you would query a normal table.

The power of views is twofold:

  • Complex queries with joins and unions can be represented as a queryable table on their own.
  • MySQL is generally smarter than us when it comes to querying data. Compared to using collections or array functions in PHP, there's a big performance gain.

There's also a caveat to using views though. Depending on the kind of query, MySQL will need to construct an "in memory" table representing the view, at runtime. This operation is called table materialization and happens when using certain keywords like GROUP BY, or aggregated functions.

The takeaway is that views might actually hurt query performance, depending on the kind of query you're executing. As with all things, views are a good solution for some problems, but a terrible idea for others. Use them wisely, and read up on their restrictions here.

# Views and their alternatives

Let's look at a real-life example, to demonstrate how we could solve a given problem.

We've got a model MeterReading which logs a meter reading done in an apartment building. Every unit in the building has its own electricity, water and gas meters.

Every reading is listed in the database with a reference to the unit, the date, the user doing the reading, the type, and the actual meter value. Type in this example is electricity, water or gas.

This is what a simplified migration of this table looks like:

Schema::create('meter_readings', function (Blueprint $table) {
    $table->unsignedInteger('unit_id');
    $table->unsignedInteger('user_id');

    $table->string('type');
    $table->dateTime('date');
    $table->unsignedInteger('value');
});

Now the client asks us to generate reports based on this raw data. He wants to see an overview of the units, where every row represents the readings for that unit, on that day, and whether all readings were done or not.

In short, he wants to see this:

+---------+---------+------------+-------------+-------+-----+
| unit_id | user_id | date       | electricity | water | gas |
+---------+---------+------------+-------------+-------+-----+
|      14 |      72 | 2018-08-19 |           0 |     1 |   0 |
|      59 |      61 | 2018-08-06 |           0 |     0 |   1 |
|      41 |      64 | 2018-08-02 |           1 |     1 |   1 |
|      41 |      45 | 2018-08-02 |           1 |     1 |   1 |
...
|      41 |      51 | 2018-08-02 |           1 |     1 |   1 |
+---------+---------+------------+-------------+-------+-----+

The report show a data set that is grouped by unit, user and day; and the corresponding readings done for at the time.

Here are a few ways of generating this report.

# On the fly

We always query all the data, and group it in our code. This is the most easy way of doing it, but has some downsides:

  • PHP and Laravel collections are slow, compared to the optimised algorithms MySQL can use.
  • Building a virtual data set means you'll have to manually implement pagination. One row can represent multiple models.
  • You're adding a lot of code to manage that special collection of readings.

# Using a raw query

We can of course skip PHP and build the raw query to fully use the power of MySQL. While this solves the performance issue, we're still working with a custom data set which can't make use of standard pagination. Also, you're now maintaining a big SQL query somewhere in your code. It's probably a string somewhere in PHP, or –slightly better– a separate sql file.

# Projecting the changes

We could make a separate model called MeterReadingReport, and use event hooks on MeterReading to manage these reports.

Every time a reading is added, we can get or create a report for that unit, day and user; and update the data accordingly.

Now there's a separate model that's simple to query. There's no more performance impact and the pagination issue is also solved.

But on the other hand, there's a lot more code to manage these event hooks. Creating reports is one thing, but what if a reading is updated or deleted? That's a lot of complexity we need to manage.

Projecting events into other models isn't a bad idea though. It's one of the key features in event sourcing. If you've got the right setup, making projectors would definitely be an option.

While we do have a package that handles this exact use case (laravel-event-projector), it seemed overkill for this use case; especially since there are a lot of other "normal" models in this project.

# Finding the middle ground

Looking at all the possible solutions, we can make a simple list of requirements:

  • As less overhead as possible in the code base.
  • Good performance.
  • Be able to use the standard Laravel features without any workarounds.

MySQL views are this perfect middle ground. Let's look at how they are implemented.

# SQL views in Laravel

To work with a view, we'll have to first create a query that can build this view. While many people are scared of SQL –modern ORMs made us way too lazy– I find it a lot of fun.

Beware that I'm no SQL master, so there might be things that could be done better. I also won't explain what this query does exactly, as it'll be different for your use case.

In this case, it generates the table listed above. This is it:

SELECT 
    unit_id
    , user_id
    , DATE_FORMAT(`date`, '%Y-%m-%d') AS day
    , COUNT(CASE WHEN type = 'electricity' THEN type END) 
        AS `electricity`
    , COUNT(CASE WHEN type = 'water' THEN type END) 
        AS `water`
    , COUNT(CASE WHEN type = 'gas' THEN type END) 
        AS `gas`
    
FROM 
    meter_readings
    
GROUP BY
    unit_id
    , user_id
    , day
;

It's very easy to build this query in your favourite SQL browser, and afterwards plug it into your project.

How to plug it in, you ask? Very simple, with a migration.

public function up()
{
    DB::statement($this->dropView());
    
    DB::statement($this->createView());
}

First of all, dropView is required, because Laravel only drops tables when doing a fresh migration. It's as simple as this:

    private function dropView(): string
    {
        return <<<SQL
DROP VIEW IF EXISTS `meter_reading_reports`;
SQL;
    }

You notice I prefer Heredoc in these cases, a separate SQL file is of course equally good.

Michael Dyrynda pointed out to me that there's a --drop-views flag you can pass to the migrate command. So, technically, this manual dropping isn't required. I prefer this way though, because now we don't have to remember to add the extra flag.

Next up, the createView method returns the query, with some added syntax. I've shortened the sample a bit, but you get the point.

    private function createView(): string
    {
        return <<<SQL
CREATE VIEW `meter_reading_reports` AS

SELECT /* … The query */
SQL;
    }

Sidenote: I'm very much looking forward to PHP 7.3 and flexible Heredoc syntax.

Now that we have a migration in place, all else just works like normal Laravel!

class MeterReadingReport extends Model
{
    protected $casts = [
        'day' => 'date',
    ];
    
    public function unit(): BelongsTo
    {
        return $this->belongsTo(Unit::class);
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

We're using a simple model, without any workarounds whatsoever. Relations work just like normal, casting like you're used to, pagination works like it should be, and there no more performance impact.

The only thing that's not possible is of course writing to a view. It is actually possible to do it in MySQL, but completely irrelevant to our use case.

Maybe you can already see some use cases where MySQL views might be useful? Maybe you have a followup question or remark? I'd love to hear from you! You can reach me on Twitter or via e-mail.

]]>
2018-08-27T00:00:00+00:00
<![CDATA[ The web in 2045 ]]> https://www.stitcher.io/blog/the-web-in-2045 On August 6 1991, Sir Tim Berners-Lee put the world's first website online. It's been 27 years since that first website has been reachable via the world wide web. With the massive progression we've seen throughout the web's lifetime, I can't help but wonder what will happen in the next 27 years, what the web will look like in 2045.

# A brief history

What better way to know the future, than to look at the past? There's an excellent blog called "The History Of The Web". Thanks to Jay Hoffman, the writer of the blog, I've been fascinated by the roots of the web.

On August 6, 1991 Tim Berners-Lee puts the first website online. A few months later, on October 29 of the same year, the first HTML draft is posted on the www-talk mailing list.

Three years went by until Netscape Navigator, the most popular browser at the time, was released on October 13, 1994. It took Microsoft almost a year longer to release their own Internet Explorer 1 on August 15 of 1995. In December of 1996, the first iteration for CSS was conceptualised.

Mozilla released Firefox more than ten years after the beginning of the web in 2004. Only a few years later, Apple flipped the web to its mobile side by presenting the very first iPhone on January 8, 2007.

Twenty-three years after the first mention of HTML modern websites found their identity as we know it today, when HTML5 was made the formal recommendation by the W3C on October 28, 2014.

It's amazing to see how the web has evolved in less than three decades. Not only its technical boundaries were pushed; people also kept finding innovating ways for what the technology was used for. Think about sites like GeoCities or NeoPets; the first blog post written in 1997; or some recent examples like /r/place on Reddit and an experience beyond words crafted by SB Nation.

# Today

While the web's primary focus was to distribute content, its users have shaped it into a completely different, broader platform.

It's only in more recent years that we've been able to observe – and be part of – the unique phenomenon of the JavaScript world. While the language has been around since the nineties, it were frameworks like Ember, Backbone and Angular that opened a whole new area the web could grow in.

It's impossible to pinpoint an exact date on when people started looking at JavaScript as something more than a simple scripting language. But there are a few milestones worth mentioning.

  • jQuery's initial release in August, 2006.
  • AngularJS was released in October, 2010.
  • React saw the light in March, 2013.

Especially with the modern frameworks, better syntax was required; and projects like Babel came into view. This is where it starts to get real interesting: JavaScript in the browser becoming a compilation target, for other languages and supersets.

Gary Bernhardt, a well known public speaker; said that, in order for JavaScript to become as successful as it is today, it had to really suck. It's only then that people start investing in alternatives. And when JavaScript is the only thing that runs in the browsers, people are really forced to think out of the box.

With the arrival of frontend frameworks in JavaScript, people needed to start investing in performance. On the one hand, browser vendors are doing amazing things with their JavaScript engines. On the other hand, one of the most creative, out of the box thinking solutions; must have been asm.js.

Here you have an optimised subset of JavaScript; being able to run, for example, a 3D game engine—in the browser. With asm.js, and Web Assembly following; web technologies can be used for yet another, completely different goal.

It begs the question: "what is the web?"

The technologies the web was built upon: HTTP, HTML, CSS and JavaScript, became technologies to make applications and games; some of the programs we use to build the web, are built on these same technologies themselves.

Does "the web today" refer to all these technologies and creations, or just a collection of connected documents, built on top of the same technologies? Is the browser's goal still just that: browsing documents; or has it become a platform for all kinds of things, whatever we can imagine?

# 2045

With the web advancing so fast, one can only wonder what it will look like a few decades from now.

One of the key changes could be the sandbox we're all using today: the browser. Will applications stay confined to that browser window, or will they break free and live as first-class programs on your operating system?

The mobile world is already moving in this direction with progressive web apps. Chrome OS was completely built on top of the web, independent of the browser. But, some might argue, it was too ahead of its time.

Imagine a world where web apps can be "installed" via an app store; where you don't need bookmarks or URLs anymore, but simply open an app, like we open the browser today. Obviously, being built on top of the web; these apps don't need to be installed, they just work, everywhere.

Imagine JavaScript and DOM engines baked into operating systems. No more Electron or Java for easy cross-platform programming. All programs can be shared, everything will be interconnected.

And once all that is achieved, someone will come along, claiming a new invention: a simple web app for browsing and sharing content…


Hi, thanks for reading! What's your view for the web in 2045? Feel free to share your thoughts on Twitter or via e-mail, I'd love to chat!

And if you're reading this in 2045: what's your opinion on this blog post, looking back? Where will the web be in, say, another 27 years?

]]>
2018-08-15T00:00:00+00:00
<![CDATA[ Service locator: an anti-pattern ]]> https://www.stitcher.io/blog/service-locator-anti-pattern As a Laravel developer, I'm confronted daily with the service locator pattern. Every facade call and several helper functions are built upon it.

Let's take a look at a common facade call: Auth::user(). The Auth facade will reach into Laravel's service container, grab the registered component, and forward the static call to that component. In this case, it'll return the logged in user.

During a discussion with my colleagues, I found it difficult to put into words what exactly is wrong with grabbing things out of the container –a service locator– so I decided to write my thoughts down, with an example: a class CreatePostAction which is tasked to create a blog post, based on a set of parameters.

class CreatePostAction
{
    public function __invoke(
        string $title, 
        string $body
    ): Post
    {
        return Post::create([
            'title' => $title,
            'body' => $body,
            'author_id' => Auth::user()->id,
        ]);
    }
}

I want to highlight three problems with this approach, directly caused by the use of a service locator.

  • There's a bigger chance of runtime errors.
  • The code is obfuscated to the outside.
  • It increases cognitive load.

Let's look at these problems, one by one.

# Runtime- instead of compile time errors

Before even looking into this first problem, there's one assumption I'll make. That is that, as a developer, you prefer to know bugs in your code as early as possible, so that you can fix them as early as possible.

I'll assume that you don't like a situation where a client tells you a production project is broken, and the issue can only be reproduced by taking several steps.

As the name says, runtime errors can only be discovered by running the program. Truth be told: PHP, being an interpreted language; highly leans towards these kind of errors. You cannot know if a PHP program will work before running it.

There's nothing wrong with that, but my argument here is that every place we can avoid these errors, we should.

Compile time errors are errors that can be detected without running the code. For example: in your IDE or using a static analysis tool. The benefit is that you know a piece of code will absolutely work, even without testing it.

Let's put that into practice. What does Auth::user() return? A logged in User—most of the time.

Our action class doesn't know anything about the system it lives in, except the things we tell it. This means that, when calling Auth::user()->id, we assume that the surrounding system has a logged in user, with an id.

Of course, your first thought is that we know there's a user, because this action is called within a controller that requires a logged in user. I'll come back to that argument later on.

For now, speaking from a mathematical point of view, it's impossible to prove whether Auth::user()->id will work, without running it. There are two ways to fix it, from the action's perspective.

By doing a runtime check:

class CreatePostAction
{
    public function __invoke(
        string $title, 
        string $body
    ): Post
    {
        if (! Auth::user()) {
            throw new Exception('…');
        }
        
        // ...
    }
}

Or by requiring a valid user, before executing:

class CreatePostAction
{
    public function __invoke(
        string $title, 
        string $body,
        User $author
    ): Post
    {
        // ...
    }
}

I know you have arguments why this will never happen and I shouldn't be worried about it; I'll address those arguments soon.

# Obfuscated classes

Before looking at the biggest problem, how service locators affect cognitive load; there's the issue with obfuscated classes. Let's look at our action's definition.

class CreatePostAction
{
    public function __invoke(
        string $title, 
        string $body
    ): Post
    { /* ... */ }
}

I've blogged and spoken about this a lot already: developers don't read every line of code, they scan it.

At the time of writing the code, it all seems obvious: you know a blog post requires a logged in user. However, for the developer working in your legacy code, that intent is not clear. Not unless he's reading every single line of code.

Imagine being that person: having to work in a legacy project where you need to read every line of code, in order to get the general idea of what's happening.

You might as well not be interested in the specifics of how a post is created, you just want to know what's required to do so. There's two ways to solve this issue.

Either be using docblocks; meaning a lot more work for both the author and reader, and it clutters your code:

class CreatePostAction
{
    /**
     * This action will create a post, 
     * and attach the logged in user as its author.
     *
     * @param string $title
     * @param string $body
     *
     * @return Post
     */
    public function __invoke(
        string $title, 
        string $body
    ): Post
    { /* ... */ }
}

Or by injecting the user:

class CreatePostAction
{
    public function __invoke(
        string $title, 
        string $body,
        User $author
    ): Post
    { /* ... */ }
}

Which one do you prefer? Remember: from the perspective of the person working in a legacy project, and it's not just one class, there are dozens and dozens.

# Increased cognitive load

This all leads up to the final, and major, problem: cognitive load. I already wrote a lot on this topic, and I'll share some links at the end of this post.

The important question, which counters most of the pro-arguments for service locators; is how much brain effort you, the developer, has to spend on trivial questions like:

How sure am I this code will actually work?

Let's look at the most basic example: Auth::user()->id. I work on Laravel projects and admit to have used this piece of code numerous times. Here's a non-exhaustive list of questions popping into my head when writing this code:

  • Am I sure a user is logged in at this point?
  • Should I add an extra check, to be sure?
  • What context will this method be called from?
  • Are there any future features in the project's scope I need to take into account?
  • Should I add a test to be sure this never breaks in the future?

These are all such trivial questions, and I need to think about them every time I use a facade. How much more easy is it to simply say:

I need the logged in user to do this action, and the context which is calling this action can figure it out from there.

class CreatePostAction
{
    public function __invoke(
        string $title, 
        string $body,
        User $author
    ): Post
    { /* ... */ }
}

Sure, compile time errors and less code are niceties, but my main problem is this cognitive load. I don't want to ask all these questions every time I use a facade.

Seasoned Laravel developers will tell me this is the way the framework works and we should embrace it. They are right, of course. But making the assumption that "it will work" isn't good enough for me. At least, it's no argument against increased cognitive load, as you're still left with a lot of questions about the surrounding context.

# Dependency injection solves it

Dependency injection, of course; fixes this. It's a pattern which allows for inversion of control and clarifies intent. It's also perfectly possible to do proper DI in Laravel; and, in my opinion, we should do it more.

I've written about DI before, feel free to read up on it here. I also recently gave a talk about cognitive load, from a visual perspective. You can find it here.

]]>
2018-08-10T00:00:00+00:00
<![CDATA[ The Visual Perception of Code ]]> https://www.stitcher.io/blog/visual-perception-of-code How do we read code? Are there things we can do to make that reading more easy? I recently gave a talk about this exact topic.

In this talk, I gave six pointers to improve this visual perception, to make it easier to read your own code: fonts, code folding, colours, patterns, documentation and names.

These are the links mentioned in the video:

The colour scheme used is a port of Mozilla's Photon Light theme. Here's the PHPStorm version.

I you have any thoughts coming to mind, if you want to discuss this further or tell me I'm wrong; you can reach me on Twitter or via e-mail.

]]>
2018-06-09T00:00:00+00:00
<![CDATA[ Scopes in JetBrains IDEs ]]> https://www.stitcher.io/blog/phpstorm-scopes Any JetBrains IDE has an amazing feature that can significantly improve your development experience; whether it's PhpStorm, WebStorm, IntelliJ IDEA, PyCharm, or any other project; this feature enables better search and allows for custom file colours.

For example, this is what I'm talking about:

A tree view configured with coloured scopesA tree view configured with coloured scopes

These colours allow you to easily recognise files, and that in turn allow you to think more freely about things that really matter when coding. First you'll want to configure one or more scopes. A scope is a set of textual filters that are applied on your files, you can configure them by going to Settings > Scopes.

You can use the buttons to include and exclude folders and files, or you can write the filters yourself. There's a special syntax to do that, you can read about it described here. Don't forget you can expand the text area for easier configuration:

# File colours

Every scope can be applied a specific colour. This makes it easy to easily spot files.

By applying colours to a scope, you'll see them in the tree view, in file tabs and when using file navigation.

# Filtering by scope

Besides colours, scopes also allow for easy filtering. For example, in the tree view:

File coloursFile colours

But also in the finder:

File coloursFile colours

# Defaults

Setting up scopes shouldn't take longer than 10 minutes every project, and saves a lot of time in the long run. There's also the possibility to set default options though, which will be used every every time you create a new project. Go to File > New Project Settings > Preference for New Projects and configure your default scopes and colours over there, the same way you'd do as explained before.

And just in case you'd need some inspiration, these are my default scopes:

App
file:app//*||file:config//*||file:routes//*||file:app||file:config||file:routes||file:src//*||file:src

Resources
file:resources//*||file:resources

Database
file:database//*||file:database
]]>
2020-09-26T00:00:00+00:00
<![CDATA[ Acquisition by giants ]]> https://www.stitcher.io/blog/acquisition-by-giants Yesterday, Microsoft acquired GitHub. I don't think GitHub is in any immediate danger of becoming obsolete or feature bloated. But we should think about what this acquisition, one of many; means.

I say many, because GitHub isn't by far Microsoft's first big acquisition. Think of Skype and Nokia a few years back, or Linkedin and Minecraft more recently. Who can blame them? It's only natural that a company continues to look for ways to improve their market footprint, especially in the tech world.

But now what? GitHub becomes one of the many products swallowed by tech giants. It will become even more difficult for smaller products to stand a competing chance. Should we, the tech industry, be worried about big monopolies growing larger and larger?

# Embrace, extend, and extinguish

What better way to look at the future, than to look at the past? Many will say Microsoft isn't deploying their "embrace, extend, and extinguish" strategy anymore; but are they?

Here's what this strategy means, from wikipedia.

  • Embrace: Development of software substantially compatible with a competing product, or implementing a public standard.
  • Extend: Addition and promotion of features not supported by the competing product or part of the standard, creating interoperability problems for customers who try to use the 'simple' standard.
  • Extinguish: When extensions become a de facto standard because of their dominant market share, they marginalize competitors that do not or cannot support the new extensions.

It sounds like a smart strategy. It may not be called like this anymore but it seems reasonable that, if Microsoft still wants to grow; they must look at how to outgrow their competitors.

We've seen them embracing a lot of technologies and platforms recently. Think of open sourcing .NET, developing VSCode, embedding Linux etc. Microsoft is doing a lot of good for the open source community lately, but you can't help but think they are also serving their own agenda while at it.

It's only guessing what Microsoft will do to extend GitHub, but we've already seen a lot of nice features added to other acquired products. Growing them to, indeed, part of the standard.

Let's not forget the obvious example though: VSCode, a direct competitor to GitHub's Atom editor. Both are built on the same technology: Electron, managed by GitHub.

So will the tech community allow a giant to extinguish all its competitors? Time will tell.

# The drive for innovation

Thinking back 20 years ago, the browser wars were raging. There was a lot of competition, and with that competition came innovation. We must not forget that it was this period of rivalry between many different players, that lead to the web platform we have today. And it's a platform that grew beyond the wildest expectations of these first pioneers.

So we must wonder: what if there's less and less competition in the tech world? What if the giants, Microsoft, Apple, Amazon, Google and Facebook; only grow larger and larger? What does it mean for innovation?

It's worrisome to see that in the startup culture, it's actually one of the biggest questions right at the beginning: "what's our exit strategy?" The goal of almost every startup, from the start, is to be absorbed by a bigger company, in just a few years.

The drive for innovation gets replaced by the need for money. But we cannot afford stagnation. We must be aware that this, still very young, industry has very large odds of becoming the same as so many others.

To me, these are the thoughts behind GitHub's acquisition. And yes, they are scary.

]]>
2018-06-05T00:00:00+00:00
<![CDATA[ Liskov and type safety ]]> https://www.stitcher.io/blog/liskov-and-type-safety I've been fascinated by type systems in programming languages for a while now. Recently, something clicked for me about inheritance and types.

Not only did it clarify type variance, I also understood what the Liskov substitution principle actually is about. Today, I'm going to share these insights with you.

# Prerequisites

I'll be writing pseudo code to make clear what I'm talking about. So let's make sure you know what the syntax of this pseudo code will be.

A function is defined like so.

foo(T) : void

bar(S) : T

First comes the function name, second the argument list with types as parameters, and finally the return type. When a function returns nothing, it's indicated as void.

A function can extend — overwrite — another function, as can types. Inheritance is defined like so.

bar > baz(S) : T

T > S

In this example, baz extends bar, and S is a subtype of T. The last step is being able to invoke the function, which is done like so.

foo(T)

a = bar(S)

Once again: it's all pseudo code and I'll use it to show what types are, how they can and cannot be defined in combination with inheritance, and how this results in type-safe systems.

# Liskov substitution principle

Let's look at the official definition of the LSP.

If S is a subtype of T, then objects of type T may be replaced with objects of type S
Wikipedia

Instead of using S and T, I'll be using more concrete types in my examples.

Organism > Animal > Cat

These are the three types we'll be working with. Liskov tells us that wherever objects of type Organism appear in our code, they must be replaceable by subtypes like Animal or Cat.

Let's say there's a function used to feed an Organism.

feed(Organism) : void

It must be possible to call it like so:

feed(Animal)
feed(Cat)

Try to think of function definition as a contract, a promise; for the programmer to be used. The contract states:

Given an object of the type Organism, I'll be able to execute and feed that Organism.

Because Animal and Cat are subtypes of Organism, the LSP states that this function should also work when one of these subtypes are used.

This brings us to one of the key properties of inheritance. If Liskov states that objects of type Organism must be replaceable by objects of type Animal, it means that Animal may not change the expectations we have of Organism. Animal may extend Organism, meaning it may add functionality, but Animal may not change the certainties given by Organism.

This is where many OO programmers make mistakes. They see inheritance more like "re-using parts of the parent type, and overriding other parts in the sub-type", rather than extending the behaviour defined by its parent. This is what the LSP guards against.

# Benefits of the LSP

Before exploring the details of type safety with inheritance, we should stop and ask ourselves what's to gain by following this principle. I've explained what Barbara Liskov meant when she defined it, but why is it necessary? Is it bad to break it?

I mentioned the idea of a "promise" or "contract". If a function or type makes a promise about what it can do, we should be able to blindly trust it. If we can't rely on function feed being able to feed all Organisms, there's a piece of undocumented behaviour in our code.

If we know that the LSP is respected, there's a level of security. We may trust that this function will do the thing we expect; even without looking at the implementation of that function. When the contract is breached, however; there's a chance of runtime errors that both the programmer and the compiler could not –or did not– anticipate for.

In the above examples, we looked at respecting the LSP form the developer's point of view. There's another party involved though: a language's type system. A language can be designed in a type-safe way or not. Types are the building blocks to mathematically proof whether a function will do the thing you want it to do.

So, next up; we're going to look at the other side: type-safety on the language level.

# Type safety

To understand how type safety can –or cannot– be guaranteed by a language, let's look at these functions.

take_care(Animal) : void

take_care > feed(Animal) : void

As you can see, feed extends take_care and follows its parent signature one-to-one. Some programming languages don't allow children to change the type signature of their parent. This is called type invariance.

It's the easiest approach to handle type safety with inheritance, as types are not allowed to vary when inheriting.

But when you think back at how our example types are related to each other, we know that Cat extends Animal. Let's see whether the following is possible.

take_care(Animal) : void

take_care > feed(Cat) : void

The LSP only defines rules about objects, so on first sight, the function definition itself doesn't break any rules. The real question is: does this function allow for proper use of the LSP when it's called?

We know that feed extends from take_care, and thus provides at least the same contract as its parent. We also know that take_care allows Animal and its sub-types to be used. So feed should also be able to take an Animal type.

feed(Animal)

// Type error

Unfortunately, this is not the case. There's a type error occurring. Can you see what we're doing here? Instead of applying the LSP only to the parameters of a function, we're also applying the same principles to the function itself.

Wherever an invocation of take_care is used, we must be able to replace it with an invocation of feed.

This especially makes sense in an OO language where a function is no standalone entity in your code, but rather part of a class, which represents a type itself.

To keep a system type-safe, it may not allow children to make the parameter types more specific. This breaks the promises given by the parent.

However, take a look at the following definition:

take_care(Animal) : void

take_care > feed(Organism) : void

Does this definition ensures type safety? It may seem backwards at first, but it does. feed still follows the contract specified by take_care. It can take Animal as an argument, and work just fine.

In this case, feed widens the parameter types allowed, while still respecting the parent's contract. This is called contravariance. Types in argument lists should be contravariant for a type system to be safe.

# Return type variance

Moving on to return types. There are a few more types we'll have to define, in order for the examples to make sense. I'm sorry in advance for the choice of words!

Excretion > Poop

And these are the functions we're working with.

take_care(Animal) : Excretion

take_care > feed(Animal) : Poop

The question now: is the overridden return type safe? In contrast to the contravariance for the argument list, this example actually is type safe!

The parent definition take_care tells us that this function will always return an object of type Excretion.

excretion = take_care(Animal)

excretion = feed(Animal)

Because Poop is a subtype of Excretion, we can be a 100% sure that whatever feed returns, it will be within the category of Excretion.

You see the opposite rule applies for return types compared to function parameters. In the case of return types, we're calling it covariance, or covariant types.

# Real-life impact

There' no guarantee that a type-safe language will always write a bug-free program. We've seen that the language design only carries half the responsibility of respecting the LSP. The other half is the programmer's task.

Languages differ though, all have their own type system, and each will have a different level of type safety.

Eiffel, for example, allows for parameter covariance. By now you know this means there's an area of wrong behaviour possible that's undetectable by the compiler. Hence there's the possibility of runtime errors.

PHP allows for constructors of child classes to have another signature, while keeping an invariant type system for all other functions. As with many things PHP, this inconsistency increases the confusion for developers.

Some languages like Java, C# and Rust have a concept that I didn't cover today: generics. Type variance also plays a big role there. That topic is out of scope for this blog post, but I might cover it in the future.

With all these differences, there's one thing to keep in mind. The safety of a type system doesn't mean a language is better or worse. I think it's fair to say that some cases would benefit from a very strong type system, while others need the exact opposite. The key takeaway is that every programmer should learn more than just the concepts and paradigms of the languages they are used to the most. A broadened view will be beneficial, now and in the future.

So what's your opinion on type safety? If you're up for it, I'd love to talk about it even more: you can reach me on Twitter or e-mail.

]]>
2018-05-19T00:00:00+00:00
<![CDATA[ Dependency injection for beginners ]]> https://www.stitcher.io/blog/dependency-injection-for-beginners You're in the car business, your job is to make cars on-demand. The object-oriented programmer in you says: "no problem, I'll make a blueprint that I can use to make as many cars as I want!".

class Car
{
    public function drive()
    {
        // ...
    }
}

For this car to work, it needs an engine and wheels. Now, there are several approaches to achieve that goal. You could, for example, do the following:

class Car
{
    public function __construct()
    {
        $this->engine = new Engine();
        
        $this->wheels = [
            new Wheel(), new Wheel(), 
            new Wheel(), new Wheel(),
        ];
    }
    
    public function drive() { ... }
}

There's the blueprint for every car you'll make! Next up, your boss comes to you and says there's a new client and he wants an electric car.

So you end up doing this.

class ElectricCar extends Car
{
    public function __construct()
    {
        parent::__construct();
        
        $this->engine = new ElectricEngine();
    }
}

"Beautifully solved"—you think. There's of course that redundant normal engine that's created when calling parent::__construct(), but at least you could re-use the wheels!

I think you can see where this is going. The next client wants a car with some fancy wheel covers, another one would like a diesel engine with those same wheel covers, another one requests a race car, and the last one wants a self driving car.
Oh—there also was a client who wanted to buy an engine to build a boat with himself, but you told your boss that wouldn't be possible.

After a while, there's a ton of blueprints in your office, each describing a very specific variation of a car. You started with a neatly ordered pile of blueprints. But after a while you had to group them in different folders and boxes, because it was taking too long to find the blueprint you're looking for.

Object oriented programmers often fall into this trap of inheritance, ending in a completely messed up codebase. So let's look at a better approach. Maybe you've heard about "composition over inheritance" before?

Composition over inheritance is the principle that classes should achieve polymorphic behavior and code reuse by their composition rather than inheritance from a base or parent class—Wikipedia

That's a lot of buzzwords. Let's just look at our car example. The principle states that Car should achieve its polymorphic behaviour by being composed of other classes.

The word polymorphic literally means "many shapes" and implies that Car should be able to do drive in many different ways, depending on the context it's used in.

With code reuse, we're trying to make code reusable; so that we don't end up with tens of classes doing almost exactly the same thing.

# What does this have to do with dependency injection?

Instead of making a unique blueprint that describes every single possible variation of a car, we'd rather have Car do one thing, and do it good: drive.

This means it shouldn't be the car's concern how its engine is built, what wheels it has attached. It should only know the following thing:

Given a working engine and four wheels, I'm able to drive!

We could say that in order for Car to work, it needs an engine and wheels. In other words: Car depends on Engine and a collection of Wheels.

Those dependencies should be given to the car. Or, said otherwise: injected.

class Car
{
    public function __construct(
        Engine $engine, 
        array $wheels
    ) {
        $this->engine = $engine;
        $this->wheels = $wheels;
    }
    
    public function drive()
    {
        $this->engine->connectTo($this->wheels);
        
        $this->engine->start();
        
        $this->engine->accelerate();
    }
}

Would you like a race car? No problem!

$raceCar = new Car(new TurboEngine(), [
    new RacingWheel(), new RacingWheel(),
    new RacingWheel(), new RacingWheel(),
]);

That client who wanted special wheel covers? You've got that covered!

$smugCar = new Car(new Engine(), [
    new FancyWheel(), new FancyWheel(),
    new FancyWheel(), new FancyWheel(),
]);

You've got a lot more flexibility now!

Dependency injection is the idea of giving a class its requirements from the outside, instead of having that class being responsible for them itself.

# What dependency injection is not

Built upon this simple principle, there are frameworks and tools that take it to the next level. You might, for example, have heard about the following things before.

# Shared dependencies

One of the most beneficial side effects of injecting dependencies, is that the outside context can control them. This means that you can give the same instance of a class to several others that have a dependency on that class.

Shared- or reusable dependencies are the ones most often getting the label "dependency injection". Though it's certainly a very good practice, sharing a dependency is not actually the core meaning of dependency injection.

# The dependency container

Sometimes it's also called "inversion of control" container, though that's not an accurate name.

Whatever the exact name, the container is a set of class definitions. It's a big box that knows how objects in your application can be constructed with other dependencies. While such a container definitely has a lot of use cases, it's not necessary to do dependency injection.

# Auto wiring

To give developers even more flexibility, some containers allow for smart, automatically determined, class definitions. This means you don't have to manually describe how every class should be constructed. These containers will scan your code, and determine which dependencies are needed by looking at type hints and doc blocks.

A lot of magic happens here, but auto wiring can be a useful tool for rapid application development.

# Service location

Instead of injecting dependencies into a class, there are some tools and frameworks that allow a class to ask the container to "give it an instance of another class".

This might seem beneficial at first, because our class doesn't need to know how to construct a certain dependency anymore. However: by allowing a class to ask for dependencies on its own account, we're back to square one.

For service location to work, our class needs to know about the systems on the outside. It doesn't differ a lot from calling new in the class itself. This idea is actually the opposite of what dependency injection tries to achieve. It's a misuse of what the container is meant to do.

# Inject everything

As it goes in real-life projects, you'll notice that dependency injection is not always the solution for your problem.

It's important to realise that there are limits to the benefits of everything. You should always be alert that you're not taking this to the extreme, as there are valid cases in which a pragmatic approach is the better solution.

# In closing

The core idea behind dependency injection is very simple, yet allows for better maintainable, testable and decoupled code to be written.

Because it's such a powerful pattern, it's only natural that lots of tools emerge around it. I believe it's a good thing to first understand the underlying principle, before using the tools built upon it. And I hope this blog post has helped with that.

If there are any thoughts coming to your mind that you want to share, feel free to reach out to me on via Twitter or e-mail.

Also special thanks to /u/ImSuperObjective2 on Reddit and my colleague Sebastian for proof reading this post.

]]>
2018-04-30T00:00:00+00:00
<![CDATA[ PHPStorm performance issues on OSX ]]> https://www.stitcher.io/blog/phpstorm-performance-issues-on-osx Are you using a fancy Mac with the latest and greatest hardware, yet still having performance issues with PHPStorm? I've been struggling with this the past few months.

It turns out, the solution might be rather unexpected. Instead of disabling plugins, inspections and what not; it seems like there's an issue with font rendering in the JRE for Mac.

This means that on certain resolutions, for certain fonts and for certain kinds of antialiasing, PHPStorm will need a lot of CPU power just to render fonts. So how to fix it? There are a few options.

  • Use another font. I was using Ubuntu Mono, and it turns out it requires quite a lot of CPU. I've switched to Monaco instead.
  • Disabling Subpixel antialiasing. Go to Preferences > Appearance & Behavior > Appearance to configure antialiasing in your editor to Greyscale instead. Your fonts won't look as good, but you'll notice a huge performance improvement.
  • Wait for JetBrains to find a fix. 2018.2 might fix some things, but the real solution will take a while. There's an active discussion on the topic here.

If you're looking for even more performance improvements that can be made in PHPStorm, take a look over here.

]]>
2018-04-25T00:00:00+00:00
<![CDATA[ What PHP can be ]]> https://www.stitcher.io/blog/what-php-can-be Have you ever wondered how your life as a PHP developer would be different if that one feature you want was added? I've made the thought experiment quite a few times already, and came to surprising conclusions.

Let's take, for example, the debate about strong types in PHP. A lot of people, including myself, would like a better type system. Strong types in PHP would definitely have an impact on my daily work. Not just strong types, I also want generics, better variance and variable types. Improvements to PHP's type system in general would have quite the impact on my programming life.

So what's stopping us from reaching a solution?

# Type theory

Not everyone agrees on the vocabulary used when talking about type systems. So let's clarify a few terms in the way that I will use them here.

Strong or weak types define whether a variable can change its type after it's defined. A simple example: say there's a variable $a = 'test';, which is a string; you are able to re-assign that variable to another type, for example $a = 1;, an integer.

PHP is a weakly typed language, and I can illustrate this with a more real-life example:

$id = '1'; // An ID retrieved as a URL parameter.

function find(int $id): Model
{
    // ...
}


find($id);

You might think that in modern PHP, you can avoid these problems with strict types, but that's not completely true. Declaring strict types prevents other types being passed into a function, but you can still change the value of the variable in the function itself.

declare(strict_types=1);

function find(int $id): Model
{
    $id = '' . $id;

    // This is perfectly allowed in PHP: `$id` is a string now.
}

find('1'); // This would trigger a TypeError.

find(1); // This would be fine.

Like I said: PHP's type system is weak. Type hints only ensure a variable's type at that point in time, without a guarantee about any future value that variable might have.

Am I saying that strong types are better than weak ones? No. But there's an interesting property to strong types, they come with a few guarantees. If a variable has a type that's unchangeable, a whole range of unexpected behaviour simply cannot happen anymore.

You see, it's mathematically provable that if a strongly typed program compiles, it's impossible for that program to have a range of bugs which can exist in weakly typed languages. In other words, strong types give the programmer a stronger insurance that the code actually behaves how it's supposed to.

This doesn't mean that a strongly typed language cannot have bugs! You're perfectly able to write a buggy implementation. But when a strongly typed program compiles successfully, you're sure a certain set of bugs and errors can't occur in that program.

If you want to further explore the topic on strong and weak types, I'd recommend starting with this video by Gary Bernhardt. Not only does it go further into detail on types, Gary also discusses an important mindset in the whole types debate.

# When types are checked

We talked about strong and weak types, what about static and dynamic types? – This is where it starts to get truly interesting.

As you're probably aware, PHP is an interpreted language. This means a PHP script is compiled at runtime. When you send a request to a server running PHP, it will take those plain .php files, and parse the text in it to something the processor can execute.

This is one of PHP's strong points by the way: the simplicity on how you can write a script, refresh your webpage and everything is there. That's a big difference compared to a language that has to be compiled before it can be run.

There is a downside though: performance. And it's not hard to pinpoint this down: the more tasks there are to do at runtime, the more impact there is on performance. One of those many tasks the PHP engine has to take care of? Type checking.

Because PHP checks the type of variables at runtime, it is often described as a dynamically typed language. A statically typed language on the other hand, will have all its type checks done before the code is executed.

Hang on – I can hear you say – what does this have to do with what PHP can be?

—We'll get to that.

# What this means for PHP

Now we know what we're talking about, let's take a look at PHP's type system today.

I hope that after the theory, it's clear to you that PHP is a dynamic, weakly typed language. And there's nothing wrong with that!

On the other hand, it's interesting to note that many people are asking for a better type system in PHP. This doesn't mean we understand the implications of such a type system on PHP, yet but many of us feel that natural urge for a better type system. I'm sure that a lot of developers can point to real-life, daily situations where a better type system would actually benefit them.

To give one obvious example: the question for generics. Whether it is to ensure an array only contains one type of elements or to improve ORM abstractions, lots of people are asking for generics in PHP.

The question than becomes: is creating a more complicated type system feasible with PHP's current type paradigm? And the answer is, in part, yes—for sure. There are parts that could be improved in the current, dynamic weak type system.

Type hints for one, added in PHP 7.0 and 7.1 are useful to many PHP developers; Levi Morrison is working on generics in traits; also, there are very active discussions about the type system on the internals mailing list.

However: we're missing a very important point. As long as we're striving to improve PHP's runtime type system, we'll always be dealing with the huge performance cost it will take.

# The benefits of a static type system

This is what Rasmus Lerdorf has to say on the topic.

Now if the RFC was a plan for baking a compile-time static analysis engine into PHP itself, that would be interesting. But that is a massive project.

Rasmus

Imagine the possibilities when you can write PHP code that can be statically type checked before running the code. Tools like PHPStan and Psalm already do a lot of static analysis, but in my opinion it could go a step further. Say we could do this.

class List<T>
{
    private array $list;
    
    // ...
}

What if this was valid PHP code? And what if the runtime engine would just plain ignore it, and a part of PHP engine could do all the type checks, before runtime?

That's –in my opinion– a better solution than standalone tools which rely on docblocks and can't benefit from the core PHP engine, as they are written, in the case of Psalm and PHPStan, in PHP.

Don't get me wrong: tools like these are the first important step towards a bigger goal. I just think we shouldn't stop here.

The need for a better type system is clear. Lots of programmers experience a natural longing for something more than what's possible now. This doesn't only happen in the PHP community, look at modern languages like Rust, or supersets like TypeScript for JavaScript.

So maybe the answer for PHP lies into baked-in features in the core, maybe it lies in a superset that compiles to PHP with extra type checking. That last one by the way, has already been tried: Hack on HHVM.

There even is a third option, a question every programmer should ask themselves from time to time. Should we want PHP to change dramatically to match our needs, or should we change our frame of reference, and maybe look at other languages that might fit those needs better?

There's no shame in using another tool for the job, if that tools fit your needs better. And after all, isn't a programming language just that? A tool.

]]>
2018-04-15T00:00:00+00:00
<![CDATA[ PHPStorm tips for power users ]]> https://www.stitcher.io/blog/phpstorm-tips-for-power-users A selection of less-known-yet-powerful features of PHPStorm.

# Pane modes

Every pane in PHPStorm has several modes and can be configured either by hand or via key bindings.

  • docked: makes a pane not overlap with other panes or the code screen.
  • pinned: automatically hides a pane when not pinned.
  • floating: makes the pane float.
  • windowed: makes the pane a full-blown window.
  • split: to allow multiple panes in one area.

Working with non-pinned panes will allow for a much cleaner editor view. Binding certain panes to a key combination will show them at will.

# Auto-imports

By default, PHPStorm will only auto-import namespaces if you're already in a namespaced file. Auto imports can be configured to also work in normal PHP files in Settings > Editor > General > Auto Import.

# Code templates

You can change almost every template of auto-generated code in Settings > Editor > File and Code Templates For example: generate getters and setters without docblocks, generate test functions in another format and others.

--

# String actions

When pressing alt + enter (Show Intention Actions) on a string, you'll get multiple useful actions. Things like replace quotes to toggle between single- and double quotes, split string to split the string, and more.

# Copy paths

Two very useful commands:

  • Copy Paths to copy the full path to the current file.
  • Copy Reference to copy the relative project path and line number to the current file.

This "current file" can be the file you're editing, but could also be the selected file in the tree view or navigation bar.

# Commands to toggle options

Instead of opening the settings to toggle options, there are a lot of toggles you can manage from the command palette. For example: show or hide the tabs bar.

You can open the command palette with ⌘ ⇧ A on the default Mac keymap. If you want to lookup the keybinding on your system: the command is called Find Action.

# Custom JVM options

PHPStorm runs on Java, and there's a file in which you can specify extra options for the JVM to optimise performance. I've written about those options here.

# Distraction free mode

Distraction free mode will hide all panes by default, but you can easily bring them back via the command palette or key bindings.

Besides this "no clutter by default", your code will also align more centered, which can be a much more pleasant reading experience. The width of this centered code view is configured in Settings > Editor > Code Style > Hard wrap at.

# Color inspection

Do you want to know why a word is highlighted or change the colouring? There's a command called Jump to Colors and Fonts which will allow you to edit the color of your current scheme, for that entry.

# Any more suggestions?

I'd love to hear your own tips on how to use PHPStorm. Feel free to let me know via Twitter or e-mail.

]]>
2018-03-15T00:00:00+00:00
<![CDATA[ Responsive images done right ]]> https://www.stitcher.io/blog/responsive-images-done-right I want to share some thoughts on responsive images. I'll write about a certain mindset which many projects could benefit from: small- and mid-sized web projects that don't need a full blown CDN setup, but would enjoy the performance gain of responsive images.

The idea behind responsive images is simple: try to serve an image with dimensions as close as possible to the image dimensions on screen. This results in smaller bandwidth usage and faster load times!

For example: if you're displaying an image on a mobile device with a screen of 600px wide, there's no need for downloading that image with a width of 1000px.

The responsive images spec handles not only media queries, but pixel density too. The only thing the server has to do is generate multiple variations of the same image, each with a different size.

If you'd like to know more about how things are done behind the scenes, I'll share some links to interesting resources at the end of this post.

# How to render responsive images

There are different ways to render variations of the same image. The simplest approach could be this: given an image, create 4 variations: 1920px, 1200px, 800px and 400px.

While this approach is easy to implement, it's not the most optimal. The goal of responsive images is to serve faster loading images while maintaining the highest possible quality for the user's screen.

There are two variables in this equation: the width of the user's screen (and therefore the width of the image itself) and the file size of an image.

Say you have two images with the exact same dimensions. Depending on the content in that image and the encoding used, their file sizes could differ a lot.

Another approach could be to manually define the most optimal srcset for each image. This is impossible to do for most websites. A website could have lots of images, and it's also difficult to manually calculate the dimensions for that optimal srcset.

Luckily, computers are very good at tedious calculations on a large scale. This approach sounds like a good idea: given an image, generate x-amount of variations of that image, each variation being approximately 10% smaller in file size.

How does that sound? You now have a small margin of possible "overhead" for variable screen sizes, but at least we're sure that margin won't be more than 10%. Depending on the size of the image, for example: a thumbnail vs. a hero image; we could even reduce the margin to 5% instead of 10%. This will result in a different srcset for every image, but that's not our concern: the responsive images spec can handle that for us.

So how can you determine the dimensions of, say 10 variants of the same image, if you only know the dimensions of the original image? This is where high school maths come into play.

We start with these known variables
filesize = 1.000.000
width = 1920
ratio = 9 / 16
height = ratio * width

Next we introduce another one: area
area = width * height
 <=> area = width * width * ratio

We say that the pixelprice is filesize / area
pixelprice = filesize / area

Now we can replace variables until we have the desired result
 <=> filesize = pixelprice * area
 <=> filesize = pixelprice * (width * width * ratio)
 <=> width * width * ratio = filesize / pixelprice
 <=> width ^ 2 = (filesize / pixelprice) / ratio
 <=> width = sqrt((filesize / pixelprice) / ratio)

This proof says that given a constant pixelprice, we can calculate the width a scaled-down image needs to have a specified filesize. Here's the thing though: pixelprice is an approximation of what one pixel in this image costs. Because we'll scale down the image as a whole, this approximation is enough to yield accurate results though. Here's the implementation in PHP:

/*
$fileSize        file size of the source image
$width           width of the source image
$height          height of the source image
$area            the amount of pixels
                 `$width * $height` or `$width * $width * $ration` 
$pixelPrice      the approximate price per pixel:
                 `$fileSize / $area`
*/

$dimensions = [];

$ratio = $height / $width;
$area = $width * $width * $ratio;
$pixelPrice = $fileSize / $area;
$stepModifier = $fileSize * 0.1;

while ($fileSize > 0) {
    $newWidth = floor(
        sqrt(
            ($fileSize / $pixelPrice) / $ratio
        )
    );

    $dimensions[] = new Dimension($newWidth, $newWidth * $ratio);

    $fileSize -= $stepModifier;
}

I want to clarify once more that this approach will be able to calculate the dimensions for each variation with a 10% reduction in file size, without having to scale that image beforehand. That means there's no performance overhead or multiple guesses to know how an image should be scaled.

# In practice

Let's take a look at a picture of a parrot. This image has a fixed srcset:

This one has a dynamic srcset:

Feel free to open up your inspector and play around with it in responsive mode. Be sure to disable browser cache and compare which image is loaded on different screen sizes. Also keep in mind that the pixel density of your screen can have an impact.

Can you imagine doing this by hand? Neither can I! One of the first features I proposed when I started working at Spatie, my current job, was to add this behaviour in the Laravel media library, its usage is as simple as this:

$model
   ->addMedia($yourImageFile)
   ->withResponsiveImages()
   ->toMediaCollection();
<img 
    src="{{ $media->getFullUrl() }}" 
    srcset="{{ $media->getSrcset() }}" 
    sizes="[your own logic]"
/>

To finish off, here are the links which I mentioned at the start of this post.

Special thanks to my colleague Sebastian for reviewing and editing this post.

]]>
2018-03-07T00:00:00+00:00
<![CDATA[ Showing full MySQL foreign key errors ]]> https://www.stitcher.io/blog/mysql-show-foreign-key-errors In case of a foreign key error when creating or altering a table, MySQL doesn't show the full message.

You can read the full message by executing the following query and inspecting the Status column.

show engine innodb status;
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2018-02-13 11:12:26 0x70000b776000 Error in foreign key constraint of table table/#sql-7fa_247a:
 foreign key (`my_foreign_key`) references `table` (`id`)
   on delete cascade:
Cannot resolve table name close to:
 (`id`)
   on delete cascade
]]>
2018-02-13T00:00:00+00:00
<![CDATA[ MySQL query logging ]]> https://www.stitcher.io/blog/mysql-query-logging # Enable query logging
mysql -p -u root

> SET GLOBAL general_log = 'ON';

# Turning it off again when finished

> SET GLOBAL general_log = 'OFF';

# Find the log file

First, find the mysqld process ID.

ps auxww | grep mysql

brent             2042   0.0  0.4  2849776  67772   ??  S    Fri11AM   0:16.80 /usr/local/opt/mysql/bin/mysqld

Second, use lsof to find all files used by this process, and filter on log.

# sudo lsof -p <PID> | grep log

sudo lsof -p 2042 | grep log

mysqld  2042 brent    4u     REG                1,4  50331648  780601 /usr/local/var/mysql/ib_logfile0
mysqld  2042 brent    9u     REG                1,4  50331648  780602 /usr/local/var/mysql/ib_logfile1
mysqld  2042 brent   26u     REG                1,4        35  780672 /usr/local/var/mysql/mysql/general_log.CSM
mysqld  2042 brent   32r     REG                1,4         0  780673 /usr/local/var/mysql/mysql/general_log.CSV
mysqld  2042 brent   33w     REG                1,4     25504 9719379 /usr/local/var/mysql/HOST.log

/usr/local/var/mysql/HOST.log is the one you want, HOST will be the name of your host.

tail -f /usr/local/var/mysql/HOST.log
]]>
2018-01-20T00:00:00+00:00
<![CDATA[ Where a curly bracket belongs ]]> https://www.stitcher.io/blog/where-a-curly-bracket-belongs This blogpost is based on this amazing talk by Kevlin Henney.

Dedicating a whole blogpost to curly brackets might seem like overkill but I believe it's worth thinking about them. Not just because of one curly bracket, but because there's a bigger message in all this. Thinking about how we read and write code not only improves the quality of that code, it also increases our own and others ease of mind when working with it. It can improve the fluency of your work and free your mind to think about real important stuff. You know, things like "application logic" for example.

I wrote about visual code improvements a while back in a previous blogpost about cognitive load. Today I want to focus on that one little, yet very important character in our codebase: the curly bracket. More specifically, we're only going to look at the opening curly bracket, because there's little to no discussion about the closing one.

Let's take a look at a code sample.

public function __construct(string $publicDirectory, string $configurationFile, PageParser $pageParser, PageRenderer $pageRenderer) {
    // ...
}

A constructor for a render task in Stitcher. It takes two config arguments and two objects. Depending on the width of your screen, this piece of code might be fully visible in your IDE. On this website it surely will not.

So what's wrong with this code? Well first of all, you probably have to scroll to read it. That's a bad thing. Scrolling requires an extra action for the developer to take. You'll have to consciously search for information about the arguments of this method. That time distracts you from focusing on the application code.

Second, if you're a web developer, you probably know people don't read, they rather scan. This is especially true for websites, where the biggest area of attention leans towards the left. And the same goes for reading code. Putting important information to the right makes it more difficult to find, and it also doesn't convey the same importance as things to the left.

In case of an argument list, all arguments are equally important; yet in the above example a lot of useful information is pushed to that right, dark side.

So how do we pull the useful information more to the left?

public function __construct(string $publicDirectory, 
                            string $configurationFile, 
                            PageParser $pageParser, 
                            PageRenderer $pageRenderer) {
    // ...
}

This could be the first thing you think about. But it doesn't really scale. As soon as you're refactoring a method name, the alignment breaks. Say we want to make this a static constructor instead of a normal one.

public static function create(string $publicDirectory, 
                            string $configurationFile, 
                            PageParser $pageParser, 
                            PageRenderer $pageRenderer) {

See the alignment breaking? Another issue with this approach is that things are still pushed rather far to the right; let's take a look at another approach.

public function __construct(
    string $publicDirectory, string $configurationFile, 
    PageParser $pageParser, PageRenderer $pageRenderer) {
    // ...
}

The advantage here is that the alignment issue on refactoring is solved. However, how will you decide how many arguments should go on one line? Will you make some styling guidelines about this? How will you enforce them? This example has four arguments, but what if it had three or five?

Consistency is key. If there is a consistent rule about this, you won't have to think about it anymore. And like we said before, if you don't have to think about this, there's room in your head for more important things.

So let's continue searching for that consistency.

public function __construct(
    string $publicDirectory, 
    string $configurationFile, 
    PageParser $pageParser, 
    PageRenderer $pageRenderer) {
    $this->publicDirectory = rtrim($publicDirectory, '/');
    $this->configurationFile = $configurationFile;
    $this->pageParser = $pageParser;
    $this->pageRenderer = $pageRenderer;
}

By giving each argument its own line, we solve the above mentioned problems. But there's still one issue with this example: it's hard to distinguish between the argument list and the method body.

Kevlin Henney visualises this problem in a simple, yet clever way. Let's replace all characters in this code with X's:

XXXXXX XXXXXXXX __XXXXXXXXX(
    XXXXXX XXXXXXXXXXXXXXXX, 
    XXXXXX XXXXXXXXXXXXXXXXXX, 
    XXXXXXXXXX XXXXXXXXXXX, 
    XXXXXXXXXXXX XXXXXXXXXXXXX) {
    XXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXXXXXXXXXXXXXX;
    XXXXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXXXX;
    XXXXXXXXXXXXXXXXX = XXXXXXXXXXX;
    XXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXX;
}

Can you see how difficult it has become to spot where the argument list ends and the method body starts?

You might say "there's still the curly bracket on the right indicating the end". That's the thing we want to avoid! We want to keep the visual important information to the left. How do we solve it? Kevlin Henney phrased it very well:

Turns out, there is one true place where to put your curly brackets - Kevlin Henney

XXXXXX XXXXXXXX __XXXXXXXXX(
    XXXXXX XXXXXXXXXXXXXXXX, 
    XXXXXX XXXXXXXXXXXXXXXXXX, 
    XXXXXXXXXX XXXXXXXXXXX, 
    XXXXXXXXXXXX XXXXXXXXXXXXX
) {
    XXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXXXXXXXXXXXXXX;
    XXXXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXXXX;
    XXXXXXXXXXXXXXXXX = XXXXXXXXXXX;
    XXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXX;
}

That is why it makes sense to put that curly bracket on a new line. Here's the final result:

public function __construct(
    string $publicDirectory, 
    string $configurationFile, 
    PageParser $pageParser, 
    PageRenderer $pageRenderer
) {
    $this->publicDirectory = rtrim($publicDirectory, '/');
    $this->configurationFile = $configurationFile;
    $this->pageParser = $pageParser;
    $this->pageRenderer = $pageRenderer;
}

Now, you might not like this way of structuring your code. You might think it adds unnecessary length to a file. But take a look at the facts:

  • You're keeping the important information to the left of the screen, where most of your focus is.
  • This method is consistent, which allows us not having to think about it when reading it. This frees up some of your human "memory space": it reduces cognitive load; it allows you to focus on the important stuff: the real application logic.
  • No one ever died because a file was "longer than absolutely needed". People do however get very frustrated working in legacy code bases, having to read what other people wrote, especially when that code is difficult to read.
  • If the length of the file is really a bother for you, code folding can solve that issue.

I like having this rule when coding. There's never a discussion in my head about "should I do it this way or that way"? This consistency helps me write and read my own code, and benefits other developers too, maybe even years later.

# What about small functions?

Say your function only has one parameter, should it be split on multiple lines? I personally don't think so. And if we're strictly applying the rules above, the curly bracket may be put on the same line.

However, now that we're used to that one almost-empty line between the argument list and the method body, it does seem like a nice idea to use this visual divider also for smaller functions.

XXXXXX XXXXXXXX __XXXXXXXXX(XXXXXX XXXXXXXXXXXXXXXX) 
{
    XXXXXXXXXXXXXXXXXXXXXX = XXXXXXXXXXXXXXXX;
}

Now we could start arguing about the placement of that closing bracket, but that's a blogpost for another time.

# And control structures?

The question about if, for, while and others should of course be addressed too. In my opinion, the answer is simple, we can apply the same rules to them.

If the operands are pushed too far to the right, and we feel the need to split it, we do it like this:

if (
    $firstCondition === $secondCondition
    || $thirdOperand === 1
    || $fourthOperand
) {
    // ...
}

Finally, here is a daring thought - and I don't do this myself by the way, because following standards is also a good thing - it might make sense to apply the same rule to short control structures. After all: consistency, right?

foreach ($things as $thing)
{
    // ...
}

If you're not convinced by now, I'd love to hear why! You can reach out to me on Twitter or via e-mail. I'm looking forward to discussing this further with you!

If you're looking for more to read on clean code. Feel free to browse this blog a little further. This is the best starting point.

]]>
2018-01-16T00:00:00+00:00
<![CDATA[ MySQL import: JSON with binary character set ]]> https://www.stitcher.io/blog/mysql-import-json-binary-character-set If you see this error when importing MySQL files:

cannot create a JSON value from a string with CHARACTER SET 'binary'

You should find and replace parts of the import file with the following regex:

Find: (X'[^,\)]*'), and replace by: CONVERT($1 using utf8mb4)

Source: StackOverflow.

]]>
2018-01-09T00:00:00+00:00
<![CDATA[ Asynchronous PHP ]]> https://www.stitcher.io/blog/asynchronous-php We're working on a new package at Spatie. It's called spatie/async and meant to do asynchronous parallel processing in PHP.

Parallel processing in PHP might seem like an edge case for many web developers, but let's take a look at a few use-cases we see at Spatie:

We wanted to create an easy-to-use package, yet one that could solve our use cases. Some of the examples listed above will not use the new spatie/async package, because there's also a queueing system provided with Laravel.

This is how asynchronous code with our package looks like.

use Spatie\Async\Process;

$pool = Pool::create();

foreach (range(1, 5) as $i) {
    $pool[] = async(function () use ($i) {
        // Something to execute in a child process.
    })->then(function (int $output) {
        // Handle output returned from the child process.
    })->catch(function (Exception $exception) {
        // Handle exceptions thrown in the child process.
    });
}

await($pool);

# Outperforming Amp? Not quite yet.

If you're into parallel PHP, you've probably heard of Amp and ReactPHP. Our package aims not to compete with those two, as it only solves one tiny aspect of parallelism in PHP; and tries to solve it in a different way.

We did however run some benchmarks to compare our package performance against Amp. Special thanks to Niklas Keller, one of the developers of Amp. He pointed out some mistakes in our previous benchmarks, and helped making them more fair.

The new benchmarks compare a few scenarios. The first two groups plot the execution time of an empty process, while the third and fourth groups show the execution time of processes with a different time to finish, using several sleep intervals. Between the two groups, we're also comparing a capped concurrency configuration and a non-capped configuration. Capped means that there are more processes than the pool can execute at once.

The benchmark code can be found here.

Comparing Amp and spatie/asyncComparing Amp and spatie/async

I tried to draw a few conclusions from these test.

  • Real life processes take time to run and finish. For our use-cases, the "with logic" benchmarks are more relevant.
  • Regarding process execution time, it seems like our package has less overhead: as long as the pool doesn't have to manage concurrency, we're finishing faster.
  • In real life applications though, the maximum concurrency setting will most likely be in effect, so it's clear that we'll need to improve that part of our codebase if we want better performance compared to Amp.

# What about ReactPHP?

We've excluded ReactPHP from the benchmarks, because it's not a fair comparison. ReactPHP doesn't allow to run closures or Tasks as sub-processes the way Amp and our package do. With ReactPHP, you're working with plain processes, so there's no way to compare to it.

# About process signals

The biggest difference between our package and Amp is the way of communicating between processes. We're solely relying on process signals to determine when a process is finished. It allows for less overhead, but also excludes Windows as a target platform.

Processes in UNIX systems can send signals to each other. Depending on what kind of signal is received, a process will act different. Signals are handled by the kernel, so they are pretty low level. Before PHP 7.1 though, you had to declare(ticks=1) to use asynchronous signals in a reliable way. This means that PHP will check for signals much more often, but it also introduces a lot of overhead:

A tick is an event that occurs for every N low-level tickable statements executed by the parser within the declare block. The value for N is specified using ticks=N within the declare block's directive section.

With PHP 7.1, there's a new way of handling interrupts sent by the kernel.

Zend Engine in PHP 7.1 was extended with ability of safe time-out and interrupt handling. Actually, PHP VM checks for EG(vm_interrupt) flag on each loop iteration, user function entry or internal function exit, and call callback function if necessary.

By using pcntl_async_signals(true), PHP will now check for signals in a much more performant way. A more in-depth explanation can be found in the rfc, submitted by Dmitry Stogov.

It's thanks to this mechanism that we're able to act on process status changes in a real asynchronous way, without having to rely on sockets or process status polling.

]]>
2017-12-24T00:00:00+00:00
<![CDATA[ Optimised UUIDs in mysql ]]> https://www.stitcher.io/blog/optimised-uuids-in-mysql At Spatie, we're working on a large project which uses UUIDs in many database tables. These tables vary in size from a few thousand records to half a million.

As you might know, normal UUIDs are stored as CHAR(36) fields in the database. This has an enormous performance cost, because MySQL is unable to properly index these records. Take a look at the following graph, plotting the execution time of hundred queries against two datasets: one with 50k rows, one with 500k rows.

That's an average of more than 1.5 seconds when using textual UUIDs!

There's an important edit here: the benchmark above was performed on un-indexed fields. I've since changed the benchmark results to work with indexed textual fields for a more fair comparison. There's still a performance gain to not using textual UUIDs, so keep reading!

Looking around for better alternatives, we found a two-part solution.

# Saving UUIDs as binary data

Instead of saving UUIDs as CHAR, it's possible to store their actual binary data in a BINARY field. Storing them in this format, MySQL has a lot less trouble indexing this table. This is the graph plotting a much faster result.

That's an average of 0.00008832061291 seconds per query, in comparison to ~~1.5~~ 0.0001493031979 seconds for the indexed textual UUID.

# It becomes even better!

The binary encoding of UUIDs solved most of the issue. There's one extra step to take though, which allows MySQL to even better index this field for large datasets.

By switching some of the bits in the UUID, more specifically time related data, we're able to save them in a more ordered way. And it seems that MySQL is especially fond of ordered data when creating indices. There's one important thing to note: this time related bits are only available in UUID version 1.

Using this approach, we can see following result.

The optimised approach is actually slower for lookups in a small table, but it outperforms the normal binary approach on larger datasets. It even performs better than an AUTO_INCREMENT integer ID! But as you can see, we need very large tables before the optimised UUID has a benefit.

I would recommend only using UUIDs when there's a very good use case for them. For example: when you want unique IDs over all tables, and not just one; or if you want to hide exactly how many rows there are in the table.

The MySQL team wrote a blogpost explaining this bit-shifting of UUIDs in further detail. If you'd like to know how it works internally, over there is a good start.

If you're building a Laravel application and would like to use optimised UUIDs in your project, we've made a package especially for you. You'll also find more benchmark details in the README over there.

Finally, if you're looking into implementing this behaviour in a non-Laravel project, you should definitely take a look at Ramsey's UUID package, we're using it too!

]]>
2017-11-29T00:00:00+00:00
<![CDATA[ Improving PHPStorm's performance ]]> https://www.stitcher.io/blog/phpstorm-performance Let's just dive right in.

# Java VM options

PHPStorm is made in Java. If you ever played Minecraft, you know you could allocate extra RAM by adding flags to your startup command. You can also do this in PHPStorm, it's even built into the UI.

Go to help > Edit Custom VM Options. You can play around with the settings here. I for one changed the maximum amount of RAM allocated to PHPStorm, and added two graphics options (at the end of the file).

-Xms500m
-Xmx1500m

-Dawt.useSystemAAFontSettings=lcd
-Dawt.java2d.opengl=true

# Only for people on Mac, it makes Java use an optimised graphics engine.
-Dapple.awt.graphics.UseQuartz=true

# Custom properties

PHPStorm also has a file to set custom properties: help > Edit Custom Properties. Adding one option here changed the way PHPStorm renders text: it will show text immediately, instead of analysing it first. The downside is that you can sometimes see a flash of unstyled text. It feels much smoother though.

editor.zero.latency.typing=true

# Inspections and plugins

PHPStorm is a powerful IDE, with lots of functionality built in by default. While I'd highly recommend using these options to their full extent, there are some things that are never used.

Disabling unused plugins can be a start, but disabling inspections has a much bigger impact. Take a look at the list and decide for yourself which ones you don't need: Settings > Editor > Inspections.

# Language injection

One plugin in particular has a big performance impact: IntelliLang. This plugins allows for languages to be recognised in different file formats. Eg. HTML autocompletion and highlighting in a PHP file.

I would not recommend completely disabling this plugin, but there might be some injections which you don't need in your projects: Settings > Editor > Language Injections.

# Project setup

Managing which files PHPStorm must index has to be done on a project level basis. It is worth spending 5 minutes on initial project setup, for projects you'll work hours and days on.

# Excluding directories

Go to Settings > Directories to mark directories as excluded. PHPStorm won't index these files. Directories to exclude would be eg. cache, public and storage directories; directories which contain generated files from asset building, and last but not least: vendor and node_modules.

# The vendor problem

Excluding directories from indexing means no auto-complete from those directories. So excluding the vendor directory might not be the best idea. There's a neat little trick though, which allows you to whitelist vendor directories you want to use,.

Go to Settings > Languages & Frameworks > PHP. In here you can set include paths. By manually specifying which vendor directories should be indexed, you can eliminate a lot of indexing time. You might eg. always keep dependencies of vendors excluded, because chances are you won't be using those APIs. If you come across a vendor you need auto-completion for, just add it to the list.

# Node modules

Node modules are "excluded" by default, but they are added as include paths nevertheless. Because of the size of the node_modules directory, it can take quite a while to index it.

JavaScript include paths are managed like PHP includes, but in Settings > Languages & Frameworks > JavaScript > Libraries. I personally don't write a lot of JavaScript, so I just remove the inclusion of node_modules completely.

Managing directories requires a bit of time for each project, but it's worth the performance gain in the long run.

# Font rendering on OSX

There's a confirmed issue in the JRE with certain fonts. While this might seem like a minor detail, certain fonts actually require a lot of processor power to render text, slowing down PHPStorm in its whole.

I've written a separate blog post on this issue, and how you can fix it. You can read it here.

# On a personal note

I didn't start this post by writing my own thoughts, because I figured people were looking for some quick tips to speed of their IDE. As a PHP developer, I think that PHPStorm is such a powerful tool, which helps me to write good and maintainable code. I don't want it to stand in my way though, so good performance is an absolute requirement.

With the things listed above, I feel that PHPStorm offers the best balance between performance and intelligence. I've written PHP in Sublime Text for ± 5 years. I did put some time into tweaking PHPStorm to my needs, and now I'm 100% sure I'll never go back to Sublime Text. My IDE is just way too smart and helpful to me. It allows me to focus on real application logic, instead of writing the same boilerplate code over and over again. I'll talk more about the benefits of an IDE over a text editor in another post. For now, I hope that you found these tips helpful.

Happy coding!


Ready for more? I've got a new blog post full of tips for PHPStorm users!

]]>
2017-10-22T00:00:00+00:00
<![CDATA[ Stitcher beta 2 ]]> https://www.stitcher.io/blog/stitcher-beta-2 The second beta release of Stitcher ties a lot of loose ends together, getting ready for real production sites.

# Installation

composer require pageon/stitcher-core @beta

# Changelog

Note the a few config parameters are changed. These changes might fall under the category "breaking", but were really needed in order to get a more consistent API, before a real 1.0.0 release comes along.

  • Add Parsedown extension to support classes on
     tags in fenced code blocks.
  • Disable directory listing via .htaccess.
  • Add redirect.www and redirect.https options. Allowing to automatically redirect non-www to www, and http to https.
  • Add redirect option in site config files to make a route redirect to another page.
  • Use pageon/html-meta ^2.0 from now on. Lots of tweaks to social meta tags were added.
  • Add async option which, when ext-pcntl is installed, will enable asynchronous page rendering.
  • Add Parsedown extension to support target="_blank" links by prefixing the URL with *.
  • Add sitemap.xml support. When setting the sitemap.url variable, a sitemap.xml will be generated.
  • Fix bug with Collection Adapters not copying meta tags from the base page for its sub-pages.
  • Add responsive images support to markdown parser.
  • The following config parameters are changed (#2):
    • caches.cdn becomes cache.cdn.
    • caches.image becomes cache.images.
    • directories.htaccess is removed.
    • minify becomes engines.minifier
  • Support multiple extensions per template engine (#7).
  • Support nested conditions in the filter adapter (#1).
  • Remove unused eninges.async option.
]]>
2017-08-27T00:00:00+00:00
<![CDATA[ Mastering key bindings ]]> https://www.stitcher.io/blog/mastering-key-bindings This blog post aims to make you think about the way you use key bindings whilst programming. You'll read about some techniques I use to assign key bindings, how to memorise them, and use them efficiently. But before we go on, I'll need to explain why spending time on key bindings is worth the effort.

# The need for keys

I can't point to some psychological study to back this claim, just my own experience and common sense. Using the mouse as less as possible when coding is a good thing. You're not moving your hands around to grab the mouse, which saves time. Also you don't have to make the mental switch between using a keyboard and a mouse as input device.

I believe these small things have the power to improve our skills as professional programmers significantly. I've experienced a lot of gain by taking the time to learn to use the keyboard as often as I can. While I'm still searching the optimal setup, I can already share some thoughts and techniques. The most important thing to know is that key bindings are a matter of personal taste. So don't take these next points as law, but rather apply them to your own situation.

Key bindings are a personal preference.

# The meaning of modifiers

A keyboard has a few modifier keys, which allow you to modify the behaviour of other key presses. A long time ago these keys were actually hard wired in the keyboard, to change the electronic bits sent to the computer. In this modern time, it's still good to look at what their original meaning was. It helped me define a formal definition for each modifier key, allowing me to remember what key combination belongs to which action.

Define a personal meaning for each modifier key, and stick to it.

# Meta/Command (⌘)

I use this key when "executing" commands. Basically most of what's possible through the menu of an application.

# Option/Alt (⌥)

Alt stands for "alternate", changing the behaviour of another key combination. I use this key for a related action of another key binding.

# Shift (⇧)

Shift has a double meaning. First it's used for selections, because that's default OS behaviour. Second, it's also often used to reverse the action.

I prefer a maximum of two modifier keys, and if complexer combinations are needed, opt for double key bindings. One exception though: Shift (⇧) may be used in combination with other modifier keys to reverse the action.

Prefer at most two modifier keys, or use double key bindings.

# Control/Ctrl (^)

I use the control key for text- and code related manipulations. Actions like moving the cursor, working with selections, working with lines, etc. I find it hard to give a formal definition for the Control key, but its use is clear in most cases.

A note for Windows users: the Control key is used much more in comparison to the Meta (Windows) key. Meaning you probably want to switch the definition of the two, or even ditch the Meta key. Even though this might seem like a good idea, adding the meta key in your workflow can be a good thing, as it adds another modifier key to your availability.

# Function (fn)

Because the function key is often not accessible on desktop keyboards, I choose not to depend on this key. I only make an exception for some edge cases like page-up or page-down.

# Learning

Keeping my own definitions in mind, it's easy to start defining key bindings. Though to remember them requires practice. I'd recommend not assigning all key bindings at once, but rather slowly add them when you need them.

Assign new key bindings when you need them.

I choose not to override operating system (OS) key bindings. Things like copy, paste, select all or quit are never overridden. Key binding defaults provided by your IDE or editor, however, may be changed. If you come from Sublime Text like me, you've probably learned some defaults which you are accustomed with. When switching to PHPStorm a few years ago, I decided to keep some of those key bindings I knew from Sublime.

There's no need to change OS-level key bindings like copy or select all.

Even now, I'm still changing key bindings from time to time. Especially when I came up with my definition list. One thing I find useful when learning new key bindings, is to disable the old ones. IDEs like PHPStorm allow you to add multiple combinations for the same action. I prefer to immediately notice when I'm using an old combination. This makes me learn faster.

Remove key bindings you wish to unlearn.

Furthermore, when stuck in a situation, I try not to immediately grab the mouse. I try to think the problem and define what I want to do. Most of the time, I can remember which combination of keys should be pressed, because of the definition list above. When my memory fails me, I'm lucky to be working in an IDE with awesome key binding management, so it's easy to find the correct combination back.

Don't grab the mouse when panicking.

Your keymap is a very personal file, which slowly grows to match your workflow the best. I recommend you storing a backup of your keymap somewhere else, GitHub would be a good place. Here's mine.

Check your keymap into version control.

# A few of my own examples

  • ⌘ p Search file
  • ⌘ ⇧ p Search recent files
  • ⌘ ⌥ p Search symbols in file
  • ⌘ ⌥ space Show suggestions
  • ⌘ ⌥ enter Go to declaration
  • ^ ⌥ → Move right with camelHops
  • ^ ⌥ ← Move left with camelHops
  • ⌥ ↑ Move cursor paragraph up
  • ⌥ ↓ Move cursor paragraph down
  • ⇧ ⌥ ↑ Extend selection
  • ⇧ ⌥ ↓ Shrink selection

# Closing thoughts

I grew in love with key bindings over the years. I still use the mouse for basic navigation, but once I start coding, I try to use it as little as possible. I find that it's easier to work this way. Not only do I gain time by not switching as often to the mouse; I also find it puts less cognitive load on my brain, meaning I'm able to concentrate more on coding.

This might seem like a small thing to do, but as a professional programmer, you're doing those small things many, many times a day. It's worth taking the time to optimise these areas and skills, I find they make me a better programmer.

Do you want to read more about cognitive load? I've written about fonts and visuals in a previous blog post. Do you still have a question or something on your mind? Send me an email!

]]>
2017-08-25T00:00:00+00:00
<![CDATA[ Responsive images as CSS background ]]> https://www.stitcher.io/blog/responsive-images-as-css-background Integrating the Responsive Images spec together with CSS backgrounds, allowing for more flexibility for eg. hero images because you can use background-size: cover; etc., and still have the full benefits of responsive image loading.

<html>
<head>
    <style>
        img {
            width:100%;
        }
        img.loaded {
            display: none;
        }
        .responsive-image {
            width:100%;
            height:500px;
            background-size: cover;
            background-position: center;
        }
    </style>
</head>
<body>
    <div class="responsive-image">
        <img src="./small.jpg" srcset="./large.png 3000w, ./medium.jpg 1920w, ./small.jpg 425w" >
    </div>
    
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const images = document.querySelectorAll('.responsive-image');
            
            [].forEach.call(images, function (imageContainer) {
                const image = imageContainer.querySelector('img');
                
                image.addEventListener('load', function () {
                    if (!image.currentSrc) {
                        return;
                    }
                    
                    imageContainer.style['background-image'] = "url('" + image.currentSrc + "')";
                    image.classList.add('loaded');
                });
            })
        });
    </script>
</body>
</html>
]]>
2017-06-24T00:00:00+00:00
<![CDATA[ Object oriented generators ]]> https://www.stitcher.io/blog/object-oriented-generators The following code shows an object oriented way of implementing a well known generator function: to read lines from a large file.

class FileReader implements \Iterator 
{
    private $handle;
    private $current;

    public static function read(string $fileName) : FileReader {
        return new self($fileName);
    }

    public function __construct(string $fileName) {
        $this->handle = fopen($fileName, 'r');
        $this->next();
    }

    public function __destruct() {
        fclose($this->handle);
    }

    public function current() {
        return $this->current;
    }

    public function next() {
        $this->current = fgets($this->handle);
    }

    public function key() {
        return ftell($this->handle);
    }

    public function valid() {
        return !feof($this->handle);
    }

    public function rewind() {
        rewind($this->handle);
    }
}

Using the file reader.

$lines = FileReader::read('path_to_large_file.txt');

foreach ($lines as $line) {
    echo $line;
}

A comparison to using generators and the yield keyword, based on the tests I ran:

  • This approach takes the same amount of time to execute.
  • It has the same memory footprint as a generator function.
  • It has the benefit of easier re-usability (in my opinion).

In comparison to file_get_contents: reading the same file required of 15MB of memory, whilst this solution required only 2MB, because it only reads one line in memory at a time.

To round up, this is the generator solution using yield.

function read($fileName) {
    $handle = fopen($fileName, 'r');

    while (!feof($handle)) {
        yield fgets($handle);
    }

    fclose($handle);
}

$lines = read('path_to_large_file');

foreach ($lines as $line) {
    echo $line;
}
]]>
2017-06-17T00:00:00+00:00
<![CDATA[ Process forks ]]> https://www.stitcher.io/blog/process-forks function async(Process $process) : Process { socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets); [$parentSocket, $childSocket] = $sockets; if (($pid = pcntl_fork()) == 0) { socket_close($childSocket); socket_write($parentSocket, serialize($process->execute())); socket_close($parentSocket); exit; } socket_close($parentSocket); return $process ->setStartTime(time()) ->setPid($pid) ->setSocket($childSocket); } function wait(array $processes) : array { $output = []; while (count($processes)) { foreach ($processes as $key => $process) { $processStatus = pcntl_waitpid($process->getPid(), $status, WNOHANG | WUNTRACED); if ($processStatus == $process->getPid()) { $output[] = unserialize(socket_read($process->getSocket(), 4096)); socket_close($process->getSocket()); $process->triggerSuccess(); unset($processes[$key]); } else if ($processStatus == 0) { if ($process->getStartTime() + $process->getMaxRunTime() < time() || pcntl_wifstopped($status)) { if (!posix_kill($process->getPid(), SIGKILL)) { throw new \Exception("Failed to kill {$process->getPid()}: " . posix_strerror(posix_get_last_error())); } unset($processes[$key]); } } else { throw new \Exception("Could not reliably manage process {$process->getPid()}"); } } if (!count($processes)) { break; } usleep(100000); } return $output; }

The Process class, used to pass data in a defined way.

abstract class Process
{
    protected $pid;
    protected $name;
    protected $socket;
    protected $successCallback;
    protected $startTime;
    protected $maxRunTime = 300;
    
    public abstract function execute();

    public function onSuccess(callable $callback) : Process {
        $this->successCallback = $callback;

        return $this;
    }

    public function triggerSuccess() {
        if (!$this->successCallback) {
            return null;
        }

        return call_user_func_array($this->successCallback, [$this]);
    }

    public function setPid($pid) : Process {
        $this->pid = $pid;

        return $this;
    }

    public function getPid() {
        return $this->pid;
    }

    public function setSocket($socket) : Process {
        $this->socket = $socket;

        return $this;
    }

    public function getSocket() {
        return $this->socket;
    }

    public function setName(string $name) : Process {
        $this->name = $name;

        return $this;
    }

    public function getName() : string {
        return $this->name;
    }

    public function setStartTime($startTime) {
        $this->startTime = $startTime;

        return $this;
    }

    public function getStartTime() {
        return $this->startTime;
    }

    public function setMaxRunTime(int $maxRunTime) : Process {
        $this->maxRunTime = $maxRunTime;

        return $this;
    }

    public function getMaxRunTime() : int {
        return $this->maxRunTime;
    }
}

A concrete Process implementation.

class MyProcess extends Process
{
    public function execute() {
        sleep(1);
        
        return true;
    }
}

And bringing it all together.

$processA = async(new MyProcess());
$processB = async(new MyProcess());

$output = wait([$processA, $processB]);

print_r($output);
die('Done!');
]]>
2017-06-09T00:00:00+00:00
<![CDATA[ Performance 101: building the better web ]]> https://www.stitcher.io/blog/performance-101-building-the-better-web Today we're looking into web performance. I'll share some useful links to articles and tutorials written by people with a lot of professional experience on the topic. I am writing from the perspective of a developer who brought pieces of this knowledge into practice. I've learned some lessons along the way, which you can learn from too.

If you want to reach out, to talk about performance, or with additions to this post, you can always reach me via email.

Without further ado, let's dive into the mystical subject of web performance. We'll start discussing the mindset you should have when building performant websites. Then we'll move on to a lot of practical examples, and links to other learning resources.

# Performance mindset

If there's one thing you should take away from this post, it's the mindset every web developer should have. The industry builds tools, frameworks and systems to make the life of developers easier. All the while forgetting what web development actually is about. We're not making artisanal pieces of art anymore (maybe we never were?). We're generally aiming for fast development and quick results. We're forgetting about what matters in the end: the website and its visitors.

This post is meant for people with that mindset; people who want to become the best developer they can be. Always pushing yourself to the next level for a better end result. If you're a web developer who relates to this, understanding performance is one of the most important pillars to build upon.

That's it for the philosophical part of this post. Of course I'm completely ignoring the business side of the IT world. I'm not talking about money, time or scope here. I'm talking about improving your own development skills so that you could use that knowledge and experience in spare time projects or for real clients and work.

# Web basics: HTML

One of the key components to understand and improve web performance is to know how the browser renders HTML. There's a lot more to it than you might think, and understanding these steps makes you reason completely differently about your own code. Google has the best crash course on the topic: https://developers.google.com/web/fundamentals/performance/, especially the "critical rendering path" section opened my eyes.

Another important concept to understand is static HTML pages. In the end, they are what's served to the user. There's no need to generate pages on the fly, while the user is waiting to see the result. Dynamic websites abuse the user's time for the sake of easy development. Now I'm not saying dynamic websites are bad. What I do say is that every dynamic system should have the technology in place to exclude the dynamic phase from the request/response cycle. More on that topic later. If you're into real static websites, https://staticgen.com is a good place to find the right tool for your needs.

Moving on to responsive images: possibly the number one optimisation when it comes to bandwidth usage. The responsive images spec is designed to address the issue of large images, or render blocking JavaScript workarounds. It's completely backwards compatible (I'm talking to you Edge), and has a good chance of improving your website's loading time: https://responsiveimages.org.

# Backend development

I've already mentioned dynamic websites in the previous section. They are of course a must in the modern web; but you should think about which pages need to render things on the fly, and which could be cacheable. There are many layers of caching possible on the server side. We'll discuss eg. Varnish cache later in this post. Caching your backend code will highly depend on the kind of language and framework you're using. The most important thing to mention about caching is that you shouldn't view your cache as a layer "on top" of your application. It should be an integral part of all the code you write.

As a PHP developer, I'm used to the strict request/response lifecycle every PHP web application goes through. There are also a lot of other languages which provide the same logic for web applications. This approach is very easy to reason about, but it means the application has to be bootstrapped from scratch for every single request. Libraries like ReactPHP or AMP address this issue by enabling the developer to handle multiple requests from a single bootstrapped application. Asynchronous and parallel applications add a lot of complexity at first, and might be very difficult to wrap your head around. But it might very well mean a huge decrease in response time.

# Server side

Returning to the topic of caching, there's a lot that can be done server side. First of all there are caching headers which you should definitely implement: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control.

Second, you should serve content that's ready to be served. Use a CDN and Varnish in front of your real server. This way you're able to serve images, content pages, etc. immediately, having been already generated before. One of the dangers of using a so called "proxy" like Varnish is that many developers might see it as that "layer on top of your own application". In reality, you'll need to communicate a lot with Varnish from within your own application. You can read more about Varnish here: https://varnish-cache.org.

The benefit of your own server? It's your server. You have control over the resources used and available. Don't put extra load on the client, when you could let your server take care of it. This is of course a very simplified way of thinking about resources. But it's always possible to upgrade your server's hardware, when you have no control over the hardware clients are using.

And lastely, if you haven't implemented HTTP/2 yet: implement HTTP/2! Not sure why? This might give you an idea: https://sitepoint.com/what-is-http2.

# Frontend development

Disclaimer: I'm a backend web developer. I have written, and still write lots of CSS and JavaScript code, but I'm not in any way a professional when it comes to frontend web development. So I'll only use common sense and reasoning to share a few concepts of performance improvement.

You should think what resources a page really needs. If that particular page only needs 5 kilobytes out of 100 kilobytes of CSS, then don't load the other 95 kilobytes! The same goes for JavaScript.

Also think about inlining the important resources in your HTML pages, at least while HTTP/2 server push hasn't gone mainstream yet.

A good place to go from here would be Tim Kadlec's blog: https://timkadlec.com.

# In summary

  • Think performance-first.
  • Understand how HTML is loaded and rendered.
  • Serve content that's ready to be served.
  • Don't abuse the user's time by dynamically rendering on the fly when it's not needed.
  • Improve the request/response cycle server-side.
  • Put the load on your server, not the client.
  • Don't view caching as a layer on top, but rather as an integrated part of your application.
  • Set browser caching headers, use CDNs and take a look at Varnish.
  • Don't load all minified CSS or JS when you only need 10% of it on that page.

Lot's of things to think about. This is my personal checklist I try to keep in mind when developing websites, both professionally and in my spare time. Like I said at the beginning of this post, you shouldn't always do everything just because. But you should understand these concepts, and know when it's appropriate to use them. By doing so, you're contributing to the better web.

]]>
2017-05-30T00:00:00+00:00
<![CDATA[ Array objects ]]> https://www.stitcher.io/blog/array-objects-with-fixed-types abstract class Collection implements \ArrayAccess, \Iterator { private $position; private $array = []; public function __construct() { $this->position = 0; } public function current() { return $this->array[$this->position]; } public function offsetGet($offset) { return isset($this->array[$offset]) ? $this->array[$offset] : null; } public function offsetSet($offset, $value) { if (is_null($offset)) { $this->array[] = $value; } else { $this->array[$offset] = $value; } } public function offsetExists($offset) { return isset($this->array[$offset]); } public function offsetUnset($offset) { unset($this->array[$offset]); } public function next() { ++$this->position; } public function key() { return $this->position; } public function valid() { return isset($this->array[$this->position]); } public function rewind() { $this->position = 0; } }

A concrete implementation of the Collection class.

class TypeCollection extends Collection 
{
    public function offsetSet($offset, $value) {
        if (!$value instanceof Type) {
            throw new \InvalidArgumentException("Value must be of type `Type`.");
        }
    
        parent::offsetSet($offset, $value);
    }
    
    public function offsetGet($offset): ?Type {
        return parent::offsetGet($offset);
    }
    
    public function current(): Type {
        return parent::current();
    }
}

Using the TypeCollection can be done like this.

$collection = new TypeCollection();
$collection[] = new Type();

foreach ($collection as $item) {
    var_dump($item);
}
]]>
2017-05-25T00:00:00+00:00
<![CDATA[ Stitcher beta 1 ]]> https://www.stitcher.io/blog/stitcher-beta-1 The first beta release of Stitcher has arrived. Together with a lot of bugfixes, the website has been given some more love.

# Installation

The installation package, pageon/stitcher, now loads the beta version by default. If you're running an existing project, your should also require the beta version now:

composer require pageon/stitcher-core @beta

# Changelog

  • Add empty array fallback in FilterAdapter to prevent undefined index error.
  • Improved plugin initialisation support. The temporary init function isn't required anymore, the constructor can now be used.
  • Make the adapter factory extensible.
  • Improve the CollectionAdapter by adding the browse variable. This variable can be used to browse the detail pages. It has a next and prev key which contains the next and previous entry, if there are any.
  • Moved Brendt\Stitcher\SiteParser to Brendt\Stitcher\Parser\Site\SiteParser and refactored its service definition.
  • Added Brendt\Stitcher\Parser\Site\PageParser to parse a single page, which is no longer the responsibility of SiteParser.
  • Bugfix for general meta configuration overriding other meta values.
]]>
2017-05-20T00:00:00+00:00
<![CDATA[ PHP Generics and why we need them ]]> https://www.stitcher.io/blog/php-generics-and-why-we-need-them In today's blog post we'll explore some common problems with arrays in PHP. All the problems and issues listed could be solved with a pending RFC which adds generics to PHP. We won't explore in too much detail what generics are, but at the end of this read, you should have a good idea as to why they are useful, and why we really want them in PHP. So without further ado, let's dive into the subject.

Imagine you have a collection of blog posts, loaded from a data source.

$posts = $blogModel->find();

Now you want to loop over every post, and do something with its data; let's say, the id.

foreach ($posts as $post) {
    $id = $post->getId();
    
    // Do something
}

This is a scenario that happens often. And it's this scenario we'll explore to discuss why generics are awesome, and why the PHP community desperately needs them.

Let's take a look at the problems of the above approach.

# Data integrity

In PHP, an array is a collection of… things.

$posts = [
    'foo',
    null,
    self::BAR,
    new Post('Lorem'),
];

Looping over this array of posts would result in a fatal error.

PHP Fatal error:  Uncaught Error: 
Call to a member function getId() on string

We're calling ->getId() on the string 'foo'. Not done. When looping over an array, we want to be sure that every value is of a certain type. We could do something like this.

foreach ($posts as $post) {
    if (! $post instanceof Post) {
        continue;
    }

    $id = $post->getId();
    
    // Do something
}

This would work, but if you've written some production PHP code, you know these checks can grow quickly, and pollute the codebase. In our example, we could verify the type of each entry in the ->find() method on $blogModel. However, that's just moving the problem from one place to another. It's a bit better though.

There's another problem with data integrity. Say you have a method which requires an array of Posts.

function handlePosts(array $posts) {
    foreach ($posts as $post) {
        // ...
    }
}

Again, we could add extra checks in this loop, but we could not guarantee that $posts only holds a collection of Post objects.

As of PHP 7.0, you could use the ... operator to work around this issue.

function handlePosts(Post ...$posts) {
    foreach ($posts as $post) {
        // ...
    }
}

But the downside of this approach: you would have to call the function with an unpacked array.

handlePosts(...$posts);

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

# Performance

You can imagine it's better to know beforehand whether an array contains only elements of a certain type, rather than manually checking the types within a loop, every, single, time.

We can't do benchmarks on generics, because they don't exist yet, so it's only guessing as to how they would impact performance. It's not far-fetched to assume though, that PHP's optimised behaviour, written in C; is a better way to solve the problem than to write lots of userland code.

# Code completion

I don't know about you, but I use an IDE when writing PHP code. Code completion increases productivity immensely, so I'd also like to use it here. When looping over posts, we want our IDE to know each $post is an instance of Post. Let's take a look at the plain PHP implementation.

# BlogModel

public function find(): array {
    // return ...
}

As of PHP 7.0, return types were added, and in PHP 7.1 they were refined with nullables and void. But there's no way our IDE can know what's inside the array. So we're falling back to PHPDoc.

/**
 * @return Post[]
 */
public function find(): array {
    // return ...
}

When using a "generic" implementation of e.g. a model class, type hinting the ->find() method might not be possible. So we're stuck with type hinting the $posts variable, in our code.

/** @var Post[] $posts */
$posts = $blogModel->find();

Both the uncertainty of what's exactly in an array, the performance and maintenance impact because of scattered code, and the inconvenience when writing those extra checks, makes me long for a better solution.

That solution, in my opinion is generics. I won't explain in detail what generics do, you can read the RFC to know that. But I will give you an example of how generics could solve these issues, guaranteeing the developer would always have the correct data in a collection.

Big note: generics do not exist in PHP, yet. The RFC targeted PHP 7.1, and has no further information about the future. The following code is based on the the Iterator interface and the ArrayAccess interface, which both exist as of PHP 5.0. At the end, we'll dive into a generics example, which is dummy code.

First we'll create a Collection class which works in PHP 5.0+. This class implements Iterator to be able to loop over its items, and ArrayAccess to be able to use array-like syntax to add and access items in the collection.

class Collection implements Iterator, ArrayAccess
{
    private int $position;

    private array $array = [];

    public function __construct() {
        $this->position = 0;
    }

    public function current(): mixed {
        return $this->array[$this->position];
    }

    public function next(): void {
        ++$this->position;
    }

    public function key(): int {
        return $this->position;
    }

    public function valid(): bool {
        return array_key_exists($this->position, $this->array);
    }

    public function rewind(): void {
        $this->position = 0;
    }

    public function offsetExists($offset): bool {
        return array_key_exists($offset, $this->array);
    }

    public function offsetGet($offset): mixed {
        return $this->array[$offset] ?? null;
    }

    public function offsetSet($offset, $value): void {
        if (is_null($offset)) {
            $this->array[] = $value;
        } else {
            $this->array[$offset] = $value;
        }
    }

    public function offsetUnset($offset): void {
        unset($this->array[$offset]);
    }
}

Now we can use the class like this.

$collection = new Collection();
$collection[] = new Post(1);

foreach ($collection as $item) {
    echo "{$item->getId()}\n";
}

Note that with this simple implementation, there's no guarantee that $collection only holds Post object. For example, adding a string would work fine, but would break our loop.

$collection[] = 'abc';

foreach ($collection as $item) {
    // This fails
    echo "{$item->getId()}\n";
}

With PHP as it is now, we could fix this problem by creating a PostCollection class.

class PostCollection extends Collection
{
    public function current() : ?Post {
        return parent::current();
    }

    public function offsetGet($offset) : ?Post {
        return parent::offsetGet($offset);
    }

    public function offsetSet($offset, $value) {
        if (! $value instanceof Post) {
            throw new InvalidArgumentException("value must be instance of Post.");
        }

        parent::offsetSet($offset, $value);
    }
}

Now only Post objects can be added to our collection.

$collection = new PostCollection();
$collection[] = new Post(1);

$collection[] = 'abc';

foreach ($collection as $item) {
    echo "{$item->getId()}\n";
}

It works! Even without generics! There's only one issue, you might be able to guess it. This is not scalable. You need a separate implementation for every type of collection, even though the only difference between those classes would be the type. Also note that IDEs and static analysers will be able to correctly determine the type, based on the return type of offsetGet in PostCollection.

You could probably make the subclasses even more convenient to create, by "abusing" late static binding and PHP's reflection API. But you'd still need to create a class, for every type available.

# Glorious generics

With all that in mind, let's just take a look at the code we would be able to write if generics were implemented in PHP. This would be one class which could be used for every type. For your convenience, I'll only be writing the changes compared to the previous Collection class, so keep that in mind.

class GenericCollection<T> implements Iterator, ArrayAccess
{
    public function current() : ?T {
        return $this->array[$this->position];
    }

    public function offsetGet($offset) : ?T {
        return $this->array[$offset] ?? null;
    }

    public function offsetSet($offset, $value) {
        if (! $value instanceof T) {
            throw new InvalidArgumentException("value must be instance of {T}.");
        }

        if (is_null($offset)) {
            $this->array[] = $value;
        } else {
            $this->array[$offset] = $value;
        }
    }

    // public function __construct() ...
    // public function next() ...
    // public function key() ...
    // public function valid() ...
    // public function rewind() ...
    // public function offsetExists($offset) ...
}
$collection = new GenericCollection<Post>();
$collection[] = new Post(1);

// This would throw the InvalidArgumentException.
$collection[] = 'abc';

foreach ($collection as $item) {
    echo "{$item->getId()}\n";
}

And that's it! We're using T as a dynamic type, which can be checked before runtime. And again, the GenericCollection class would be usable for every type, always.


]]>
2017-05-17T00:00:00+00:00
<![CDATA[ Stitcher alpha 5 ]]> https://www.stitcher.io/blog/stitcher-alpha-5 This is the last alpha version of Stitcher. The next release will be beta for the first time, and only bugfixes and improvements will be added from now on. Alpha 5 adds the last important pieces for Stitcher to be feature-complete before a stable 1.0 release. The most important things to note are the plugin support, improved command feedback and the internal use of the service container.

You can read about the upcoming plugin support in this blogpost. Furthermore, I'm already working on the first plugin to support a REST API. Next step is a web interface to manage your content. For developers, Stitcher 1.0 will of course be completely useable without any plugins.

It's important to note that this update has a breaking change which existing Stitcher projects should take into account.

# Installation

composer require pageon/stitcher-core 1.0.0-alpha5

# Update - breaking changes

A last big refactor has been done to support more extensions in the future. This means both the Console and the DevController now live in a different namespace. You'll need an updated version of stitcher and index.php. This can be done with the following commands.

rm ./stitcher
rm ./dev/index.php
cp vendor/pageon/stitcher-core/install/stitcher ./stitcher
cp vendor/pageon/stitcher-core/install/dev/index.php ./dev/index.php

# Remove the cache dir, this might be another directory depending on your configuration.
rm -r .cache/

# Changelog

  • Add plugin support!
  • Add PHP 7.0 support.
  • Add Command tests for Router commands and Generate command.
  • Improved meta support.
  • Improved generate command feedback.
  • Refactor the use of the dependency container, enabling future extensions. (See breaking changes).
  • Use stable version of pageon/html-meta.
  • Fix folder parser bug with nested folders.
  • Fix with Sass compiler import paths. The Sass compiler can now also look directly in src/css. This is useful when doing includes and IDE auto-completion.
  • Fix global meta tags not being loaded.
  • Fix for meta tags on detail pages not correctly set.
]]>
2017-05-01T00:00:00+00:00
<![CDATA[ The simplest plugin support ]]> https://www.stitcher.io/blog/simplest-plugin-support Stitcher's plugin support is available as of stitcher 1.0.0-alpha5.

In this post, you'll read about Stitcher's plugin system. It might get a bit technical, but is definitely worth the read.

Stitcher plugins are built on top of two powerful components which already exist in many modern projects.

  • Composer's auto loading
  • Symfony's service container

Using these two components, a plugin is no more than a composer package, telling Stitcher it should add its own classes and parameters to the existing services. It's a wonderfully simple concept, and it works like a charm. Like almost everything in Stitcher: the simpler, the better. Let's take a look at an example.

# MyPlugin

This is what a plugin's folder structure could look like.

MyPlugin/
	├── src/
	│   ├── My/
	│   │    ├── MyPlugin.php
	│   │    └── Service.php
	├── config.yml
	├── services.yml
	├── composer.json
	└── README.md

The only requirement for a package to be "a plugin" is a class implementing the Brendt\Stitcher\Plugin\Plugin interface. In this example, that would be My\MyPlugin. When this class can be autoloaded with composer, your plugin is ready!

# Plugin interface

The Plugin interface requires you to only implement three methods. These methods tell Stitcher where the services.yml and config.yml files are located and how to initialise the plugin. Any other binding with Stitcher is done via the service container.

namespace My;

use Brendt\Stitcher\Plugin\Plugin;

class MyPlugin implements Plugin
{
    public function init() {
        return;
    }

    public function getConfigPath() {
        return __DIR__ . '/plugin.config.yml';
    }

    public function getServicesPath() {
        return __DIR__ . '/plugin.services.yml';
    }
}

# init method

The init method is called after all plugin config is loaded. This method can be used as a hook to add plugin configuration to existing services. An example would be adding a command to the console application.

/**
 * @return void
 */
public function init() {
    /** @var Console $console */
    $console = App::get('app.console');

    $console->add(App::get('my.plugin.command.my.cmd'));
}

# plugin.config.yml

The name doesn't matter as long as its a yaml file. This file works exactly the same as other config files: key-value pairs can be added and will be available as parameters in the service container. Keys can be nested, but will be flattened when loaded. One thing to note is that plugins cannot override existing parameters.

Your plugin parameters can of course be overridden from within a Stitcher project.

# ./vendor/MyPlugin/plugin.services.yml

my.plugin:
    parameter: test

# plugin.services.yml

Again, the name doesn't matter, but the root element must be named services as per Symfony's requirements. You could also add parameters here.

# ./vendor/MyPlugin/plugin.services.yml

services:
    my.plugin.my.service:
        class: My\Service
        arguments: ['%my.plugin.parameter%', '%directories.src%', '@stitcher']

As you can see, Stitcher services and parameters are available, as well as your own.

# Loading a plugin

Finally, a plugin must be loaded into your project for it to be active. The plugins parameter in your project's config file is used for doing that.

# ./config.yml

plugins:
    - My\MyPlugin

That's it!

# Future possibilities

This plugin system is so simple, yet it opens the possibility to add all kinds of functionality to a Stitcher project. It's an important step towards some of my own ideas; custom themes and other applications (API and CMS); and we'll discover more of its true strength in the future.

The most important thing for me is its simplicity. When looking at plugin systems in other applications, you'll often find complex setups like a virtual directory structure, a custom plugin loader, dirty file naming conventions, own package managers, etc. I wanted to use existing and proven technologies to build on top on, and keep the system as clean as possible. I believe this approach is a step towards the right direction.

]]>
2017-04-27T00:00:00+00:00
<![CDATA[ Stitcher alpha 4 ]]> https://www.stitcher.io/blog/stitcher-alpha-4 A new alpha release has arrived for Stitcher!

This release brings a lot of optimizations and bugfixes to many parts of Stitcher. The biggest changes are found in brendt/responsive-images, in which a lot of bugfixes and extra options are added. Furthermore, there's one change reverted, namely the asynchronous support for image rendering. This functionality relied on several amphp development packages, and broke with almost every update. Async support might be re-added in the future, but for now it's disabled.

One of the biggest new features is the support for custom htaccess headers and with that, HTTP2 server push! This feature has been added and is tested, but not yet used in any real projects. So there's more testing to do before declaring it "stable". You can use it in almost any template function by added the push=true parameter.

Stitcher also uses papgeon/html-meta now, and will build on top of this library more and more in the future.

One final new feature is the addition of the cdn config parameter. This parameter takes an array of files, located in the source directory, and will copy them on-the-fly or during compile-time to the public directory. This way you can expose folders or files directly, without parsing them through Stitcher.

# Installation

The installation package, pageon/stitcher, still comes with 1.0.0-alpha3 by default. Feel free to manually update the composer requirement to 1.0.0-alpha4. The default version will change as soon as HTTP/2 server push is fully tested.

Some people might need to run composer dump-autoload -o one more time when updating to alpha4.

# Future updates

Before this update, Stitcher was always re-tagged on the fly when new things were added. From now on, tags will only be added after a certain feature set is complete. By doing so, updating Stitcher won't break things as much as it used to do. Keep in mind Stitcher is still in alpha phase, so breaking changes will happen now and then. There's still a small feature set to be added before a first beta release will be available. Slowly but surely, we're getting there.

]]>
2017-04-21T00:00:00+00:00
<![CDATA[ Static site generators vs. caching ]]> https://www.stitcher.io/blog/static_sites_vs_caching What's the difference between static site generators and caching you ask?

A short answer might be: there is no technical benefit between both. But the mindset behind the two is completely different.

I think the topic is too interesting to leave it like that. Let's talk about caching.

For many years now, we've been creating systems which help us build websites. Many of those systems are built around the idea of "dynamic websites". Instead of writing HTML and CSS pages; we've designed systems which can load information from a data source (say for example, a MySQL database); parse that data into so called "templates" (these are like blueprints for HTML pages); and finally send the rendered HTML to the client. Of course, there is more than just HTML and CSS to a website, but that's a topic for another day.

Now imagine you've got many visitors on your website, each of them visiting the same page. Rendering that page for every visit would require more server resources than to render the page once, and send that output to everyone asking it. That's a cached page. You could also cache other parts of the application. For example: not always perform the same database query, but rather cache the result of that query and reuse that over and over again.

Evidently, caching is way more than what I just described. My try at a general definition for caching on the web would be something like this.

Once a resource intensive operation is done, remember the outcome. The next time the same operation is requested, you can just give the result instead of doing that operation again.

Caching is a very powerful tool which wraps around your system, enabling it to be much more performant.

Stitcher, and all static site generators, are the opposite. These tools don't wrap around a system. Rather, their core is the HTML output. All other things needed by developers to smoothly build websites, are plugged in into that core. What's the downside? You'll have to re-render parts of your website before they are visible to the visitor. A tedious task. Luckily computers are good at performing the same tedious tasks over and over again. Re-rendering your website isn't really a bother when you have the right tools available.

Another "downside" of static websites? It requires a bit more thought of the developer. But when could that a bad thing?

So static websites do have their downsides. But take a look at the things you're able to "plug in" that HTML rendering core:

  • Image optimisation: enabling the developer to use the responsive images specification to its full extent, without any work.
  • SASS precompiling: I'm not a frontend developer, but these guys tell me that's a must.
  • Pagination, overviews and detail pages.
  • Parse MarkDown, YAML and JSON into templates and use those templates like in any dynamic system.
  • JavaScript and CSS minifying: very important for website performance.
  • Things like ordering and filtering data sets.

Some important things are still missing in Stitcher though.

  • Form support: although Stitcher will not include form handling at its core. That will be a separate module.
  • Frontend filtering of data sets: technically this is possible, but it might have huge performance costs depending on the amount of filters. I will be working on it in the future though.
  • Content management: this is also possible, but not from within Stitcher's core. It would be a separate module acting as a client to modify a Stitcher project.

To be clear: I don't think static site generators are the best solution for all websites. But there are lots of cases which could benesfit from using a static site generator over of a dynamic system and caching. I view many caching systems as like putting a bandaid on top of a wound, but not stitching the wound (pun intended). Don't forget that clearing caches is one of the most difficult parts of software development. But we should also be realistic: the static website approach mainly targets small to medium websites, not complex web applications.

So if you want to give it a go, be sure to check out a static site generator, there are many!

]]>
2017-03-02T00:00:00+00:00
<![CDATA[ Image optimizers ]]> https://www.stitcher.io/blog/image_optimizers I've been working lately on image optimisation in Stitcher. As a try-out, I've added this library to the responsive images module.

Enabling the optimizer is done by updating Stitcher (1.0.0-alpha2), and adding the following parameter in config.yml.

engines:
    optimizer: true
]]>
2017-02-26T00:00:00+00:00
<![CDATA[ Tackling responsive images - part 2 ]]> https://www.stitcher.io/blog/tackling_responsive_images-part_2 In my previous post, I wrote about the idea behind integrating responsive images and Stitcher. A pretty robust library came to be. You could throw it any image, and it would generate a set of variations of that images, scaled down for multiple devices. It returned an object, which Stitcher parsed into a template variable. In templates, the following is now possible.

<img src="{$image.src}" srcset="{$image.srcset}" sizes="{$image.sizes}" />

If you would like to read the source code instead of this post, here you go.

Like I wrote earlier, the first version of the scaling down algorithm was based on the width of images. It worked, but it wasn't solving the actual problem: optimizing bandwidth usage. The real solution was in downscaling images based on their filesizes. The problem there: how could you know the dimensions of an image, when you know the desired filesize. This is where high school maths came into play. I was actually surprised how much fun I had figuring out this "formula". I haven't been in school for a few years, and I was rather happy I could use some basic maths skills again!

This is what I did:

filesize = 1.000.000
width = 1920
ratio = 9 / 16
height = ratio * width

area = width * height
 <=> area = width * width * ratio

pixelprice = filesize / area
 <=> filesize = pixelprice * area
 <=> filesize = pixelprice * (width * width * ratio)
 <=> width * width * ratio = filesize / pixelprice
 <=> width ^ 2 = (filesize / pixelprice) / ratio
 <=> width = sqrt((filesize / pixelprice) / ratio)

So given a constant pixelprice, I can calculate the required width an image needs to have a specified filesize. Here's the thing though: pixelprice is an approximation of what one pixel in this image costs. That's because not all pixels are worth the same amount of bytes. It heavily depends on which image codecs are used. It is however the best I could do for now, and whilst I might add some more logic in the future, I'd like to try this algorithm out for a while.

So now the Responsive Factory scales down images by filesize instead of width. A much better metric when you're trying to reduce bandwidth usage. This is how the library is used in Stitcher:

use Brendt\Image\Config\DefaultConfigurator;
use Brendt\Image\ResponsiveFactory;

$config = new DefaultConfigurator([
    'driver'      => Config::get('engines.image'),
    'publicPath'  => Config::get('directories.public'),
    'sourcePath'  => Config::get('directories.src'),
    'enableCache' => Config::get('caches.image'),
]);

$responsiveFactory = new ResponsiveFactory($config);

All images in Stitcher go through this factory, the factory will generate x-amount of variations of the image, and the browser decides which one it will download. Its pretty cool, and I hope it will help websites to serve more optimized images, while a developer can still focus on the most important parts of his project.

]]>
2017-02-18T00:00:00+00:00
<![CDATA[ Tackling responsive images - part 1 ]]> https://www.stitcher.io/blog/tackling_responsive_images-part_1 One of the main goals when I started with Stitcher was heavily optimized images. Looking at the HTTP Archive stats, it's clear we're doing something wrong. Luckily, the Responsive images spec has been made by a lot of smart people to counter the image problem. My goal was to implement this spec in Stitcher in a way that was easy enough for developers to use it to its full extent. We're not completely there yet, but we're close. In this blogpost I want to talk about the challenges I faced creating this library. And if you're more into code then into reading a blog post, here you go.

To be clear: the goal of the responsive images spec is to reduce bandwidth used when downloading images. Images nowadays require so much bandwidth. When you think about it, it's insane to load an image which is 2000 pixels wide, when the image on screen is only 500 pixels wide. That's the issue the spec addresses, and that's the issue I wanted to solve in Stitcher.

So I want one image to go in, x-amount of the same image with varying sizes coming out, and let the browser decide which image is the best to load. How could I downscale that source image? That was the most important question I wanted answered. All other problems like accessebility in templates and how to expose the generated image files, were concerns of Stitcher itself.

My first take on downscaling images was the following:

Take the source image and a set of configuration parameters. These parameters would decide the maximum amount of image variations and the minimum width of the image. Eg. I want a maximum of ten images, with the smallest image being 300 pixels wide. Now the algorithm would loop a maximum of 10 times, always creating an image which is 10% smaller in width than the previous one.

You might already see this is not the optimal approach. After all: we're trying to reduce bandwidth used when loading images. There is no guarantee an image which is downscaled 10%, is also reduced in size. Much depends on which image codecs are used, and what's in the image itself. But by using this approach early on, I was able to implement this "image factory" with Stitcher. Next I would be working on optimizing the algorithm, but for the time being I could tackle the Stitcher integration.

# Linking with Stitcher

Letting Stitcher know about responsive images was both easy and difficult at the same time. The basic framework was already there. So I could easily create an image provider which used the responsive factory, and returned an array representation of the image. The template syntax looks like this:

<img src="{$image.src}" srcset="{$image.srcset}" sizes="{$image.sizes}" />

Unfortunately, there is no way to automate the sizes part, unless you start crawling all CSS and basically implement a browser engine in PHP. My solution for this part is pre-defined sets of sizes. That's still a work in progress though, I'm not sure yet how to make it easy enough to use. For now, I'm just manually specifying sizes when writing template code.

But the tricky part wasn't the sizes, neither the srcset. It was handling paths and URLs. I've noticed this throughout the whole Stitcher framework: creating the right paths and URLs (correct amount of slashes, correct root directory etc.) is actually quite the pain to manage. I'm convinced by now I need some kind of helper which always renders the correct paths and URLs. It's on my todo list.

That's it for this blogpost, next up I'll be writing about optimizing the image algorithm.

]]>
2017-02-17T00:00:00+00:00
<![CDATA[ Which editor to choose? ]]> https://www.stitcher.io/blog/which-editor-to-choose So many editors to choose from! Which one is the best for you? I can already tell you that you won’t find the answer here. But maybe I can list some pros and cons. We’ll be looking at Sublime Text, Github’s Atom, Adobe’s Brackets and Microsoft’s Visual Studio Code. All of these editors are based on the same core concepts, some of which Sublime made extremely popular. But there are some big and subtle differences.

# Out of the box features

All four editors are multi platform, have the command palette and fuzzy finder we’ve grown accustomed to. It’s important to keep in mind that Sublime and Atom are primarily focussed on packages to provide functionality, while Brackets and Visual Studio Code provide a more all-in-one solution from the start. More about packages later, here are the most important differences out of the box.

Visual Studio Code comes with built-in GIT support, a task runner and a linter. You can start to code without having to set up anything. It’s focussed on Node and ASP.NET development, which is reflected in the tools provided. But you can use it for any other language.

Sublime Text provides a lot of themes from the start, has a built in project manager and offers many customisable keybindings and commands to do text manipulation. There are however a lot of packages you’ll want to download immediately.

Atom has a package manager shipped by default. Atom’s file tree sidebar has some very nice features such as GIT support and file manipulation (see below). There’s also a live MarkDown editor which is really neat. But like Sublime, you’ll want to install extra packages from the start.

Brackets has an awesome live preview feature which just blew my mind. Brackets is focussed on front-end web development and provides very good tools to do so. It also comes with a linter, debugger, inline editor and Photoshop integration. There’s an extension manager available too. (That’s the Adobe version of packages, more about those later).

I felt Visual Studio Code and Brackets were really just plug-and-play from the start. Both Sublime and Atom require a lot of tweaking to set everything up for the best coding experience. This isn’t a bad thing, but in this category, Visual Studio Code and Brackets are the best.



# Packages

Packages (or extensions, thanks Adobe), give you access to a lot of extra features.

Brackets has an extension manager which is rather slow and bulky and has an “Adobe feel” to it. You can easily install packages from a local source, URL or an online repository. The extension manager lacks however good package documentation.

In Sublime, you’ll need Package Control if you want to easily install other packages. There’s a very wide variety of packages available there. Chances are that you’ll be able to do that one thing you like with an existing package. Browsing packages is a bit of a pain from the command palette though. There are many small undocumented packages which makes it often a guess as to what a package really does. The online documentation isn’t user friendly either. It’s mostly a huge pile of text per package.

Atom shines when it comes to packages. It has a built-in package manager which works directly with GitHub. Not only are there a lot of packages available, there’s also a very high standard on documentation. You’ll be able to see screenshots, keybinding references and even animated GIFs explaining how a package works and what it does. All from within Atom. It’s super easy to update packages and Atom will tell you when a package is outdated or uses deprecated code. It shouldn’t surprise you that Atom itself is actually a collection of these same packages.

Visual Studio Code as of VSC V0.10.1 there’s extension support, which looks a lot like Sublime’s Package Control. Because of the recent popularity of Visual Studio Code, there's a big plugin system rising.

Atom is a winner when it comes to packages. The whole system is built upon the package manager, and there’s a big community behind it. That should be no surprise, knowing that GitHub is creating this editor.



# File tree

You might find it odd I list the file tree as a category. From experience though, I feel the tree is one of the most important features which can really work with or work against you. You might not use the file tree at all, but a lot of people do. So I felt it was right to talk about it here.

Sublime Text is fast and this is also reflected in the tree. It lacks however some important functionality related to file manipulation from the tree.

Brackets has a very bulky and slow tree. Opening folders and files takes a notable time. It also offers only the bare minimal tools like Sublime: new files and folders, renaming, deleting and revealing/searching files.

Visual Studio Code doesn’t have a lot more tools than Brackets or Sublime, but it allows you to move files inside the tree, which is a big help. There are some minor points though. Visual Studio Code doesn’t show tabs, but uses the tree pane to show open files. It makes this pane become cluttered and makes it difficult to find the open file you’re looking for. It’s also not possible to scroll sideways. But you can use the same pane as a search and debugger view, which is space efficient.

Atom has a lot of tree functionality: there are simple tools like copy/paste, but also cut, duplicate, rename etc. You can also move files by dragging them. Atom furthermore integrates GIT project status in the file tree. The tree might feel a bit slower than Sublime or Visual Studio Code though.

Both Atom and Sublime have great file tree features, and both lack some. Sublime can’t be beaten by speed, but Atom offers a lot more functionality. Many people don’t use the tree view in Sublime, but together with Atom’s GIT status you’ll get a good project overview by just looking at the tree.



# Performance

Performance is one of the most important metrics. All of these editors are performant for sure, but each has its own small differences.

Atom lacks in this category. There are two major issues: startup time and big files. Atom is built upon web technologies (HTML, CSS and JavaScript). It has some major advantages, but takes a while longer to load. It’s however only the startup, and still considerably faster than any IDE. Once everything is loaded, Atom is as fast as Brackets. On the other side, big file loading time is a disaster. Atom will open files once you’ve selected them in the tree view. It’s easy to miss click a minified file, which will make Atom hang for several seconds or even minutes.

Visual Studio Code is a bit faster than Atom and Brackets, it works as you might expect from a Microsoft product: not slow, but also not the fastest.

Brackets is comparable to Atom, but the slow and bulky tree view makes everything feel slower.

Sublime is by far the winner here. It’s lightning fast all the time, and can’t be beaten by any other editor. Atom and Brackets loose this competition, but are still a lot faster than full blown IDEs. Another aspect to keep in mind is the amount of packages you’re using. Atom actually tells you how much milliseconds each package adds to startup time. Sublime is also subject to this: the more packages the slower. But without any doubt: Sublime shines in the field of performance.



# Configuration

Sublime, Brackets and Visual Studio Code offer an easy JSON config file for settings and keybindings. Brackets and Visual Studio Code even open a two column layout when editing settings, one with the defaults and one with your own. A small but convenient feature.

Atom however excels at customisability with its own stylesheet and startup script which can be hacked in any way you want. It has a built-in keybinding debugger, the Chrome developer tools, works with CoffeeScript (JS) and CSS. You don’t need to learn another language to customise Atom, it’s built upon web technologies. Furthermore, each package has its own configuration page with a lot of documentation and sometimes input fields to set parameters.



That was a lot of information! Some of the most important things summarized:

Visual Studio Code is focused on Node and ASP.NET development. It isn’t very customisable but has the Microsoft IDE feel to it. It’s an easy plug and play setup. Files are not shown in tabs, which makes it feel a bit unorganised, but I think that this is a preference and a developer can get used to this method of work.

Sublime Text has a lot of power. It’s fast and reliable. There are a lot of packages to customise your development environment, but they are often not very well documented. Sublime starts out as a text editor, but can be made the perfect, performant IDE with time and effort.

Brackets has some awesome front-end web development features like live previews, linters and PSD integration. The main downside is that it feels a bit slow, especially the file tree.

Atom is built on web technologies and its packages. It’s offers a very nice interface for packages and configuration and is “hackable to the core”. It has some quirks still with performance, but there’s a very active community working on it. Its customisability makes Atom accessible for a wide variety of programmers with their own workflow.

]]>
2015-08-24T00:00:00+00:00