"Just use Psl for that"
![Gert de Pagter](/author/gert-de-pagter/avatar_hu16836263939497502965.webp)
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.