PHP 8.2.0 Beta 2 available for testing – PHP: Hypertext Preprocessor

The PHP team is pleased to announce the second beta release of PHP 8.2.0, Beta 2. This continues the PHP 8.2 release cycle, the rough outline of which is specified in the PHP Wiki.For source downloads of PHP 8.2.0 Beta 2 please visit the download page.Please carefully test this version and report any issues found in the bug reporting system.Please DO NOT use this version in production, it is an early test version.For more information on the new features and other changes, you can read the NEWS file, or the UPGRADING file for a complete list of upgrading notes. These files can also be found in the release archive.The next release will be PHP 8.2.0 Beta 3, planned for Aug 18 2022.The signatures for the release can be found in the manifest or on the QA site.Thank you for helping us make PHP better.

Narrowing types for static analysis – Jordi Boggiano

I have spent the last year moving a few big old codebases, including Composer, to PHPStan’s level 8. Here are a few lessons I think I have learned in the process.

Baseline + strict static analysis is the way to go

I was for a while skeptical about using the baseline feature as it seemed to me like shoving all type errors under the rug, never to be looked at again.

I still believe there is some truth to this, and going back and fixing things does take a conscious effort. Yet after having gone full strict (level 8 + phpstan-strict-rules + phpstan-deprecation-rules at least) on a few projects I think it is well worth it.

It lets you move much quicker to a point where all new code is at least checked strictly for errors, so you can stop piling up technical debt right now. As such I would highly recommend using a baseline to increase strictness.

Fix essential types as soon as possible

The main struggle with a strict config + baseline approach is if you have deeply broken types in PHPDoc. Including nullability information for example wasn’t so common 5-10 years ago. And maybe you changed data types entirely and forgot to update docblocks.

This can lead you to see many bogus error reports in static analysis when new code using these broken types is being analyzed. Every time you have to waste time figuring out whether this issue really needs fixing or not, and possibly decide to add it to the baseline as well.

Therefore spending some time fixing your most essential classes/types that are used throughout the project as early as possible makes a lot of sense and will save you time down the line. You can skip loading the baseline and analyze specific files to identify and fix issues in those areas that afford the greatest return on investment.

Broad input types, narrow output types

Being too strict on input (param types) means you can sometimes waste the consumers’ time validating things which maybe don’t need to be. Of course you do want to be strict enough that you don’t cause bugs so this point is definitely one for the “it depends” category.

Being too loose on output (return type) means you will definitely waste consumers’ time as they have to narrow down the types again before being able to use them.

As most APIs have more consumers than implementors, defining your API boundaries to accept broad types and return narrow types saves time overall.

This is perhaps more true for open source libraries which have even more consumers, but I think it also applies more generally to every function in every application.

Split up functions to avoid returning union types

Nullable return values is probably the most common kind of union type, and getting a Foo|null back is usually a huge pain as you will have to check for nullability before using it.

If possible at all it is usually better to offer multiple APIs doing the same but one of them enforcing that the returned type is Foo.

One concrete example of this in Composer would be the former BaseCommand::getComposer method, which is used throughout most commands to retrieve a Composer\Composer instance. However it quickly became obvious we sometimes were OK not getting an instance back, so a bool $required = true parameter was added, and when you set it to false it would change the return type to Composer\Composer|null.

This is quite a mess, and while PHPStan nowadays allows you to express the return value with @return ($required is true ? Composer : Composer|null) I would still not recommend doing this if you can avoid it.

The approach I took was to split it up in two functions, tryComposer (which can return null) and requireComposer (which will throw if it cannot give you a Composer instance). It allows most code to get a narrower return type and the few points where we do want to consider the null value can use tryComposer which mirrors the BackedEnum::tryFrom method to give you what you want or null. It also has the added benefit of leading to more readable code on the consumer side, as tryComposer hints at what it does much more than a $required parameter set to false.

Note that I would probably have named requireComposer getComposer if it wasn’t for BC requirements here, as the method already existed with different semantics. It is now deprecated though.

What’s a simple solution? – Matthias Noback

“As I’m becoming a more experienced programmer, I tend to prefer simple solutions.” Or something similar. As is the case with many programming-related quotes, this is somewhat of a blanket statement because who doesn’t prefer simple solutions? To make it a powerful statement again, you’d have to explain what a simple solution is, and how you distinguish it from not-so-simple solutions. So the million-dollar question is “What is a simple solution?”, and I’ll answer it now.

