Skip to content

Commit ac22467

Browse files
Fix escaping support for pipes and colons in modifier arguments
Co-authored-by: henriquemoody <154023+henriquemoody@users.noreply.github.com>
1 parent 8f7f854 commit ac22467

3 files changed

Lines changed: 86 additions & 3 deletions

File tree

src/Modifiers/FormatterModifier.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
use Throwable;
1818

1919
use function array_slice;
20-
use function explode;
2120
use function is_string;
21+
use function preg_split;
2222
use function ucfirst;
2323

2424
final readonly class FormatterModifier implements Modifier
@@ -35,7 +35,7 @@ public function modify(mixed $value, string|null $pipe): string
3535
}
3636

3737
// Try to parse as a formatter
38-
$parts = explode(':', $pipe);
38+
$parts = preg_split('/(?<!\\\\):/', $pipe) ?: [];
3939
$formatterName = $parts[0];
4040
$arguments = array_slice($parts, 1);
4141

src/PlaceholderFormatter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function formatUsing(string $input, array $parameters): string
4747
private function formatUsingParameters(string $input, array $parameters): string
4848
{
4949
return (string) preg_replace_callback(
50-
'/{{(\w+)(\|([^}]+))?}}/',
50+
'/{{(\w+)(\|([^}\\\\]*(?:\\\\.[^}\\\\]*)*))?}}/',
5151
fn(array $matches) => $this->processPlaceholder($matches, $parameters),
5252
$input,
5353
);
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
/*
4+
* SPDX-FileCopyrightText: (c) Respect Project Contributors
5+
* SPDX-License-Identifier: ISC
6+
* SPDX-FileContributor: Henrique Moody <henriquemoody@gmail.com>
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace Respect\StringFormatter\Test\Unit;
12+
13+
use PHPUnit\Framework\Attributes\CoversClass;
14+
use PHPUnit\Framework\Attributes\DataProvider;
15+
use PHPUnit\Framework\Attributes\Test;
16+
use PHPUnit\Framework\TestCase;
17+
use Respect\StringFormatter\PlaceholderFormatter;
18+
19+
#[CoversClass(PlaceholderFormatter::class)]
20+
final class PlaceholderFormatterEscapingTest extends TestCase
21+
{
22+
/** @param array<string, mixed> $parameters */
23+
#[Test]
24+
#[DataProvider('providerForEscapedColons')]
25+
public function itShouldHandleEscapedColonsInFormatterArguments(
26+
array $parameters,
27+
string $template,
28+
string $expected,
29+
): void {
30+
$formatter = new PlaceholderFormatter($parameters);
31+
$actual = $formatter->format($template);
32+
33+
self::assertSame($expected, $actual);
34+
}
35+
36+
/** @return array<string, array{0: array<string, mixed>, 1: string, 2: string}> */
37+
public static function providerForEscapedColons(): array
38+
{
39+
return [
40+
'pattern with escaped colon' => [
41+
['time' => '1234'],
42+
'{{time|pattern:##\:##}}',
43+
'12:34',
44+
],
45+
'pattern with multiple escaped colons' => [
46+
['time' => '123456'],
47+
'{{time|pattern:##\:##\:##}}',
48+
'12:34:56',
49+
],
50+
];
51+
}
52+
53+
/** @param array<string, mixed> $parameters */
54+
#[Test]
55+
#[DataProvider('providerForEscapedPipes')]
56+
public function itShouldHandleEscapedPipesInFormatterArguments(
57+
array $parameters,
58+
string $template,
59+
string $expected,
60+
): void {
61+
$formatter = new PlaceholderFormatter($parameters);
62+
$actual = $formatter->format($template);
63+
64+
self::assertSame($expected, $actual);
65+
}
66+
67+
/** @return array<string, array{0: array<string, mixed>, 1: string, 2: string}> */
68+
public static function providerForEscapedPipes(): array
69+
{
70+
return [
71+
'pattern with escaped pipe' => [
72+
['value' => '123456'],
73+
'{{value|pattern:###\|###}}',
74+
'123|456',
75+
],
76+
'pattern with multiple escaped pipes' => [
77+
['value' => '12345678'],
78+
'{{value|pattern:##\|##\|##\|##}}',
79+
'12|34|56|78',
80+
],
81+
];
82+
}
83+
}

0 commit comments

Comments
 (0)