azjezz/psl
PSL is a modern, well-typed standard library for PHP 8.4+, inspired by HHVM’s HSL. It offers safer, predictable APIs for async, collections, networking, I/O, crypto, terminal UI, and robust data validation—replacing brittle built-ins with consistent alternatives.
Installation: Add via Composer:
composer require php-standard-library/php-standard-library
Requires PHP 8.4+.
First Use Case: Validate input data with Type\shape():
use Psl\Type;
$userType = Type\shape([
'name' => Type\non_empty_string(),
'age' => Type\positive_int(),
]);
$validated = $userType->coerce($_POST['user']);
Where to Look First:
src/Psl/ (core components)examples/ (practical use cases in the repo)Type\shape() for nested validation with composable types.
$orderType = Type\shape([
'items' => Type\vec(Type\shape([
'id' => Type\positive_int(),
'price' => Type\positive_float(),
])),
'total' => Type\positive_float(),
]);
Async\concurrently().
Async\main(static function() {
[$user, $orders] = Async\concurrently([
static fn() => UserRepository::fetch(1),
static fn() => OrderRepository::fetchForUser(1),
]);
return view('dashboard', ['user' => $user, 'orders' => $orders]);
});
Job classes or Route handlers.array_map/array_filter with typed Vec/Dict:
use Psl\Vec;
use Psl\Str;
$uppercaseNames = Vec\map($users, fn($user) => Str\uppercase($user['name']));
Artisan command:
use Psl\Async;
use Psl\TCP;
Async\main(static function() {
$server = TCP\listen('0.0.0.0', 3333);
while (true) {
$conn = $server->accept();
Async\run(static fn() => handleConnection($conn))->ignore();
}
});
Validator with PSL’s Type in service methods:
public function createUser(array $data): User {
$userType = Type\shape([
'name' => Type\non_empty_string(),
'email' => Type\email_address(),
]);
$validated = $userType->coerce($data);
return User::create($validated);
}
Job:
public function handle() {
Async\main(static function() {
[$job1, $job2] = Async\concurrently([
static fn() => $this->dispatch(new ProcessPayment()),
static fn() => $this->dispatch(new SendNotification()),
]);
$job1->wait();
$job2->wait();
});
}
Vec/Dict to transform Eloquent collections:
public function index() {
$users = Vec\map(User::all(), fn($user) => [
'id' => $user->id,
'name' => Str\lowercase($user->name),
]);
return response()->json($users);
}
ServiceProvider:
$this->app->bind(GraphInterface::class, fn() => new DirectedGraph());
Async Context:
Async\main() must be the outermost function. Nesting it incorrectly causes silent failures.Async\run() for nested async operations.
Async\main(static function() {
Async\run(static fn() => doAsyncWork())->ignore();
});
Type Coercion:
Type\coerce() throws on invalid data. Use Type\validate() for silent checks.
if (!$userType->validate($data)) {
return back()->withErrors(['data' => 'Invalid input']);
}
Immutable Collections:
Vec\map() returns a new collection. Avoid overwriting variables:
// ❌ Bug-prone
$users = Vec\map($users, fn($user) => [...]);
// ✅ Safer
$users = Vec\map($users, fn($user) => [...]);
Fiber Compatibility:
Swoole/ReactPHP without isolation.Async\run() to bridge PSL async with other libraries.Performance:
Type\shape() validation has reflection overhead. Cache types for repeated use:
private Type\Shape $userType;
public function __construct() {
$this->userType = Type\shape([...]);
}
Async Errors:
Async\run()->catch(fn($e) => error_log($e))->ignore() to log uncaught errors.Type Mismatches:
Psalm plugin for static analysis:
composer require --dev php-standard-library/psalm-plugin
Networking:
Async\main(static fn() => TCP\connect('localhost', 8080)->ignore());
Custom Types:
Type\TypeInterface for domain-specific validation:
class EmailType implements TypeInterface {
public function coerce(mixed $value): string { ... }
}
Graph Algorithms:
Graph\Traversal.Async Middleware:
public function handle($request, Closure $next) {
return Async\run(static fn() => $next($request))->ignore();
}
Collection Macros:
Vector/Map:
Vector::macro('groupBy', fn($keyFn) => ...);
Async\run() sparingly in loops to avoid leaks.Type\enum()) require PHP 8.4’s enums.psl namespace is auto-loaded in composer.json.How can I help you explore Laravel packages today?