Just kidding. But I do have some ideas about this.

There are several aspects of simplicity we’d have to consider. For instance, we could consider simple solutions in programming to mean several things:

  • Easy to use (e.g. a single method call, a single dependency, a single argument, etc.)
  • Easy to understand (e.g. the logic can be grasped by looking at a single function definition)
  • Requires the least amount of code (e.g. 5 lines of code instead of 100)
  • Requires the least amount of classes, functions, etc. (e.g. 1 class instead of 10)
  • Independent (e.g. no additional packages need to be installed)
  • Easy to change (e.g. maintained by ourselves, not dependended upon too heavily)
  • Allowing reuse of the solution in a somewhat different context (e.g. portable, not necessarily generic)

These properties aren’t exclusive. What we consider simple solutions often expose several of them at the same time. There may also be others that I’ve missed here (of course). Still, this list could help us judge the simplicity of a given solution. We might even be able to quantify this simplicity. For instance, extending from a parent class would subtract 5 points from the simplicity score, because it imports quite a bit of code, adds a class to your solution, makes it less easy to understand because you have to jump to the parent class and figure out how it merges its behavior with the subclass, and on top of that requires an additional library to be installed. Considering all the downsides, the penalty might be a bit higher 😉

In the next few articles I’ll cover several areas of programming where there’s a debate about the best way to do something. E.g. for a model, should we use active record, or the data mapper pattern? For dependency resolution, should we use a container, or a locator? And so on. We’ll look at each of the solutions from the perspective of simplicity, and calculate some kind of score for the competing solutions. If I’m not mistaken, you should then be able to calculate your level of programming experience based on those simplicity scores 😉

Serde: A modern serialization library for PHP 8.1 – Larry Garfield

My book-writing workflow – Matthias Noback

By request: what’s my workflow for writing books? Steps, tools, etc.

Writing with the Leanpub platform

A long time ago I noticed that testing-advocate Chris Hartjes published his books on Leanpub. When I had the idea of writing a book about the Symfony framework, I tried this platform and it was a good match. You can write your book in Markdown, commit the manuscript to a GitHub repository, and push the button to publish a new version. Leanpub will generate EPUB and PDF versions for you. Readers can be updated about releases via email or a notification on the website.

While publishing multiple books like this, I kept running into these limitations:

  • Even though writing a book like this feels a lot like writing code (make a change, push, deploy), what’s missing is a continuous integration step where we can verify that the book is “correct”.
  • With the Leanpub Markdown setup you can include .md chapter files from your main Book.txt file, but you can’t include files from those .md files. This makes it hard to create a nested folder structure for chapters, sections, and code samples.
  • If you include source code in a book, it has to be formatted to be readable and to fit on a page, and parts have to be skipped or abbreviated. Still, you want the code to be correct. For this you want to run tests on the code, but you can’t run tests on incomplete or abbreviated code samples. I no longer wanted to copy/paste working code and format it manually for presentation purposes.
  • The code samples should be tested, but they should also be analyzed with PHPStan, and they should be upgrade-able to newer
    PHP versions. The go-to tool for this is Rector. I want to have these tools available in my book projects by default.
  • When I include the output of tools like PHPUnit in the book, I don’t want to copy/paste it from the terminal either. When I make a change to the code, the output should be regenerated automatically and included in the book.

A manuscript pre-processor

To overcome all these problems I created a pre-processor tool that allows me to do things that aren’t possible with Leanpub’s Markdown (or Markua, their variation on Markdown), but that eventually result in a manuscript directory that Leanpub can turn into an actual book. This means that nowadays I write in “Matthias-Flavored Markua” 😉 And that I have a continously-integrated book-writing process which uses PHPUnit for tests, PHPStan for static analysis, Rector for automated refactoring, and ECS for the coding standard. The pre-processor tool is hand-written in PHP. Given that it has to process Markua, it contains a Markua parser which leverages the awesome Parsica library.

