Releases: azjezz/psl
Crown 5.5.0
PSL 5.5.0
IO: Bounded Reads
Reader::readUntilBounded() reads from a handle until a suffix is found, just like readUntil(), but enforces a maximum byte limit. If the suffix is not encountered within $max_bytes, an IO\Exception\OverflowException is thrown.
This is essential when reading from untrusted sources. for example, capping HTTP header lines so a malicious client cannot exhaust memory by sending an endless line:
use Psl\IO;
$reader = new IO\Reader($connection);
// Read a header line, but never buffer more than 8KB
$line = $reader->readUntilBounded("\r\n", max_bytes: 8192);Type: Type\json_decoded() and Type\nullish()
Two new type coercions from @veewee:
-
Type\json_decoded(TypeInterface $inner): accepts a JSON string and transparently decodes it, then coerces the result through$inner. Useful for APIs and form fields that pass structured data as JSON strings. -
Type\nullish(TypeInterface $inner): matchesnull, the absence of a key (for shape fields), and the inner type. Ideal for optional-and-nullable shape fields where "missing" and "null" should be treated the same.
Documentation: psl.carthage.software/ | IO | Type
Full Changelog: 5.4.0...5.5.0
Crown 5.4.0
PSL 5.4.0
TCP: Backlog Configuration and Batch Accept
TCP\listen() now accepts a backlog parameter (default 512) to configure the OS-level queue of pending connections. The listener also now drains the accept backlog in a loop instead of accepting one connection per event-loop tick, dramatically improving throughput under high connection rates.
use Psl\TCP;
$listener = TCP\listen('127.0.0.1', 8080, backlog: 4096);New: Dict\{filter_nonnull_by, map_nonnull}, Vec\{filter_nonnull_by, map_nonnull}
New collection helpers for filtering and mapping with null-awareness.
Documentation: psl.carthage.software/ | TCP
New Contributors
Full Changelog: 5.3.0...5.4.0
Crown 5.3.0
PSL 5.3.0
New: IO\spool()
Create a handle that writes to memory until a threshold is reached, then transparently spills to a temporary file on disk. Useful for buffering data of unknown size without risking excessive memory usage.
use Psl\IO;
$handle = IO\spool(); // default 2MB threshold
$handle->writeAll($data);
$handle->seek(0);
$contents = $handle->readAll();
$handle->close();
// Custom threshold: spool to disk after 64 bytes
$small = IO\spool(maxMemory: 64);The returned handle implements CloseHandleInterface, SeekHandleInterface, ReadHandleInterface, WriteHandleInterface, and StreamHandleInterface.
Documentation: psl.carthage.software/5.3.0/ | IO
Full Changelog: 5.2.0...5.3.0
Crown 5.2.0
PSL 5.2.0
New: IP Component
Immutable, binary-backed value object for IPv4 and IPv6 addresses. Parse, classify, compare, and format IP addresses with a clean API.
use Psl\IP\Address;
$addr = Address::parse('192.168.1.1');
$addr->family; // Family::V4
$addr->isPrivate(); // true
$addr->isGlobalUnicast(); // false
$addr->toArpaName(); // '1.1.168.192.in-addr.arpa'
$v6 = Address::v6('2001:db8::1');
$v6->toString(); // '2001:db8::1'
$v6->toExpandedString(); // '2001:0db8:0000:0000:0000:0000:0000:0001'Improved: CIDR\Block::contains()
Block::contains() now accepts string|IP\Address:
use Psl\CIDR\Block;
use Psl\IP\Address;
$block = new Block('192.168.1.0/24');
$block->contains(Address::v4('192.168.1.100')); // trueDocumentation: psl.carthage.software/5.2.0/ | IP | CIDR
Full Changelog: 5.1.0...5.2.0
Crown 5.1.0
PSL - Crown 5.1.0
Features
- TLS connection pooling: New
TLS\TCPConnectorimplementsTCP\ConnectorInterface, enabling TLS connections to be pooled viaTCP\SocketPool. Pass aTCPConnectortoSocketPooland get automatic connection reuse without repeated TLS handshakes. TLS\StreamInterfacenow extendsTCP\StreamInterface, making TLS streams compatible with any API that accepts TCP streams.
Example
$pool = new TCP\SocketPool(
new TLS\TCPConnector(
new TCP\Connector(),
new TLS\Connector($config),
),
);
$stream = $pool->checkout('example.com', 443);
// ... use stream ...
$pool->checkin($stream);
// Next checkout reuses the TLS connection
$stream = $pool->checkout('example.com', 443);Full Changelog: 5.0.0...5.1.0
Crown 5.0.0
PSL - Crown 5.0
PSL 5.0 - nicknamed Crown - is the biggest release of the PHP Standard Library to date, introducing 10 new components, a complete networking stack rewrite, and significant performance improvements across the board.
Requires PHP 8.4+.
Crypto
Full-featured cryptography built on libsodium: symmetric and asymmetric encryption, digital signatures, AEAD, key derivation (KDF, HKDF), key exchange, and stream ciphers, all with a type-safe, hard-to-misuse API.
use Psl\Crypto\Symmetric;
$key = Symmetric\generate_key();
$ciphertext = Symmetric\seal('Hello, World!', $key);
$plaintext = Symmetric\open($ciphertext, $key);
// 'Hello, World!'use Psl\Crypto\Signing;
$keyPair = Signing\generate_key_pair();
$signature = Signing\sign('This message is authentic.', $keyPair->secretKey);
$valid = Signing\verify($signature, 'This message is authentic.', $keyPair->publicKey);
// trueBinary
Structured binary data parsing and encoding with a fluent Reader/Writer API. Read and write integers, floats, and strings in any byte order. Ideal for building protocol parsers and working with binary formats.
use Psl\Binary\{Reader, Writer, Endianness};
$data = new Writer(endianness: Endianness::Big)
->u8(1) // version
->u16(0x0042) // type
->u32(5) // payload length
->bytes('Hello') // payload
->toString();
$reader = new Reader($data, Endianness::Big);
$version = $reader->u8(); // 1
$type = $reader->u16(); // 0x0042
$length = $reader->u32(); // 5
$payload = $reader->bytes($length); // 'Hello'Networking Stack Rewrite
The Network, TCP, TLS, UDP, Unix, CIDR, and Socks components were rewritten from scratch with connection pooling, retry logic, socket pairs, and first-class TLS support.
use Psl\TCP;
$listener = TCP\listen('127.0.0.1');
$connection = $listener->accept();
$data = $connection->readAll();
$connection->writeAll($data);use Psl\TLS;
$tls = TLS\connect('example.com', 443);
$tls->writeAll("GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
$tls->shutdown();
$response = $tls->readAll();use Psl\CIDR;
$block = new CIDR\Block('192.168.1.0/24');
$block->contains('192.168.1.100'); // true
$block->contains('192.168.2.1'); // falseProcess
Async process management with non-blocking I/O, inspired by Rust's Command API. A safer, higher-level replacement for proc_open and friends. Psl\Shell\execute is now powered by it under the hood.
use Psl\Process\Command;
$output = Command::create('echo')
->withArguments(['Hello', 'from', 'process'])
->output();
if ($output->status->isSuccessful()) {
$output->stdout; // 'Hello from process'
}Terminal & Ansi
A full terminal UI framework: buffered rendering, layouts, widgets, keyboard/mouse event handling, and ANSI styling. Build rich interactive TUI applications entirely in PHP.
use Psl\Async;
use Psl\Terminal;
use Psl\Terminal\{Event, Widget};
Async\main(static function (): int {
$app = Terminal\Application::create(new MyState(), title: 'My App');
$app->on(Event\Key::class, static function (Event\Key $event) use ($app): void {
if ($event->is('ctrl+c')) {
$app->stop();
}
});
return $app->run(static function (Terminal\Frame $frame, MyState $state): void {
Widget\Paragraph::new([
Widget\Line::new([Widget\Span::raw('Hello, World!')]),
])->render($frame->rect(), $frame->buffer());
});
});Here's a system monitor UI demo built entirely with Psl\Terminal (source):
You can even build fully functional terminal games (source):
DateTime Additions
New Period and Interval types for representing and manipulating durations and time spans.
use Psl\DateTime;
$period = DateTime\Period::fromParts(years: 1, months: 6, days: 15);
$period->toIso8601(); // 'P1Y6M15D'
$start = DateTime\DateTime::fromParts(DateTime\Timezone::UTC, 2024, 3, 15);
$end = DateTime\DateTime::fromParts(DateTime\Timezone::UTC, 2025, 7, 20);
$between = DateTime\Period::between($start, $end);
// 1 year(s), 4 month(s), 5 day(s)Performance
Optimizations across Vec, Dict, Str, Iter, Type, and more components. Benchmarks show up to 100% improvement in certain functions.
Documentation
A new documentation website is available at psl.carthage.software.
Breaking Changes
- PHP 8.4 is now the minimum required version
- Complete networking stack rewrite (
Network,TCP,Unix) Psl\Env\temp_dir()now always returns a canonicalized pathFilesystem\create_temporary_file()now canonicalizes the temporary directory path- Migrated to PHPUnit 13
Bug Fixes
Vec\range()now uses strict comparison for float precision
Documentation: https://psl.carthage.software/
Full Changelog: 4.3.0...5.0.0
Noise 4.3.0
What's Changed
- chore: prepare for the next version of mago by @azjezz in #569
- chore: update to mago 1.6.0 by @azjezz in #570
- Fix PHPDoc return type annotation by @mitelg in #571
- Add
uuidtype by @gsteel in #568 - refactor(phpunit): resolve test case naming deprecations by @simPod in #573
- feat: introduce Either by @simPod in #572
- fix(shell): terminate the process on timeout by @azjezz in #574
New Contributors
Full Changelog: 4.2.1...4.3.0
Noise 4.2.1
What's Changed
- Release the handles before changing permissions when copying files by @dragosprotung in #550
- fix(iter): do not narrow down seek($offset) type by @azjezz in #552
- chore: apply mago fixes by @azjezz in #554
- chore(ga): bump actions/checkout from 5 to 6 by @dependabot[bot] in #555
- chore: update dependencies by @azjezz in #559
- revert(option): revert #475 by @devnix in #560
- chore(ga): bump actions/cache from 4 to 5 by @dependabot[bot] in #561
- chore: ignore mago warnings about catching errors by @azjezz in #562
- chore: ignore mago warnings about catching errors by @azjezz in #563
- chore(deps): update to mago 1.0.0 by @azjezz in #564
- chore(deps): update mago to 1.0.3 by @azjezz in #565
- fix(tree): explicit type precedence by @azjezz in #566
- chore: update dependencies by @azjezz in #567
Full Changelog: 4.2.0...4.2.1
Noise 4.2.0
Noise 4.1.0
Psl Noise - 4.1.0
This release introduces two major new data structure components to the PHP Standard Library: Tree and Graph. These additions bring powerful hierarchical and relational data manipulation tools to PHP developers with PSL's signature type-safe API.
What's Changed
- chore: upgrade to mago beta-31 by @azjezz in #538
- chore: upgrade to mago beta-32 by @azjezz in #539
- feat: add reflection-based type functions for class members by @azjezz in #543
- chore: migrate from
maketojustby @azjezz in #544 - chore(ga): bump extractions/setup-just from 2 to 3 by @dependabot[bot] in #545
- feat: add Tree component for hierarchical data structures by @azjezz in #546
- feat: add Graph component with directed and undirected graph support by @azjezz in #547
- chore: add veewee to GitHub sponsors by @azjezz in #548
✨ New Features
🌳 Psl\Tree Component
A comprehensive tree data structure implementation for working with hierarchical data.
Features: Immutable tree nodes, functional operations (map, filter, reduce, fold), traversal algorithms (pre-order, post-order, level-order), search utilities, and conversion functions.
use Psl\Tree;
// Create and manipulate trees
$tree = Tree\tree('root', [
Tree\leaf('child1'),
Tree\tree('child2', [Tree\leaf('grandchild')]),
]);
// Functional operations
$doubled = Tree\map($tree, fn($x) => $x . '!');
$values = Tree\pre_order($tree); // ['root', 'child1', 'child2', 'grandchild']
$count = Tree\count($tree); // 4
// Build from database records
$tree = Tree\from_list(
$records,
fn($r) => $r['id'],
fn($r) => $r['parent_id'],
fn($r) => $r['name']
);Use Cases: File systems, organizational hierarchies, DOM structures, category trees, menu systems.
🕸️ Psl\Graph Component
A robust graph data structure implementation supporting both directed and undirected graphs with algorithms for analysis and pathfinding.
Features: Immutable graphs, weighted/unweighted edges, BFS/DFS traversal, shortest path (Dijkstra/BFS), topological sorting, cycle detection, flexible node types.
use Psl\Graph;
// Create and traverse graphs
$graph = Graph\directed();
$graph = Graph\add_edge($graph, 'A', 'B');
$graph = Graph\add_edge($graph, 'B', 'C');
$path = Graph\shortest_path($graph, 'A', 'C'); // ['A', 'B', 'C']
$sorted = Graph\topological_sort($graph); // ['A', 'B', 'C']
// Weighted graphs
$graph = Graph\add_edge($graph, 'NYC', 'Boston', 215);
$graph = Graph\add_edge($graph, 'NYC', 'Philadelphia', 95);
$graph = Graph\add_edge($graph, 'Philadelphia', 'Boston', 310);
$route = Graph\shortest_path($graph, 'NYC', 'Boston'); // ['NYC', 'Boston']
// Undirected graphs
$social = Graph\undirected();
$social = Graph\add_edge($social, 'Alice', 'Bob');Use Cases: Dependency resolution, route finding, social networks, state machines, task scheduling.
🔍 Reflection-Based Type Functions
New type functions for runtime validation of class members using PHP's reflection API:
Type\constant_name_of()- Validate constant namesType\enum_case_of()- Validate enum case namesType\method_name_of()- Validate method names (case-insensitive)Type\property_name_of()- Validate property names
Each includes visibility-specific variants (public_*, protected_*, private_*).
use Psl\Type;
Type\method_name_of(MyClass::class)->assert('someMethod');
Type\property_name_of(MyClass::class)->assert('someProperty');
Type\public_constant_name_of(MyClass::class)->assert('SOME_CONSTANT');🛠️ Tooling Updates
- Migration to
just: Migrated frommaketojustfor improved cross-platform compatibility and developer experience
⬆️ Dependency Updates
magoupdated to1.0.0-beta.32actions/setup-justbumped from v2 to v3- Various development dependency updates
🤝 Contributors
@veewee has been added to GitHub sponsors
Full Changelog: 4.0.1...4.1.0

