ipl/stdlib provides reusable building blocks for Icinga PHP libraries and
applications. It covers declarative filtering, event emission, string and
iterable utilities, lightweight data and message containers, and a
stable priority queue.
The recommended way to install this library is via Composer:
composer require ipl/stdlibipl/stdlib requires PHP 8.2 or later with the openssl extension.
Build composable filter trees with ipl\Stdlib\Filter and evaluate them
against arrays or objects:
use ipl\Stdlib\Filter;
$filter = Filter::all(
Filter::equal('problem', '1'),
Filter::none(Filter::equal('handled', '1')),
Filter::like('service', 'www.*')
);
$row = [
'problem' => '1',
'handled' => '0',
'service' => 'www.icinga.com',
];
if (Filter::match($filter, $row)) {
// The row matches the rule set.
}Available condition factories: equal, unequal, like, unlike,
greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual.
Available logical factories: all, any, none, not.
When an object needs to collect filter conditions over time, use the Filters
trait. It complements the Filterable contract and exposes filter(),
orFilter(), notFilter(), and orNotFilter():
use ipl\Stdlib\Contract\Filterable;
use ipl\Stdlib\Filter;
use ipl\Stdlib\Filters;
class Query implements Filterable
{
use Filters;
}
$query = (new Query())
->filter(Filter::equal('problem', '1'))
->orNotFilter(Filter::equal('handled', '1'));
$filter = $query->getFilter();The Events trait wraps Evenement and
adds event validation. Declare event name constants on the class to give
callers a typo-safe API and to let isValidEvent() enforce an explicit
allow-list:
use ipl\Stdlib\Events;
class Connection
{
use Events;
public const ON_CONNECT = 'connected';
public const ON_DISCONNECT = 'disconnected';
protected function isValidEvent($event): bool
{
return in_array($event, [static::ON_CONNECT, static::ON_DISCONNECT], true);
}
public function open(): void
{
// ... connect ...
$this->emit(self::ON_CONNECT, [$this]);
}
public function close(): void
{
// ... disconnect ...
$this->emit(self::ON_DISCONNECT, [$this]);
}
}
$conn = new Connection();
$conn->on(Connection::ON_CONNECT, function (Connection $c): void {
echo "Connected\n";
});
$conn->on(Connection::ON_DISCONNECT, function (Connection $c): void {
echo "Disconnected\n";
});
$conn->open();
$conn->close();Str offers string utilities that complement PHP's built-in functions.
It converts between naming conventions, splits and trims in one step, and
provides startsWith with case-insensitive matching.
use ipl\Stdlib\Str;
// Convert snake_case or kebab-case identifiers to camelCase:
Str::camel('host_name'); // 'hostName'
Str::camel('display-name'); // 'displayName'
// Split on a delimiter and trim whitespace from every part in one pass:
Str::trimSplit(' foo , bar , baz '); // ['foo', 'bar', 'baz']
Str::trimSplit('root:secret', ':'); // ['root', 'secret']
// Always return exactly $limit parts: pads with null if the delimiter is
// absent, and fold any remainder into the last part if there are more
// separators than expected:
[$user, $pass] = Str::symmetricSplit('root', ':', 2); // ['root', null]
[$user, $pass] = Str::symmetricSplit('root:secret:extra', ':', 2); // ['root', 'secret:extra']
// Case-insensitive prefix check:
Str::startsWith('Foobar', 'foo', caseSensitive: false); // true
Str::startsWith('foobar', 'foo'); // trueSeq searches arrays, iterators, and generators by value, key, or callback
without first materializing them into arrays. When the second argument to
find or contains is a non-callable, it is compared by value; pass a
closure to match by predicate instead:
use ipl\Stdlib\Seq;
$users = [
'alice' => 'admin',
'bob' => 'viewer',
];
Seq::contains($users, 'viewer'); // true
[$key, $value] = Seq::find($users, 'admin'); // ['alice', 'admin']
// Match by predicate — returns as soon as a result is found:
[$key, $value] = Seq::find($users, fn(string $role): bool => $role !== 'admin'); // ['bob', 'viewer']use function ipl\Stdlib\iterable_key_first;
use function ipl\Stdlib\iterable_value_first;
$map = [
'id' => 42,
'name' => 'Alice',
];
iterable_key_first($map); // 'id'
iterable_value_first($map); // 42
// Works with generators and iterators — does not require an array:
iterable_key_first(new ArrayIterator(['a' => 1])); // 'a'
iterable_key_first([]); // nullyield_groups partitions a pre-sorted traversable into named groups.
The callback must return at least the grouping criterion, but it can
also return a custom value and key. The traversable must be sorted
by the grouping criterion before being passed in; results are undefined
otherwise:
use function ipl\Stdlib\yield_groups;
foreach (yield_groups($rows, fn(object $row): string => $row->category) as $category => $items) {
// $items contains all rows for $category.
}Data— mutable key/value storeMessages— collects user-facing messages and supportssprintf-style placeholdersPriorityQueue— extendsSplPriorityQueuewith stable insertion-order for items at equal priority; iterate non-destructively withyieldAll()
See CHANGELOG.md for a list of notable changes.
ipl/stdlib is licensed under the terms of the MIT License.