Some of the things the tool can do:

  • You can add special comments to code samples that influence the final look when included in the manuscript, e.g. // skip-start and // skip-end to create an ellipsis (// ...) or // crop-start and // crop-end to remove lines from the beginning and end of an included file.
  • You can create a cover image with Gimp, and the tool will convert the .xcf file to a .png file that matches Leanpub’s expectations.
  • You can include .md files from any other .md file. In the end, everything will be compiled into a single book.md file.
  • When you regenerate this manuscript, it’s easy to see which parts have been changed, which allows you to spot parts that were changed by mistake, or that no longer work because of a different output from PHPUnit, etc.
  • You can collect all external links and generate a link registry for it, so the book will never have broken links.
  • You can include code from a package in vendor/ and reformat it according to the style used for the other code samples.
  • And so on! The project is yet another example of the amazing powers that will be unleashed if you have access to an Abstract Syntax Tree for a language, in this case Markua.

The directory structure of a book project

The path towards releasing a new book

For me, a new book always starts with an idea, which I immediately judge for its capability of becoming a book (as opposed to a blog post, or series of blog posts). In a sense, every idea can become a book, but it has to be interesting, I should be able to build on my own exp

Truncated by Planet PHP, read more at the original (another 8937 bytes)

When to use a trait? – Matthias Noback

When to use a trait? Never.

Well, a trait could be considered to have a few benefits:

Benefits

  1. If you want to reuse some code between multiple classes, using a trait is an alternative for extending the class. In that case the trait may be the better option because it doesn’t become part of the type hierarchy, i.e. a class that uses a trait isn’t “an instance of that trait”.
  2. A trait can save you some manual copy/paste-ing by offering compile-time copy/paste-ing instead.

Downsides

On the other hand, there are several problems with traits. For instance:

  • When a trait adds one or more public methods to a class, you’ll often experience the need to define those methods as an interface that the class will automatically implement by using the trait. That’s impossible. A trait can’t implement an interface. So you have to use the trait and implement the interface explicitly if you want to achieve this.
  • Traits have no “private” methods and properties of their own. I often like to hide some things from the class where the trait is used, but it’s impossible to make things “trait-private”. Anything defined as private in the trait will still be accessible by the class where the trait is used. Which makes it impossible for a trait to encapsulate anything.

Better alternatives

Anyway, in practice, I always find better alternatives for using a trait. As an example, here are some alternatives:

  • If the trait has service responsibilities, it’s better to turn it into an actual service instead, which can and should be injected as a constructor argument into the service that requires this service. This is known as composing behavior, instead of inheriting behavior. This approach can also be used when you want to get rid of parent classes.
  • If the trait adds some behavior to an entity that is the same for another entity, it often makes sense to introduce a value object and use it instead.
  • Another option when you have the same behavior in different parts of the model, is to just copy the code. This will keep the design of each object flexible enough, so each can evolve in its own direction. When we want to change the logic, we won’t have to worry about other places that reuse the same logic.

A counter-example

Even though most situation don’t really need a trait, and there are better alternatives, there is one situation that I keep using a trait for. This is the code for that trait, and if you ever attended one of my workshops, you’ll know it already:

trait EventRecording
{ /** * @var list<object> */ private array $events = []; private function recordThat(object $event): void { $this->events[] = $event; } /** * @return list<object> */ public function releaseEvents(): array { $events = $this->events; $this->events = []; return $events; }
}

I use this trait in entities, because there I always want to do the same thing: record any number of events, then after saving the modified state of the entity, release those events in order to dispatch them.

One concern might be that releaseEvents() is a public method. But it doesn’t have to be on any interface. We don’t need an Entity interface, or a RecordsEvents interface, unless we want to create some reusable code that can save an entity and dispatch its recorded events.

Another concern is that this trait suffers from the lack of “trait-private”. As an example, instead of using $this->recordThat(...), an entity that uses this trait could also just do $this->events[] = ....

We could fix this issue by extracting the code into an object, (adding even more evidence to the statement that there’s always an alternative for using a trait):

