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

The PHP team is pleased to announce the third beta release of PHP 8.2.0, Beta 3. 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 3 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 the first release candidate (RC 1), planned for Sept 1 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.

On syntactic sugar – Evert Pot

Ever so often the term ‘syntactic sugar’ comes when people discuss language
features, and it’s not uncommon to see the word ‘just’ right in front of it;
some examples:

The ‘just’ has a lot of meaning here. To me it suggests that language features
that are ‘just’ syntactic sugar, aren’t quite as important as features that
aren’t. Maybe it even suggests to me that the language would be fine without.

So while the above two examples both argue that both Javascript classes and
async/await aren’t syntactic sugar, they also kind of come to the defence
of those features and justify their existence. In other cases when people
call something syntactic sugar, it’s often in a context that’s somewhat
dismissal of the feature.

I think this is a bit odd and also confuses people, especially since any
actual definition I’ve found is generally positive, such as this one from
Wikipedia.

In computer science, syntactic sugar is syntax within a programming language
that is designed to make things easier to read or to express. It makes the
language “sweeter” for human use: things can be expressed more clearly, more
concisely, or in an alternative style that some may prefer.

The thing is, isn’t every language feature beyond the bare minimum of what
makes a language turing-complete syntax sugar?

  • You don’t need classes because you can use functions and structs.
  • You don’t really need types because everything can fit in a string.
  • Functions can be implemented with goto.
  • Multiply can be implemented with addition.
  • or and and, xor can be implemented with nand.
  • async/await can be implemented with generators.

These are all incredibly useful features that make it easier to read and write
code and express ideas. Almost every language feature could be considered
syntactic sugar. That’s not a bad thing, it’s just uninteresting to point out.

Simple Solutions 1 – Active Record versus Data Mapper – Matthias Noback

Having discussed different aspects of simplicity in programming solutions, let’s start with the first topic that should be scrutinized regarding their simplicity: persisting model objects. As you may know, we have competing solutions which fall into two categories: they will follow either the Active Record (AR) or the Data Mapper pattern (DM) (as described in Martin Fowler’s “Patterns of Enterprise Application Architecture”, abbrev. PoEAA).

Active record

How do we recognize the AR pattern? It’s when you instantiate a model object and then call save() on it:

$user = new User('Matthias'); $user->save();

In terms of simplicity as seen from the client’s perspective, this is amazing. We can’t imagine anything that would be easier to use. But let’s take a look behind the scenes. If we’d create our own AR implementation, then the save() function looks something like this:

final class User
{ public function __construct( private string $name ) { } public function save(): void { // get the DB connection $connection->execute( 'INSERT INTO users SET name = ?', [ $this->name ] ); }
}

In order for save() to be able to do its work, we need to somehow inject the database connection object to the save(), so it can run the necessary INSERT SQL statement. Two options:

One, we let save() fetch the connection:

use ServiceLocator\Database; final class User
{ // ... public function save(): void { $connection = Database::getConnection(); $connection->execute( 'INSERT INTO users SET name = ?', [ $this->name ] ); }
}

The practice of fetching dependencies is called service location, and it’s often frowned upon, but for now this does the trick. However, the simplicity score goes down, since we have to import the service locator, and call a method on it (-2 points?).

The second option is to pass the connection somehow to the User object. The wrong approach is this:

use ServiceLocator\Database; final class User
{ // ... public Connection $connection; public function save(): void { $this->connection->execute( // ... ); }
}

That’s because the burden of providing the Connection is now on the call site where the User is instantiated:

$user = new User();
$user->connection = /* ... get the connection */;
// ...

This would definitely cost points in the “ease-of-use” category. A better idea is to provide the connection in the framework’s bootstrap code somehow:

final class User
{ // ... public static Connection $connection; public function save(): void { self::$connection->execute( // ... ); }
} // Somewhere in the framework boot phase:
User::$connection = /* get the connection from the container */;

Because we don’t want to do this setup step for every model class, and because we are likely doing similar things in the save() function of every model, and because we want each of our model classes to have a save() function anyway, every AR implementation will end up with a more generalized, reusable approach. The way to do that is to remove the specifics (e.g. the table and column names) and define a parent class that can do everything. This parent class defines a few abstract methods so the model is forced to fill in the details:

abstract class Model
{ public static Connection $connection; abstract protected function tableName(): string; /** * @return array<string, string> */ abstract protected function dataToSave(): array; public function save(): void { $dataToSave = $this->dataToSave(); $columnsAndValues = /* turn into column = ? */; $values = /* values for parameter binding */; $this->connection->execute( 'INSERT INTO ' . $this->tableName() . ' SET ' . $columnsAndValues, $values ); }
} // Pass the connection to all models at once:
Model::$connection = /* get the connection from the container */;

We should award ourselves several simplicity points in the area of reusability! The AR model class i

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

Xdebug Update: July 2022 – Derick Rethans

Xdebug Update: July 2022

In this monthly update I explain what happened with Xdebug development in this past month. These are normally published on the first Tuesday on or after the 5th of each month.

Patreon and GitHub supporters will get it earlier, around the first of each month.

You can become a patron or support me through GitHub Sponsors. I am currently 46% towards my $2,500 per month goal. If you are leading a team or company, then it is also possible to support Xdebug through a subscription.

In July, I spend 18 hours on Xdebug, with 32 hours funded. I have been on holiday, so was not be able to dedicate as much time as I wanted to.

Xdebug 3.2

Most of the work that I did on Xdebug was related to release the first two alpha releases of Xdebug 3.2, which add support for PHP 8.2, and drops support for PHP 7.2, PHP 7.3, and PHP 7.4. I do not enjoy dropping support for older PHP versions, but the additional time it costs to maintain older versions, and have their configurations run on CI, is not really worth my time. Of course, users of PHP 7.2 through PHP 7.4 can continue to use Xdebug 3.1. The guidelines of which PHP versions Xdebug supports is outlined in the documentation.

Now that PHP 8.2 itself has approached beta state, it was time to release the first alphas of Xdebug 3.2 as well. In the near future, when PHP 8.2 comes into their Release Candidates cycle, I will release beta versions of Xdebug 3.2 as well, with a stable release when PHP 8.2.0 comes out.

These alphas and betas are an excellent opportunity to try out Xdebug 3.2, as well as PHP 8.2, to see whether your projects support the latest PHP version, and how Xdebug 3.2 interacts with that.

The main new features in Xdebug 3.2 can now also be tested. And these include:

  • Support for PHP 8.2.

  • The step debugger now introduces an extra step when returning from a function, so that you can inspect the return value. This is supported in the PHP Debug Adapter for Visual Studio Code, but not yet in PhpStorm, although there is a ticket.

  • You can now configure a list of headers to scan for through the xdebug.client_discovery_header setting, instead of only being able to set one. The documentation for this is yet to follow.

  • The step debugger now supports the pseudo hosts xdebug://gateway and xdebug://nameserver as values to the xdebug.client_host setting. These pseudo-hosts instruct Xdebug to try to make a debugging connection to the default gateway and name server (as configured on Linux through /etc/resolv.conf and friends) respectively. The documentation for this is yet to follow too.

I am intending to create a video for each of these new features for my YouTube channel.

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