Value Objects
Relex wraps PHP’s preg_* functions with immutable value objects that provide type-safe, fluent APIs for working with regex results.
MatchResult
Section titled “MatchResult”Represents the result of a single preg_match operation.
Creating
Section titled “Creating”use Cline\Relex\Relex;
$match = Relex::match('/(\d+)-(\d+)/', 'Order 123-456');Checking for Matches
Section titled “Checking for Matches”$match->hasMatch(); // true if pattern matched$match->isEmpty(); // true if no match foundAccessing Results
Section titled “Accessing Results”$match = Relex::match('/(\w+)@(\w+)\.(\w+)/', 'user@example.com');
// Full match$match->result(); // "user@example.com"
// By group index (0 = full match)$match->group(0); // "user@example.com"$match->group(1); // "user"$match->group(2); // "example"$match->group(3); // "com"
// All groups$match->groups(); // [0 => "user@example.com", 1 => "user", ...]Named Groups
Section titled “Named Groups”$match = Relex::match( '/(?<user>\w+)@(?<domain>\w+)\.(?<tld>\w+)/', 'user@example.com');
$match->group('user'); // "user"$match->group('domain'); // "example"$match->group('tld'); // "com"
// All named groups$match->namedGroups(); // ['user' => 'user', 'domain' => 'example', 'tld' => 'com']With Offsets
Section titled “With Offsets”Get byte positions of matches:
$match = Relex::matchWithOffsets('/\d+/', 'abc123def');
$match->result(); // "123"$match->offset(); // 3
// Group offsets (when using PREG_OFFSET_CAPTURE)$match->groupOffset(0); // 3Getting Pattern Info
Section titled “Getting Pattern Info”$match->pattern(); // The pattern string used$match->subject(); // The subject string searchedMatchAllResult
Section titled “MatchAllResult”Represents all matches from a preg_match_all operation.
Creating
Section titled “Creating”$matches = Relex::matchAll('/\d+/', 'a1 b22 c333');Basic Access
Section titled “Basic Access”$matches->hasMatch(); // true$matches->isEmpty(); // false$matches->count(); // 3
// Get all full matches as strings$matches->results(); // ["1", "22", "333"]Accessing Individual Matches
Section titled “Accessing Individual Matches”Each match is a MatchResult object:
$matches->first(); // MatchResult for "1"$matches->last(); // MatchResult for "333"$matches->get(1); // MatchResult for "22"$matches->get(99); // null (out of bounds)
// All as MatchResult objects$matches->all(); // [MatchResult, MatchResult, MatchResult]Named Captures
Section titled “Named Captures”$matches = Relex::matchAll('/(?<letter>[a-z])(?<number>\d)/', 'a1 b2 c3');
// Get named captures from all matches$matches->namedCaptures();// [// ['letter' => 'a', 'number' => '1'],// ['letter' => 'b', 'number' => '2'],// ['letter' => 'c', 'number' => '3'],// ]
// Pluck specific group from all matches$matches->pluck('letter'); // ['a', 'b', 'c']$matches->pluck('number'); // ['1', '2', '3']$matches->pluck(0); // ['a1', 'b2', 'c3']Iteration
Section titled “Iteration”// Foreach loopforeach ($matches as $match) { echo $match->result();}
// Each with callback$matches->each(function (MatchResult $match, int $index) { echo "{$index}: {$match->result()}\n";});Filtering
Section titled “Filtering”$matches = Relex::matchAll('/\d+/', 'a1 b22 c333');
// Filter by callback$filtered = $matches->filter(fn(MatchResult $m) => strlen($m->result()) > 1);$filtered->results(); // ["22", "333"]Mapping
Section titled “Mapping”$matches = Relex::matchAll('/\d+/', 'a1 b2 c3');
// Transform matches$doubled = $matches->map(fn(MatchResult $m) => (int) $m->result() * 2);// [2, 4, 6]Reducing
Section titled “Reducing”$sum = $matches->reduce( fn(int $carry, MatchResult $m) => $carry + (int) $m->result(), 0);// 6Taking and Skipping
Section titled “Taking and Skipping”$matches = Relex::matchAll('/\d+/', 'a1 b2 c3 d4 e5');
$matches->take(2)->results(); // ["1", "2"]$matches->skip(2)->results(); // ["3", "4", "5"]$matches->skip(2)->take(2)->results(); // ["3", "4"]Capture Modes
Section titled “Capture Modes”Control which captures are included:
use Cline\Relex\Enums\CaptureMode;
$matches = Relex::matchAll('/(?<letter>\w)(\d)/', 'a1 b2');
// All captures (default)$matches->capture(CaptureMode::All);
// First capture group only$matches->capture(CaptureMode::First);
// All but full match (group 0)$matches->capture(CaptureMode::AllButFirst);
// Named groups only$matches->capture(CaptureMode::Named);
// No captures$matches->capture(CaptureMode::None);ReplaceResult
Section titled “ReplaceResult”Represents the result of a preg_replace or preg_replace_callback operation.
Creating
Section titled “Creating”// Simple replacement$result = Relex::replace('/\d+/', 'X', 'a1 b2 c3');
// Callback replacement$result = Relex::replace('/\d+/', fn($m) => $m->result() * 2, 'a1 b2');
// Replace first only$result = Relex::replaceFirst('/\d+/', 'X', 'a1 b2 c3');Accessing Results
Section titled “Accessing Results”$result = Relex::replace('/\d+/', 'X', 'a1 b2 c3');
$result->result(); // "aX bX cX"$result->count(); // 3 (number of replacements)Multiple Subjects
Section titled “Multiple Subjects”When replacing in an array of strings:
$result = Relex::replace('/\d+/', 'X', ['a1', 'b2', 'c3']);
$result->results(); // ["aX", "bX", "cX"]Checking Replacements
Section titled “Checking Replacements”$result->hasReplacements(); // true if any replacements made$result->isEmpty(); // true if no replacementsGetting Original Info
Section titled “Getting Original Info”$result->pattern(); // Pattern(s) used$result->subject(); // Original subject string(s)SplitResult
Section titled “SplitResult”Represents segments from a preg_split operation.
Creating
Section titled “Creating”// Basic split$split = Relex::split('/\s+/', 'hello world foo');
// With limit$split = Relex::split('/\s+/', 'a b c d e', 3);
// Keep delimiters$split = Relex::splitWithDelimiters('/(\s+)/', 'a b c');Basic Access
Section titled “Basic Access”$split = Relex::split('/,/', 'a,b,c,d,e');
$split->results(); // ["a", "b", "c", "d", "e"]$split->toArray(); // Same as results()$split->count(); // 5
$split->isEmpty(); // false$split->isNotEmpty(); // trueAccessing Segments
Section titled “Accessing Segments”$split->first(); // "a"$split->last(); // "e"$split->get(2); // "c"$split->get(99); // nullIteration
Section titled “Iteration”foreach ($split as $segment) { echo $segment;}
$split->each(function (string $segment, int $index) { echo "{$index}: {$segment}\n";});Filtering
Section titled “Filtering”$split = Relex::split('/,/', 'a,,b,,c');
$filtered = $split->filter(fn(string $s) => $s !== '');$filtered->results(); // ["a", "b", "c"]Mapping
Section titled “Mapping”$split = Relex::split('/,/', 'hello,world');
$upper = $split->map(fn(string $s) => strtoupper($s));// ["HELLO", "WORLD"]Taking and Skipping
Section titled “Taking and Skipping”$split = Relex::split('/,/', 'a,b,c,d,e');
$split->take(2)->results(); // ["a", "b"]$split->skip(2)->results(); // ["c", "d", "e"]Reversing
Section titled “Reversing”$split->reverse()->results(); // ["e", "d", "c", "b", "a"]Unique Values
Section titled “Unique Values”$split = Relex::split('/,/', 'a,b,a,c,b');
$split->unique()->results(); // ["a", "b", "c"]Joining Back
Section titled “Joining Back”$split = Relex::split('/\s+/', 'hello world');
$split->join('-'); // "hello-world"$split->join(); // "helloworld"Countable Interface
Section titled “Countable Interface”All collection value objects implement Countable:
count($matches); // Same as $matches->count()count($split); // Same as $split->count()IteratorAggregate Interface
Section titled “IteratorAggregate Interface”Collection value objects can be used in foreach:
foreach ($matches as $match) { ... }foreach ($split as $segment) { ... }Immutability
Section titled “Immutability”All value objects are immutable. Operations return new instances:
$original = Relex::matchAll('/\d+/', 'a1 b2 c3');$filtered = $original->filter(fn($m) => (int) $m->result() > 1);
$original->count(); // 3$filtered->count(); // 2