final class EventRecording
{ /** * @var list<object> */ private array $events = []; public function recordThat(object $event): void { // ... } /** * @return list<object> */ public function releaseEvents(): array { // ... }
}

We then need to assign an instance of this new class to a private property of the entity:

final class SomeEntity
{ // Can we do this already? I forgot private EventRecording $eventRecording = new EventRecording(); /** * @return list<object> */ public function releaseEvents(): array { return $this->eventRecording->releaseEvents(); } // ...
}

Truncated by Planet PHP, read more at the original (another 1227 bytes)

Installing Xdebug on PHP 8.1 installed with Homebrew – Rob Allen

I have recently set up a new M2 MacBook Air and as usual, installed Homebrew and then installed PHP. Homebrew is always up to date, so it installed PHP 8.1 for me.

Again, as usual, I installed Xdebug using pecl install xdebug.

This whirrs and clicks for a while downloading and compiling the xdebug.so and then fails with:

rob@ardent ~ $ pecl install xdebug
downloading xdebug-3.1.5.tgz ...
Starting to download xdebug-3.1.5.tgz (232,070 bytes) ... Build process completed successfully
Installing '/opt/homebrew/Cellar/php/8.1.8/pecl/20210902/xdebug.so' PHP Warning: mkdir(): File exists in /opt/homebrew/Cellar/php/8.1.8/share/php/pear/System.php on line 294
ERROR: failed to mkdir /opt/homebrew/Cellar/php/8.1.8/pecl/20210902

Investigating, I found that /opt/homebrew/Cellar/php/8.1.8/pecl is a symlink to /opt/homebrew/lib/php/pecl, however, this directory doesn’t exist. So to fix, I created the directory and installed Xdebug again:

mkdir /opt/homebrew/lib/php/pecl
pecl install xdebug

This time it worked:

rob@ardent ~ $ pecl install xdebug
downloading xdebug-3.1.5.tgz ...
Starting to download xdebug-3.1.5.tgz (232,070 bytes) ... Build process completed successfully
Installing '/opt/homebrew/Cellar/php/8.1.8/pecl/20210902/xdebug.so'
install ok: channel://pecl.php.net/xdebug-3.1.5
Extension xdebug enabled in php.ini

A quick test shows that all is now as it should be:

rob@ardent ~ $ php -v
PHP 8.1.8 (cli) (built: Jul 8 2022 10:46:35) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.8, Copyright (c) Zend Technologies with Xdebug v3.1.5, Copyright (c) 2002-2022, by Derick Rethans with Zend OPcache v8.1.8, Copyright (c), by Zend Technologies

A nomenclature of hate – Jordi Boggiano

(What feels like) a lifetime of maintaining open source projects has left me with some things to say about the haters. I’m not sure why.

Trying to do a light take of it instead of getting mad, here is a post categorizing them.

Raging hater

Your average online troll, the raging hater just needs to empty their bile on you. Usually not worth engaging tho I’ve defused a few and gotten valuable feedback over the years, but it takes great amounts of patience.

Verbose hater

I will try to keep this one short, unlike our first hater. The verbose hater writes a book of a rant and mostly exhausts your nerves by the length of it all. There is usually a point in there somewhere but it’s so tedious to get to it and once you figure it out you just don’t want to help them anymore.

Schrödinger’s hater

This tweet by Marco Pivetta (named and shamed here with his permission because I don’t hold it against him) is a prime example of this, even though a fairly harmless one. Schrödinger’s hater is angry but you’re not sure about what. Right now they could both be hating themselves and just venting, or hate something you did. Until you engage them to know which it is it’s likely to leave you feeling bad just in case.

If you can get past the initial urge to yell back at them, I find it is usually worth asking for more info.

Illiterhater

The illiterhater isn’t really hating you, but hates to read warnings and errors. They would like you to do it for them. For some reason they can read better when they’re wasting someone else’s time and they will probably write a long issue and make you ask three times until they post the output that contains a warning giving the solution to their problem.

The mirror hater

I can tell they hate mirrors not because of their shoddy haircut but because I am pretty sure they’ve complained before about a customer or user of theirs not giving them any helpful info at all and yet here they are opening an issue which contains no more text than “it did not work”.

PHP 8.2.0 Beta 1 available for testing – PHP: Hypertext Preprocessor

The PHP team is pleased to announce the first beta release of PHP 8.2.0, Beta 1. This continues the PHP 8.2 release cycle, the rough outline of which is specified in the PHP Wiki.For source downloads of PHP 8.2.0 Beta 1 please visit the download page.Please carefully test this version and report any issues found in the bug reporting system.Please DO NOT use this version in production, it is an early test version.Because of a bug found in early testing of this release, this version is NOT usable with ZTS builds.For more information on the new features and other changes, you can read the NEWS file, or the UPGRADING file for a complete list of upgrading notes. These files can also be found in the release archive.The next release will be PHP 8.2.0 Beta 2, planned for Aug 4 2022.The signatures for the release can be found in the manifest or on the QA site.Thank you for helping us make PHP better.