Common classes that cover a wide range of cases that are used throughout the inanepain libraries.
This section documents the BitmaskEnumTrait used to add bitmask (flags) behaviour to backed `enum`s.
BitmaskEnumTrait provides a concise API for working with integer bitmasks on PHP 8.5+ backed enums. It enables:
-
Combining multiple enum cases into a single integer mask
-
Checking if any or all cases are set in a mask
-
Testing for and modifying individual flags (both statically and via instance helpers)
-
Listing the set enum cases from a mask
Trait location:
-
Namespace:
Inane\Stdlib\Bitmask -
File:
lib/inanepain/stdlib/src/Bitmask/EnumBitmaskTrait.php
-
Use on backed enums with
intvalues only. -
Each enum case should represent a single bit: use powers of two (e.g.
1 << 0,1 << 1,1 << 2, …). -
Masks are plain integers (
int).
Static helpers on the enum using the trait:
-
parseBitmask(mixed $mask): int— Normalises arbitrary input into anintmask (null-safe, defaults to0). -
combine(self …$flags): int— ORs cases to a single mask. -
hasAny(int $mask): bool— True if any defined case is present in the mask. -
hasAll(int $mask): bool— True if all defined cases are present in the mask. -
has(int $mask, self $flag): bool— True if the given case is present in the mask. -
add(int $mask, self $flag): int— Returns a mask with the case added. -
remove(int $mask, self $flag): int— Returns a mask with the case removed. -
list(int $mask): array<self>— Returns the enum cases present in the mask.
Instance helpers on an enum case:
-
in(int $mask): bool— True if this case is present in the mask. -
addTo(int $mask): int— Returns a mask with this case added. -
removeFrom(int $mask): int— Returns a mask with this case removed.
|
Note
|
Internally, hasAny and hasAll use array helpers equivalent to “any”/“all” checks over self::cases().
|
The following example shows a typical permission enum using the trait.
<?php
declare(strict_types=1);
use Inane\Stdlib\Bitmask\BitmaskEnumTrait;
enum Permission: int {
use BitmaskEnumTrait;
case Read = 1 << 0; // 1
case Write = 1 << 1; // 2
case Execute = 1 << 2; // 4
case Delete = 1 << 3; // 8
}
// Build a mask from multiple flags
$mask = Permission::combine(Permission::Read, Permission::Write); // 3
// Normalise input
$mask = Permission::parseBitmask($mask); // still 3
// Check across the enum
$hasAny = Permission::hasAny($mask); // true (at least one flag set)
$hasAll = Permission::hasAll($mask); // false (not all flags set)
// Check a specific flag
$canRead = Permission::has($mask, Permission::Read); // true
$canExec = Permission::has($mask, Permission::Execute); // false
// The same checks via instance helpers
$canRead2 = Permission::Read->in($mask); // true
$canExec2 = Permission::Execute->in($mask); // false
// Modify mask
$mask = Permission::add($mask, Permission::Delete); // add Delete
$mask = Permission::Write->removeFrom($mask); // remove Write
// Enumerate set flags
$set = Permission::list($mask); // [Permission::Read, Permission::Delete]The Output component provides a consistent strategy for converting data into
various formats. It is designed around the OutputInterface and an abstract
base class, allowing for easy expansion and uniform usage across the framework.
The Output tools are located in the Inane\Stdlib\Output namespace.
-
OutputInterface— defines theoutput()method. -
AbstractOutput— base implementation handling input data storage and lazy-processing. -
ArrayOutput— converts input to a PHP array (supports JSON, Serialized, Objects). -
JsonStringOutput— converts input to a JSON string. -
SerializedOutput— converts input to a PHP serialized string. -
XmlOutput— converts input to aSimpleXMLElementobject. -
XmlStringOutput— converts input to an XML string.
All output classes share the same constructor and method:
public function __construct(protected mixed $inputData);
public function output(): mixed;The output() method is lazy-loaded; the processing happens once and the result
is cached in the outputData property.
ArrayOutput is highly flexible. It attempts to detect the input type and convert
it to an array accordingly.
use Inane\Stdlib\Output\ArrayOutput;
// From an object (recursive conversion)
$output = new ArrayOutput($myObject);
$array = $output->output();
// From a JSON string
$output = new ArrayOutput('{"key": "value"}');
$array = $output->output(); // ['key' => 'value']
// From a serialized string
$output = new ArrayOutput(serialize(['a' => 1]));
$array = $output->output(); // ['a' => 1]-
If input is an array, it is returned as is.
-
If input is a string:
-
Tries to decode as JSON.
-
If not JSON, tries to unserialize.
-
If both fail, wraps the string in an array:
[$input].
-
-
If input is an object, it uses
iteratorToArrayDeepto convert it recursively. -
For any other type, it wraps the input in an array:
[$input].
Converts any input into a JSON string using Inane\Stdlib\Json.
use Inane\Stdlib\Output\JsonStringOutput;
$data = ['name' => 'Inane', 'type' => 'Framework'];
$output = new JsonStringOutput($data);
echo $output->output(); // {"name":"Inane","type":"Framework"}Converts input into a PHP serialized string.
use Inane\Stdlib\Output\SerializedOutput;
$output = new SerializedOutput(['a', 'b', 'c']);
echo $output->output(); // a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}Converts input data into a SimpleXMLElement. It uses ArrayOutput internally
to normalize the input before conversion.
use Inane\Stdlib\Output\XmlOutput;
$data = ['user' => ['id' => 1, 'name' => 'John']];
$output = new XmlOutput($data);
$xml = $output->output(); // SimpleXMLElement instanceTo create a new output format, extend AbstractOutput and implement the output() method:
use Inane\Stdlib\Output\AbstractOutput;
class MyCustomOutput extends AbstractOutput {
public function output(): string {
if (!isset($this->outputData)) {
// ... custom conversion logic ...
$this->outputData = "processed data";
}
return $this->outputData;
}
}Helper utilities for merging configuration/options arrays and iterators with
explicit control over how keys are added and/or updated. The Merge tool lives
under Inane\Stdlib\Merge and consists of:
-
MergeTrait— core implementation and convenience helpers -
Merge— small class that exposes the trait as a ready‑to‑use type -
MergeInterface— contract for merge behaviour -
MergeMethod— enum that selects the merge strategy
Use Merge to combine option sets coming from defaults, environment, per‑user
overrides, feature flags, etc. It supports nested structures and works with
both PHP arrays and ArrayAccess/Iterator implementations.
MergeMethod controls how keys are handled during a merge:
-
AddOnly— only add keys that do not exist on the target; existing keys are left unchanged. -
UpdateOnly(default) — only update keys that already exist on the target; new keys are ignored. -
AddAndUpdate— add missing keys and update existing keys (full overlay/recursive replace).
Merging is recursive for nested arrays/objects that are arrays or implement
ArrayAccess/Iterator on both source and target sides.
Namespace: Inane\Stdlib\Merge
Core static method:
Iterator|array MergeTrait::mergeOptionsWithMethod(
MergeMethod $mergeMethod,
Iterator|array $target,
Iterator|array ...$sources
): Iterator|arrayConvenience helpers (static):
-
mergeOptionsWithAddOnly($target, …$sources) -
mergeOptionsWithUpdateOnly($target, …$sources) -
mergeOptionsWithAddAndUpdate($target, …$sources)
Instance method (via Merge or any class using the trait):
public MergeMethod $mergeMethod = MergeMethod::UpdateOnly; // default
public function mergeOptions(Iterator|array $target, Iterator|array ...$sources): Iterator|array<?php
use Inane\Stdlib\Merge\MergeTrait; // used via Merge facade below
use Inane\Stdlib\Merge\Merge;
$defaults = [
'host' => 'localhost',
'port' => 3306,
'flags' => [ 'compress' => false, 'strict' => true ],
];
$env = [
'port' => 3307,
'flags' => [ 'compress' => true ],
'extra' => 'ignored in UpdateOnly',
];
// Update existing keys only (default semantics)
$merged = Merge::mergeOptionsUpdateOnly($defaults, $env);
/* Result:
[
'host' => 'localhost',
'port' => 3307,
'flags' => [ 'compress' => true, 'strict' => true ],
]
*/
// Add missing keys only
$added = Merge::mergeOptionsAddOnly($defaults, ['timeout' => 5]);
// 'timeout' is appended, existing values untouched
// Add and update (full overlay)
$overlay = Merge::mergeOptionsAddAndUpdate($defaults, $env);
// Includes 'extra' and applies nested updates<?php
use Inane\Stdlib\Merge\{Merge, MergeMethod};
$merger = new Merge();
$merger->mergeMethod = MergeMethod::AddAndUpdate; // choose strategy at runtime
$target = [ 'a' => 1, 'b' => ['x' => true] ];
$source = [ 'b' => ['x' => false, 'y' => 2], 'c' => 3 ];
$result = $merger->mergeOptions($target, $source);
// [ 'a' => 1, 'b' => ['x' => false, 'y' => 2], 'c' => 3 ]-
The key existence on the target is determined as follows:
-
arrays:
array_key_exists($key, $target) -
ArrayAccess:$target→offsetExists($key) -
iterables:
isset($target[$key]) -
Recursion happens only when both source and target values at a key are array‑like (
is_arrayorArrayAccess). Otherwise, the source value replaces the target value subject to the chosen strategy. -
Multiple sources are merged left‑to‑right in the order provided.
You can pass objects implementing Iterator and/or ArrayAccess as both target
and sources. Merge will respect their semantics for key existence checks and
assignments, allowing use with custom option containers.
If you receive a user‑provided string (e.g. from config), you can map it to a
MergeMethod case using MergeMethod::tryFromName($name, $ignoreCase = false).
use Inane\Stdlib\Merge\MergeMethod;
$method = MergeMethod::tryFromName('addonly', true) ?? MergeMethod::UpdateOnly;For frontend experiments there is a simple ES module at
public/js/inane/class-lib/MergeOptions.mjs that mirrors the basic behaviour for merging option objects.
Utility class providing static methods for validating and verifying common value types such as booleans, emails, integers, floats, IP addresses, MAC addresses, domains, and regex-matched strings.
All methods wrap PHP’s native filter_var function with a consistent, expressive API and sensible defaults.
Validates and converts a given value into a boolean.
Accepts the same truthy/falsy strings that PHP’s filter_var recognises (e.g. "true", "yes", "1", "on" and their negatives).
public static function boolVerify(mixed $value, bool $nullOnFailure = false): ?bool| Parameter | Type | Description |
|---|---|---|
|
|
The value to validate and convert to boolean. |
|
|
When |
VerifyValue::boolVerify('yes'); // true
VerifyValue::boolVerify('off'); // false
VerifyValue::boolVerify('maybe', true); // nullValidates an email address or an array of email addresses.
When an array is supplied, each element is validated individually, and the method returns an associative array keyed by the original input values.
public static function emailVerify(string|array $value): false|string|array| Parameter | Type | Description |
|---|---|---|
|
|
A single email address string, or an array of email address strings. |
VerifyValue::emailVerify('user@example.com'); // 'user@example.com'
VerifyValue::emailVerify('not-an-email'); // false
VerifyValue::emailVerify(['a@b.com', 'bad', 'c@d.com']); // ['a@b.com' => 'a@b.com', 'bad' => false, 'c@d.com' => 'c@d.com']Validates an integer value with optional range and base constraints.
Supports octal (prefix 0) and hexadecimal (prefix 0x) notation when the corresponding flags are enabled.
public static function integerVerify(mixed $int, mixed $default = false, ?int $min = null, ?int $max = null, bool $allowOctal = false, bool $allowHex = false): bool| Parameter | Type | Description |
|---|---|---|
|
|
The value to validate as an integer. |
|
|
Fallback value returned on validation failure. Pass |
|
|
Optional minimum allowed value (inclusive). |
|
|
Optional maximum allowed value (inclusive). |
|
|
When |
|
|
When |
VerifyValue::integerVerify(42); // true
VerifyValue::integerVerify(42, false, 1, 100); // true
VerifyValue::integerVerify(200, false, 1, 100); // false
VerifyValue::integerVerify('0x1A', false, null, null, false, true); // trueValidates an integer value using a named options array.
A convenience wrapper around integerVerify() that accepts a named options array instead of individual parameters. Unrecognised keys are silently ignored.
public static function intVerify(mixed $int, array $options = []): bool| Parameter | Type | Description |
|---|---|---|
|
|
The value to validate as an integer. |
|
|
Named validation options: |
VerifyValue::intVerify(42, ['min' => 1, 'max' => 100]); // true
VerifyValue::intVerify('0xFF', ['allowHex' => true]); // trueValidates a float value with optional range and thousand-separator support.
public static function floatVerify(mixed $int, mixed $default = false, ?int $min = null, ?int $max = null, bool $acceptFloat = false): bool| Parameter | Type | Description |
|---|---|---|
|
|
The value to validate as a float. |
|
|
Fallback value returned on validation failure. Pass |
|
|
Optional minimum allowed value (inclusive). |
|
|
Optional maximum allowed value (inclusive). |
|
|
When |
VerifyValue::floatVerify(3.14); // true
VerifyValue::floatVerify('1,234.56', false, null, null, true); // true
VerifyValue::floatVerify('abc'); // falseValidates a value against a regular expression pattern.
Returns the original value when it matches the pattern, the $default string when provided and the match fails, or null otherwise.
public static function regexVerify(mixed $value, string $pattern, ?string $default = null): ?string| Parameter | Type | Description |
|---|---|---|
|
|
The value to validate. |
|
|
A valid PCRE regular expression (including delimiters). |
|
|
Optional fallback string returned when validation fails. |
VerifyValue::regexVerify('hello123', '/^[a-z]+\d+$/'); // 'hello123'
VerifyValue::regexVerify('!!!', '/^[a-z]+$/', 'no-match'); // 'no-match'
VerifyValue::regexVerify('!!!', '/^[a-z]+$/'); // nullValidates a domain name, optionally enforcing strict hostname rules.
public static function domainVerify(mixed $value, bool $hostname = false): ?string| Parameter | Type | Description |
|---|---|---|
|
|
The value to validate as a domain name. |
|
|
When |
VerifyValue::domainVerify('example.com'); // 'example.com'
VerifyValue::domainVerify('-bad.com', true); // null
VerifyValue::domainVerify('not a domain'); // nullValidates an IP address with configurable version and range policies.
By default, both IPv4 and IPv6 addresses are accepted. Passing false for one version while leaving the other as true restricts validation to the remaining version.
public static function ipVerify(mixed $value, bool $allowV4 = true, bool $allowV6 = true, bool $denyPrivate = false, bool $denyReserved = false, bool $globalOnly = false): ?string| Parameter | Type | Description |
|---|---|---|
|
|
The value to validate as an IP address. |
|
|
Accept IPv4 addresses (default |
|
|
Accept IPv6 addresses (default |
|
|
Reject private-range addresses (e.g. |
|
|
Reject reserved-range addresses (e.g. |
|
|
Accept only globally routable addresses. |
VerifyValue::ipVerify('192.168.1.1'); // '192.168.1.1'
VerifyValue::ipVerify('192.168.1.1', true, true, true); // null (private range denied)
VerifyValue::ipVerify('::1', false, true); // '::1' (IPv6 only)
VerifyValue::ipVerify('not-an-ip'); // nullValidates a MAC address and optionally normalises its format.
PHP’s filter_var accepts colons (:), hyphens (-), and dots (.) as separators. When $normalize is true the validated address is stripped of its original separators and rebuilt using $separator.
public static function macVerify(mixed $value, bool $normalize = false, string $separator = ':'): ?string| Parameter | Type | Description |
|---|---|---|
|
|
The value to validate as a MAC address. |
|
|
When |
|
|
The separator character used when normalising (default |
VerifyValue::macVerify('00-1A-2B-3C-4D-5E'); // '00-1A-2B-3C-4D-5E'
VerifyValue::macVerify('00-1A-2B-3C-4D-5E', true); // '00:1a:2b:3c:4d:5e'
VerifyValue::macVerify('00.1A.2B.3C.4D.5E', true, '-'); // '00-1a-2b-3c-4d-5e'
VerifyValue::macVerify('not-a-mac'); // null
██████████████ ██████████ ██ ████ ████ ██ ██ ██████████████
██ ██ ██ ████ ██████████ ██ ██ ██
██ ██████ ██ ████ ████ ████████ ██████████████████ ██ ██████ ██
██ ██████ ██ ████ ██ ████ ████████ ████ ██ ██████ ██
██ ██████ ██ ██ ██ ████ ██ ██ ██ ██████ ██
██ ██ ██ ██████ ██ ██ ██ ██ ██
██████████████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████████████
██ ██████ ██ ██████████ ████
████ ████ ██ ████ ██ ██ ██ ██ ████ ██ ████
██ ████ ████ ████ ██ ██ ██ ██ ████ ██
████ ██████████ ██ ██ ██ ████ ████████ ██
████ ████████ ██ ████ ██ ████ ████ ████ ██
██ ████ ████ ████ ██ ████ ██ ██ ██ ████ ██ ████
██ ████ ████ ██████ ██ ██ ██ ████ ██ ██ ██
████ ██████████████ ██████ ██ ████████ ██ ████████
██ ██ ██ ██████ ██ ██ ████ ██ ██████ ██
████ ██████ ██ ██ ██ ████ ██ ██████ ██ ██ ██
██ ██████ ██ ██████ ████████ ██ ██ ██ ██
████ ████████ ████████████ ████████ ██████ ████ ██████ ████ ██
██ ██ ██ ████ ████ ████ ██ ████ ████ ██████████████ ██
██ ████████ ██ ██ ██ ██ ██████ ██ ██
██ ████████ ██ ██ ██████ ████ ██████ ████ ████ ██████
██ ████ ██ ██ ██ ██ ██ ██ ██████ ██
██ ██ ██ ████ ██ ██ ████ ██ ██ ██████ ██ ████████
██ ██████████ ████ ████ ██████ ██████████ ██████
██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██
████ ██ ██████ ████ ██████ ████ ████ ████ ████████ ████
██ ██████ ████ ██████ ██████████ ████ ████ ██ ██
██ ██████ ████ ██ ████ ██████████████ ████ ██████████████
██████ ████████ ██ ██ ██ ██ ██ ██████
██████████████ ██ ██████ ██████ ████ ████████ ██ ██ ██████ ██
██ ██ ██████ ████ ██ ██ ████ ████ ████
██ ██████ ██ ████ ██ ████ ████ ██ ██ ██████████████
██ ██████ ██ ██ ██████ ████ ██ ██ ██████ ████ ████
██ ██████ ██ ██ ██████████ ██ ████ ████ ████ ████
██ ██ ██ ██ ██ ██ ████ ██████ ████████
██████████████ ██ ██ ████ ██ ████ ██ ████ ██