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.
Str\chr() previously returned an empty string for invalid code points (negative values, surrogates, values above U+10FFFF) because mb_chr() returns false and it was silently cast to string. It now throws Str\Exception\OutOfBoundsException.
Str\from_code_points() had a hand-rolled UTF-8 encoder that silently produced invalid byte sequences; encoding surrogates, wrapping out-of-range values via modulo, and accepting negative inputs. The implementation has been replaced with a simple loop over Str\chr(), making both functions fully consistent. Invalid code points now throw Str\Exception\OutOfBoundsException.
use Psl\Str;
// These all threw no error before, now they throw OutOfBoundsException:
Str\chr(-1);
Str\chr(0xD800); // surrogate
Str\chr(0x110000); // above Unicode max
Str\from_code_points(72, 0xD800, 111); // throws on the surrogate
Valid inputs are unaffected — chr() and from_code_points() produce identical output for all valid Unicode code points (U+0000..U+D7FF, U+E000..U+10FFFF).
All ambiguous function calls have been made explicit. Every call site now uses either a use function import for global PHP functions or namespace\foo() for same-namespace functions.
Str\width(), Str\truncate(), and Str\width_slice() PHPDoc now explicitly states that width is defined by mb_strwidth() / mb_strimwidth(), and cross-references related functions like Str\length(), Str\Grapheme\length(), and Str\Grapheme\slice().usleep() resolution is too coarse for sub-millisecond timing assertions.In PSL 5.x, we focused on the foundational networking stack-TCP, TLS, Unix sockets, UDP, and connection pooling. It was all about getting the low-level plumbing right.
With the 6.x series, we're moving up the stack and diving into protocols. Our ultimate goal is to bring robust support for HTTP/2, DNS, SMTP, WebSockets, and more. PSL 6.1.0 is the crucial first step: it delivers the core infrastructure that all of these upcoming features will rely on.
Everything in 6.1 was built with a clear destination in mind. We are paving the way for:
The components introduced today are the engine for this roadmap. The new H2 connections will drive the HTTP client and server, the new async Cache will back the DNS resolver, and the new Compression system will handle content-encoding seamlessly.
We've added streaming compression and decompression natively to IO handles. By defining your own CompressorInterface or DecompressorInterface (brotli, gzip, zstd, etc.), PSL gives you four handle decorators to wire them directly into the IO system:
CompressingReadHandle & CompressingWriteHandleDecompressingReadHandle & DecompressingWriteHandleWe've also included compress() and decompress() convenience functions for simple, one-shot operations. Under the hood, write handles implement the new BufferedWriteHandleInterface for explicit flushing and cancellation, while read handles accept configurable chunk sizes. Compressors automatically reset after calling finish(), making them easily reusable across streams.
This release includes a complete RFC 7541 encoder and decoder, featuring static table lookups, dynamic table indexing, and Huffman coding.
We wanted to be absolutely certain of its reliability, so we tested it against 14 independent implementations from the http2jp test suite. That translates to 1,172 tests and over 102,000 assertions, covering every encoding variant and edge case with full roundtrip verification.
We’ve shipped full support for the HTTP/2 binary framing protocol (RFC 9113), plus key extensions.
To keep responsibilities clean, connections are split by role. ServerConnection handles client prefaces, response headers, server pushes, Alt-Svc, and ORIGIN. Meanwhile, ClientConnection manages connection prefaces, priority signaling, and extended CONNECT.
Comprehensive Frame Support:
ALTSVC (RFC 7838) for HTTP/3 migration signaling.ORIGIN (RFC 8336) for connection coalescing.PRIORITY_UPDATE (RFC 9218) for extensible prioritization.Smart, Async-Native Flow Control:
We've designed flow control to get out of your way. sendAllData() automatically chunks payloads by window size and waits for updates. waitForSendWindow() suspends the fiber entirely (zero polling!) and resumes exactly when the window opens. Multiple fibers can wait on different streams with independent cancellation, and connection-level updates efficiently wake all relevant waiters.
Additional H2 Features:
getStreamState(), activeStreamCount(), isConnected()).ServerConfiguration and ClientConfiguration using fluent with* builders.We're introducing an async-safe, in-memory LRU cache. The standout feature here is per-key atomicity powered by KeyedSequence.
If two fibers request the same cache key simultaneously, only one will compute the result. The other simply waits and receives the cached value—preventing cache stampedes and eliminating duplicate work.
LocalStore: A bounded LRU cache with a configurable max size and TTL support. It uses an event loop timer for proactive expiration, dropping to zero overhead when there are no expiring entries.NullStore: A dummy cache that never stores and always recomputes. It's perfect for testing or temporarily disabling caching without having to modify your calling code.We've introduced BufferedWriteHandleInterface, which extends the standard WriteHandleInterface with a flush() method. This is essential for handles that buffer data internally (like the new compression writers) and need an explicit "send everything now" trigger with full cancellation support.
No code changes. This release improves the release infrastructure.
6.0.x) to the tag before splitting, ensuring split repos always receive the correct commits for patch releases.See CHANGELOG.md for details.
Patch release fixing a bug in IO\Reader that affected non-blocking stream reads (TLS, TCP, etc.).
Reader::readUntil() and Reader::readUntilBounded() assumed that an empty read() meant end-of-stream. On non-blocking handles (TLS, TCP, Unix sockets), read() can return empty before data arrives. This caused readLine() to return the entire stream content as a single string instead of splitting into individual lines.
This bug affected any code using IO\Reader with network streams. If you were using readLine(), readUntil(), or readUntilBounded() on a non-blocking stream and getting unexpected results, this is the fix.
Documentation source links (See src/Psl/Default/ for the full API) now link to packages/default/src/Psl/Default/ instead of the non-existent top-level src/Psl/Default/.
See CHANGELOG.md for details.
PSL 6.0 is the biggest release in the project's history. New home, new packages, new capabilities.
PSL has moved. The repository, the organization, the website - everything has a new address:
azjezz/psl)psl.carthage.software)php-standard-library/php-standard-library (was azjezz/psl)The azjezz/psl package is now abandoned. Run composer require php-standard-library/php-standard-library to switch.
The namespace has not changed. It is Psl\, and it will always remain Psl\.
PSL is now split into 61 independently installable packages. You no longer need to pull in the entire library.
Need just type-safe coercion? composer require php-standard-library/type
Building an async TCP server? composer require php-standard-library/tcp
Working with URIs? composer require php-standard-library/uri
Every package declares its own dependencies, so you only get what you actually use. The full library install still works for those who want everything:
composer require php-standard-library/php-standard-library
All 61 packages live under the php-standard-library GitHub organization, each with its own read-only split repository for Composer.
Full RFC-compliant resource identifier handling:
Standalone RFC 3492 Punycode encoding and decoding for internationalized domain names.
A new cancellation system replaces the old Duration $timeout pattern across all async/IO operations:
CancellationTokenInterface - base contractTimeoutCancellationToken - auto-cancels after a durationSignalCancellationToken - manually triggered cancellationLinkedCancellationToken - cancelled when either of two inner tokens firesTaskGroup and WaitGroup for structured concurrencyQuotedPrintable and EncodedWord encoding (RFC 2045, RFC 2047)TLS\Listener for wrapping any listener with TLSTCP\RestrictedListener for IP/CIDR-based access controlNetwork\CompositeListener for accepting from multiple listenersBufferedReadHandleInterface with readByte(), readLine(), readUntil()This is a major release with breaking changes. The most impactful:
Cancellation replaces timeouts. All null|Duration $timeout parameters are now CancellationTokenInterface $cancellation = new NullCancellationToken().
// Before (5.x)
$data = $reader->read(timeout: Duration::seconds(5));
// After (6.0)
$data = $reader->read(cancellation: new Async\TimeoutCancellationToken(Duration::seconds(5)));
Naming conventions. All variables, parameters, and properties now use $camelCase.
Configuration objects. TCP\listen(), TCP\connect(), Unix\listen(), UDP\Socket::bind(), and Socks\Connector now accept configuration objects instead of individual parameters.
Removed timeout exceptions. IO\Exception\TimeoutException, Network\Exception\TimeoutException, Process\Exception\TimeoutException, and Shell\Exception\TimeoutException are removed. Use Async\Exception\CancelledException instead.
TLS renamed. TLS\ServerConfig -> TLS\ServerConfiguration, TLS\ClientConfig -> TLS\ClientConfiguration.
See the full CHANGELOG for the complete list.
RetryConnector backoff sleep now respects cancellation tokensIO\write(), IO\write_line(), IO\write_error(), IO\write_error_line(), and Str\format() no longer crash when the message contains % characters and no arguments are passedPSL is built by its community. Thank you to everyone who contributed code, reported bugs, sponsored the project, or simply used it in production.
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\json_decoded() and Type\nullish()Two new type coercions from [@veewee](https://github.com/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): matches null, 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: https://github.com/azjezz/psl/compare/5.4.0...5.5.0
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);
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
Full Changelog: https://github.com/azjezz/psl/compare/5.3.0...5.4.0
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: https://github.com/azjezz/psl/compare/5.2.0...5.3.0
IP ComponentImmutable, 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'
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')); // true
Documentation: psl.carthage.software/5.2.0/ | IP | CIDR
Full Changelog: https://github.com/azjezz/psl/compare/5.1.0...5.2.0
TLS\TCPConnector implements TCP\ConnectorInterface, enabling TLS connections to be pooled via TCP\SocketPool. Pass a TCPConnector to SocketPool and get automatic connection reuse without repeated TLS handshakes.TLS\StreamInterface now extends TCP\StreamInterface, making TLS streams compatible with any API that accepts TCP streams.$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: https://github.com/azjezz/psl/compare/5.0.0...5.1.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+.
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);
// true
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'
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'); // false
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'
}
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):
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)
Optimizations across Vec, Dict, Str, Iter, Type, and more components. Benchmarks show up to 100% improvement in certain functions.
A new documentation website is available at psl.carthage.software.
Network, TCP, Unix)Psl\Env\temp_dir() now always returns a canonicalized pathFilesystem\create_temporary_file() now canonicalizes the temporary directory pathVec\range() now uses strict comparison for float precisionDocumentation: https://psl.carthage.software/
Full Changelog: https://github.com/azjezz/psl/compare/4.3.0...5.0.0
uuid type by @gsteel in https://github.com/azjezz/psl/pull/568Full Changelog: https://github.com/azjezz/psl/compare/4.2.1...4.3.0
Full Changelog: https://github.com/azjezz/psl/compare/4.2.0...4.2.1
Full Changelog: https://github.com/azjezz/psl/compare/4.1.0...4.2.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.
make to just by @azjezz in https://github.com/azjezz/psl/pull/544A 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.
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.
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 namesEach 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');
just: Migrated from make to just for improved cross-platform compatibility and developer experiencemago updated to 1.0.0-beta.32actions/setup-just bumped from v2 to v3[@veewee](https://github.com/veewee) has been added to GitHub sponsors
Full Changelog: 4.0.1...4.1.0
[@var](https://github.com/var) tags from constants by @azjezz in https://github.com/azjezz/psl/pull/533Full Changelog: https://github.com/azjezz/psl/compare/4.0.0...4.0.1
This release marks a major update for the PHP Standard Library, introducing several breaking changes aimed at improving strictness, consistency, and developer experience. The library is now much stricter in its type handling and serialization, and the static analysis has been migrated to a more powerful tool.
Psl\Result\wrap() No Longer Unwraps Nested ResultsTo reduce surprising behavior, Psl\Result\wrap() will no longer automatically unwrap a Result instance returned from within the wrapped closure. It will now consistently wrap the closure's return value in a Success object, even if that value is itself a Result.
Before:
$result = Psl\Result\wrap(fn() => Psl\Result\wrap(fn() => 'hello'));
// $result is Success('hello')
After:
$result = Psl\Result\wrap(fn() => Psl\Result\wrap(fn() => 'hello'));
// $result is Success(Success('hello'))
The JSON serialization for Map and Set collections has been changed to provide a more natural and predictable representation.
Psl\Collection\Map and Psl\Collection\MutableMap now serialize to a JSON object ({...}) in all cases, even when empty or when containing sequential integer keys. This ensures a consistent key-value structure.Psl\Collection\Set and Psl\Collection\MutableSet now serialize to a JSON array ([...]), which better represents a collection of unique values.A large number of intersection interfaces in the Psl\IO and Psl\File namespaces have been removed to simplify the component's hierarchy. These interfaces were combinations of other interfaces (e.g., CloseReadHandleInterface as CloseHandleInterface&ReadHandleInterface).
Developers should now use union types or explicit interface checks where needed. For example, instead of type-hinting CloseReadHandleInterface, use CloseHandleInterface&ReadHandleInterface.
Psl\sequence() Function RemovedThe Psl\sequence() function, which returned the last argument it received, has been removed as it provided little practical value.
Psl\Type\container()A new container<Tk, Tv> type has been added to assert that a value is an iterable with specific key and value types. Unlike iterable(), it will coerce the value to an array.
Psl\Type\int_range()The new int_range(int $min, int $max) type allows asserting that a value is an integer within a specified range. It supports the same coercions as the int() type.
Psl\Type\always_assert()This new type wrapper ensures that the inner type is always asserted, even during coercion. This is useful for disabling coercion on a per-type basis.
Psl\Iter\search_with_keys() and search_with_keys_opt()These functions provide the ability to search an iterable using a predicate that receives both the key and the value, returning the value of the first match.
Psalm to Mago for static analysis, resulting in stricter type checking and improved code quality. Many internal [@psalm-suppress](https://github.com/psalm-suppress) annotations have been removed.Dict, Iter, Regex, and Vec have been enhanced with conditional types. This allows static analysis tools to infer more precise types, especially for non-empty arrays and regex match results, reducing the need for user-land assertions.[@psalm-pure](https://github.com/psalm-pure) have been replaced with the more generic [@pure](https://github.com/pure) to reduce dependency on a single tool.mago has been updated to 1.0.0-beta.15.actions/checkout has been bumped from v4 to v5 in GitHub Actions.Full Changelog: https://github.com/azjezz/psl/compare/3.3.0...4.0.0
0.12.0 by @azjezz in https://github.com/azjezz/psl/pull/509Full Changelog: https://github.com/azjezz/psl/compare/3.2.0...3.3.0
Full Changelog: https://github.com/azjezz/psl/compare/3.1.0...3.2.0
Full Changelog: https://github.com/azjezz/psl/compare/3.0.2...3.1.0
Full Changelog: https://github.com/azjezz/psl/compare/3.0.1...3.0.2
Full Changelog: https://github.com/azjezz/psl/compare/3.0.0...3.0.1
stdClass to array<TKey, TValue> by @Ocramius in https://github.com/azjezz/psl/pull/437json_encode never returns an empty string by @gsteel in https://github.com/azjezz/psl/pull/441apply closure should be able to return mixed by @devnix in https://github.com/azjezz/psl/pull/442Locale::default() function to retrieve default locale by @azjezz in https://github.com/azjezz/psl/pull/447Default component by @azjezz in https://github.com/azjezz/psl/pull/449ReadHandle::reachedEndOfDataSource by @azjezz in https://github.com/azjezz/psl/pull/452min(), max(), median() and mean() by @simPod in https://github.com/azjezz/psl/pull/464isSucceeded() and isFailed() by @simPod in https://github.com/azjezz/psl/pull/466isSucceeded() and isFailed() (#466) by @simPod in https://github.com/azjezz/psl/pull/469Iter\apply (Closure(T): void) $function to (Closure(T): mixed) by @devnix in https://github.com/azjezz/psl/pull/471Vec versions of the unique functions by @BackEndTea in https://github.com/azjezz/psl/pull/472Result::unwrapOr() by @simPod in https://github.com/azjezz/psl/pull/470first_opt(), first_key_opt(), last_opt(), last_key_opt() and search_opt() by @simPod in https://github.com/azjezz/psl/pull/467DateTime component by @azjezz in https://github.com/azjezz/psl/pull/446param-immediately-invoked-callable in Option by @klifoth in https://github.com/azjezz/psl/pull/475Set, SetInterface, MutableSet, and MutableSetInterface by @azjezz in https://github.com/azjezz/psl/pull/482Full Changelog: https://github.com/azjezz/psl/compare/2.9.0...3.0.0
Full Changelog: https://github.com/azjezz/psl/compare/2.9.0...2.9.1
Option::zip(), Option::zipWith() and Option::unzip() methods by @devnix in https://github.com/azjezz/psl/pull/434Option::proceed() method by @devnix in https://github.com/azjezz/psl/pull/433Option::apply() method by @devnix in https://github.com/azjezz/psl/pull/426Full Changelog: https://github.com/azjezz/psl/compare/2.8.0...2.9.0
Full Changelog: https://github.com/azjezz/psl/compare/2.7.0...2.8.0
Base64\Variant enum to support encoding/decoding different variants - #408 by @GashmobOption<never> for Option::none() - #415 by @devnixFull Changelog: https://github.com/azjezz/psl/compare/2.6.0...2.7.0
converted type. by @veewee in https://github.com/azjezz/psl/pull/405numeric-string type by @veewee in https://github.com/azjezz/psl/pull/406Full Changelog: https://github.com/azjezz/psl/compare/2.5.0...2.6.0
Full Changelog: https://github.com/azjezz/psl/compare/2.4.1...2.5.0
positive_int() by @dragosprotung in https://github.com/azjezz/psl/pull/400Full Changelog: https://github.com/azjezz/psl/compare/2.4.0...2.4.1
Full Changelog: https://github.com/azjezz/psl/compare/2.3.1...2.4.0
reproduce and range return type is always non-empty-list @dragosprotung in https://github.com/azjezz/psl/pull/383Full Changelog: https://github.com/azjezz/psl/compare/2.3.0...2.3.1
Full Changelog: https://github.com/azjezz/psl/compare/2.2.0...2.3.0
Full Changelog: https://github.com/azjezz/psl/compare/2.1.0...2.2.0
Psl\Type\unit_enum function - [@19d1230](https://github.com/azjezz/psl/commit/19d123074546cc3ebfca18ad666f100e7fad0658) by @azjezzPsl\Type\backed_enum function - [@19d1230](https://github.com/azjezz/psl/commit/19d123074546cc3ebfca18ad666f100e7fad0658) by @azjezzPsl\Type\mixed_vec function - #362 by @BackEndTeaPsl\Type\mixed_dict function - #362 by @BackEndTeaPsl\Type\vec performance - #364 by @BackEndTeaPsl\Type\float, and Psl\Type\num - #367 by @bcremerrevolt-php/event-loop to 1.0.0 - [@c7bf866](https://github.com/azjezz/psl/commit/c7bf866a362b9528934a758981da718408ec15d4) by @azjezzFull Changelog: https://github.com/azjezz/psl/compare/2.0.4...2.1.0
How can I help you explore Laravel packages today?