Skip to content

Commit 317c8a5

Browse files
authored
Also add replaceCallbackStrictGroups as 3.x can rely on PREG_UNMATCHED_AS_NULL for preg_replace_callback (#15)
1 parent eb6128f commit 317c8a5

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

src/Preg.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,24 @@ public static function replaceCallback($pattern, callable $replacement, $subject
192192
return $result;
193193
}
194194

195+
/**
196+
* Variant of `replaceCallback()` which outputs non-null matches (or throws)
197+
*
198+
* @param string $pattern
199+
* @param callable(array<int|string, string>): string $replacement
200+
* @param string $subject
201+
* @param int $count Set by method
202+
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_OFFSET_CAPTURE or PREG_UNMATCHED_AS_NULL, only available on PHP 7.4+
203+
*
204+
* @param-out int<0, max> $count
205+
*/
206+
public static function replaceCallbackStrictGroups(string $pattern, callable $replacement, $subject, int $limit = -1, int &$count = null, int $flags = 0): string
207+
{
208+
return self::replaceCallback($pattern, function (array $matches) use ($pattern, $replacement) {
209+
return $replacement(self::enforceNonNullMatches($pattern, $matches, 'replaceCallback'));
210+
}, $subject, $limit, $count, $flags);
211+
}
212+
195213
/**
196214
* @param array<string, callable(array<int|string, string|null>): string> $pattern
197215
* @param string $subject

src/Regex.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,21 @@ public static function replaceCallback($pattern, callable $replacement, $subject
131131
return new ReplaceResult($count, $result);
132132
}
133133

134+
/**
135+
* Variant of `replaceCallback()` which outputs non-null matches (or throws)
136+
*
137+
* @param string $pattern
138+
* @param callable(array<int|string, string>): string $replacement
139+
* @param string $subject
140+
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_OFFSET_CAPTURE or PREG_UNMATCHED_AS_NULL, only available on PHP 7.4+
141+
*/
142+
public static function replaceCallbackStrictGroups($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult
143+
{
144+
$result = Preg::replaceCallbackStrictGroups($pattern, $replacement, $subject, $limit, $count, $flags);
145+
146+
return new ReplaceResult($count, $result);
147+
}
148+
134149
/**
135150
* @param array<string, callable(array<int|string, string|null>): string> $pattern
136151
* @param string $subject

tests/PregTests/ReplaceCallbackTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Composer\Pcre\BaseTestCase;
1515
use Composer\Pcre\Preg;
16+
use Composer\Pcre\UnexpectedNullMatchException;
1617

1718
class ReplaceCallbackTest extends BaseTestCase
1819
{
@@ -60,6 +61,26 @@ public function testFailure(): void
6061
self::assertSame('def', $result);
6162
}
6263

64+
public function testSuccessStrictGroups(): void
65+
{
66+
$result = Preg::replaceCallbackStrictGroups('{(?P<m>\d)(?<matched>a)?}', function ($match) {
67+
return strtoupper($match['matched']);
68+
}, '3a', -1, $count);
69+
70+
self::assertSame(1, $count);
71+
self::assertSame('A', $result);
72+
}
73+
74+
public function testFailStrictGroups(): void
75+
{
76+
self::expectException(UnexpectedNullMatchException::class);
77+
self::expectExceptionMessage('Pattern "{(?P<m>\d)(?<unmatched>a)?}" had an unexpected unmatched group "unmatched", make sure the pattern always matches or use replaceCallback() instead.');
78+
79+
$result = Preg::replaceCallbackStrictGroups('{(?P<m>\d)(?<unmatched>a)?}', function ($match) {
80+
return strtoupper($match['unmatched']);
81+
}, '123', -1, $count);
82+
}
83+
6384
public function testBadPatternThrowsIfWarningsAreNotThrowing(): void
6485
{
6586
$this->expectPcreException($pattern = '{(?P<m>d)');

0 commit comments

Comments
 (0)