"Just use Psl for that"

Jul 23, 2024·
Gert de Pagter
Gert de Pagter
· 4 min read

Something I’ve been hearing myself says in every project that I have installed Psl in, is “Just use Psl for that”. And while some bits and bobs like array_find are getting into PHP 8.4, the library does so much more. In this post I’ll go over the bits that I use often, and maybe convince you to give it a try as well.

What is Psl

Psl is a php composer package that can be found right here.

To quote the Psl readme:

Psl is a standard library for PHP, inspired by hhvm/hsl. The goal of Psl is to provide a consistent, centralized, well-typed set of APIs for PHP programmers.

And that is what it does, from async operations, to working with the filesystem, to hashing, it provides a clean api, that is consistent and well typed, and integrates well with static analysis tools like PHPStan and Psalm.

Useful Psl parts

Type

In my opinion the Type namespace is probably the most useful part of Psl. Very often we have to validate some data that comes from some input where we are (almost) guaranteed what type it is, but we can’t be 100% certain. Here the Type namespace of Psl comes into play. Let’s say you have a specific structure you expect to receive from and API, and you want to validate that it is in fact correct. With Psl that would look something like this:

use Psl\Type

$userResponse = fetchUsers();

$userResponse = Type\shape([
    'users' => Type\vec(Type\shape([
        'id' => Type\int(),
        'name' => Type\nullable(Type\string()),
        'email' => Type\string(),
        'roles' => Type\vec(Type\string())
    ])),
])->coerce($userResponse);

Now after this line, you are guaranteed that $userResponse is an array, with the key users, which is a list of arrays, containing the keys specified, with their respective types. This is a runtime check that will error out if the type does not match. It also helps PHPStan and Pslam understand the structure of the data.

Each function in the Type namespace returns a Type object that you can use to either coerce or assert your data. assert checks that your data 100% matches, and then return that. Meaning that Type\int()->assert('1') wil fail. coerce will try to coerce your data, meaning that Type\int()->coerce('1'), will return the integer 1. There is also the matches function, which returns a bool of whether your type matches.

Dict, Vec & Iter

These three namespaces have a lot of overlap. Dict is for Dictionaries, Vec for lists, and Iter for general iterables. They contain all kinds of usefull array/iterable functions that you need, that aren’t present in PHP natively, or require multiple function calls.

For example if I want to filter out all elements that are 3 or smaller, but keep my list as a list, I need to do an array_filter, and then don’t forget to wrap it in an array_values. With Psl, since the Vec namespace is about lists, it makes it a list, regardless of whether it was a list before, or even if it was a Generator before, without any extra steps required.

use Psl\Vec;
// Native
array_values(array_filter($myArray, function (int $input) => $input > 3));

// Psl
Vec\filter($myArray, function (int $input) => $input > 3))

One function I tend to use a lot from the Dict namespace is Dict\reindex. What this does is reindex an array given a callback. Which you can do with something like array_reduce, or in a foreach loop, but having a dedicated function to do some makes the code a lot more readable in my opinion.

$users = [
    ['id' => 42, 'name' => 'foo'],
    ['id' => 24, 'name' => 'bar']
 ];

 $indexedUsers = Dict\reindex($users, fn($user) => $user['id'])
 // $indexedUsers is now:
 [
    42 => ['id' => 42, 'name' => 'foo'],
    24 => ['id' => 24, 'name' => 'bar']
 ]

And these few namespaces aren’t even scratching the surface of the functions this library provides. Although I currently don’t have to deal with Async operations in an existing codebase, if I needed to, I would probably check out Psl first.

If you want to get notified of the next blog post, join the newsletter.