• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

keradus / PHP-CS-Fixer / 17279562118

27 Aug 2025 09:47PM UTC coverage: 94.693%. Remained the same
17279562118

push

github

keradus
CS

28316 of 29903 relevant lines covered (94.69%)

45.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.33
/src/Preg.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <fabien@symfony.com>
9
 *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14

15
namespace PhpCsFixer;
16

17
/**
18
 * This class replaces preg_* functions to better handling UTF8 strings,
19
 * ensuring no matter "u" modifier is present or absent subject will be handled correctly.
20
 *
21
 * @author Kuba Werłos <werlos@gmail.com>
22
 *
23
 * @internal
24
 *
25
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
26
 */
27
final class Preg
28
{
29
    /**
30
     * @param array<array-key, mixed>                               $matches
31
     * @param int-mask<PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL> $flags
32
     *
33
     * @param-out ($flags is PREG_OFFSET_CAPTURE
34
     *     ? array<array-key, array{string, 0|positive-int}|array{'', -1}>
35
     *     : ($flags is PREG_UNMATCHED_AS_NULL
36
     *         ? array<array-key, string|null>
37
     *         : ($flags is int-mask<PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL>&768
38
     *             ? array<array-key, array{string, 0|positive-int}|array{null, -1}>
39
     *             : array<array-key, string>
40
     *         )
41
     *     )
42
     * ) $matches
43
     *
44
     * @throws PregException
45
     */
46
    public static function match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool
47
    {
48
        $result = @preg_match(self::addUtf8Modifier($pattern), $subject, $matches, $flags, $offset);
18✔
49
        if (false !== $result && \PREG_NO_ERROR === preg_last_error()) {
18✔
50
            return 1 === $result;
4✔
51
        }
52

53
        $result = @preg_match(self::removeUtf8Modifier($pattern), $subject, $matches, $flags, $offset);
14✔
54
        if (false !== $result && \PREG_NO_ERROR === preg_last_error()) {
14✔
55
            return 1 === $result;
7✔
56
        }
57

58
        throw self::newPregException(preg_last_error(), preg_last_error_msg(), __METHOD__, $pattern);
7✔
59
    }
60

61
    /**
62
     * @param array<array-key, mixed>                                                                   $matches
63
     * @param int-mask<PREG_PATTERN_ORDER, PREG_SET_ORDER, PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL> $flags
64
     *
65
     * @param-out ($flags is PREG_PATTERN_ORDER
66
     *     ? array<list<string>>
67
     *     : ($flags is PREG_SET_ORDER
68
     *         ? list<array<string>>
69
     *         : ($flags is int-mask<PREG_PATTERN_ORDER, PREG_OFFSET_CAPTURE>&(256|257)
70
     *             ? array<list<array{string, int}>>
71
     *             : ($flags is int-mask<PREG_SET_ORDER, PREG_OFFSET_CAPTURE>&258
72
     *                 ? list<array<array{string, int}>>
73
     *                 : ($flags is int-mask<PREG_PATTERN_ORDER, PREG_UNMATCHED_AS_NULL>&(512|513)
74
     *                     ? array<list<?string>>
75
     *                     : ($flags is int-mask<PREG_SET_ORDER, PREG_UNMATCHED_AS_NULL>&514
76
     *                         ? list<array<?string>>
77
     *                         : ($flags is int-mask<PREG_SET_ORDER, PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL>&770
78
     *                             ? list<array<array{?string, int}>>
79
     *                             : ($flags is 0 ? array<list<string>> : array<mixed>)
80
     *                         )
81
     *                     )
82
     *                 )
83
     *             )
84
     *         )
85
     *     )
86
     * ) $matches
87
     *
88
     * @throws PregException
89
     */
90
    public static function matchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = \PREG_PATTERN_ORDER, int $offset = 0): int
91
    {
92
        $result = @preg_match_all(self::addUtf8Modifier($pattern), $subject, $matches, $flags, $offset);
6✔
93
        if (false !== $result && \PREG_NO_ERROR === preg_last_error()) {
6✔
94
            return $result;
3✔
95
        }
96

97
        $result = @preg_match_all(self::removeUtf8Modifier($pattern), $subject, $matches, $flags, $offset);
3✔
98
        if (false !== $result && \PREG_NO_ERROR === preg_last_error()) {
3✔
99
            return $result;
2✔
100
        }
101

102
        throw self::newPregException(preg_last_error(), preg_last_error_msg(), __METHOD__, $pattern);
1✔
103
    }
104

105
    /**
106
     * @param-out int $count
107
     *
108
     * @return ($subject is non-empty-string ? ($replacement is non-empty-string ? non-empty-string : string) : string)
109
     *
110
     * @throws PregException
111
     */
112
    public static function replace(string $pattern, string $replacement, string $subject, int $limit = -1, ?int &$count = null): string
113
    {
114
        $result = @preg_replace(self::addUtf8Modifier($pattern), $replacement, $subject, $limit, $count);
16✔
115
        if (null !== $result && \PREG_NO_ERROR === preg_last_error()) {
16✔
116
            return $result;
3✔
117
        }
118

119
        $result = @preg_replace(self::removeUtf8Modifier($pattern), $replacement, $subject, $limit, $count);
13✔
120
        if (null !== $result && \PREG_NO_ERROR === preg_last_error()) {
13✔
121
            return $result;
6✔
122
        }
123

124
        throw self::newPregException(preg_last_error(), preg_last_error_msg(), __METHOD__, $pattern);
7✔
125
    }
126

127
    /**
128
     * @param-out int $count
129
     *
130
     * @throws PregException
131
     */
132
    public static function replaceCallback(string $pattern, callable $callback, string $subject, int $limit = -1, ?int &$count = null): string
133
    {
134
        $result = @preg_replace_callback(self::addUtf8Modifier($pattern), $callback, $subject, $limit, $count);
6✔
135
        if (null !== $result && \PREG_NO_ERROR === preg_last_error()) {
6✔
136
            return $result;
3✔
137
        }
138

139
        $result = @preg_replace_callback(self::removeUtf8Modifier($pattern), $callback, $subject, $limit, $count);
3✔
140
        if (null !== $result && \PREG_NO_ERROR === preg_last_error()) {
3✔
141
            return $result;
2✔
142
        }
143

144
        throw self::newPregException(preg_last_error(), preg_last_error_msg(), __METHOD__, $pattern);
1✔
145
    }
146

147
    /**
148
     * @return ($flags is PREG_SPLIT_OFFSET_CAPTURE ? list<array{string, int<0, max>}> : list<string>)
149
     *
150
     * @throws PregException
151
     */
152
    public static function split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array
153
    {
154
        $result = @preg_split(self::addUtf8Modifier($pattern), $subject, $limit, $flags);
6✔
155
        if (false !== $result && \PREG_NO_ERROR === preg_last_error()) {
6✔
156
            return $result;
3✔
157
        }
158

159
        $result = @preg_split(self::removeUtf8Modifier($pattern), $subject, $limit, $flags);
3✔
160
        if (false !== $result && \PREG_NO_ERROR === preg_last_error()) {
3✔
161
            return $result;
2✔
162
        }
163

164
        throw self::newPregException(preg_last_error(), preg_last_error_msg(), __METHOD__, $pattern);
1✔
165
    }
166

167
    private static function addUtf8Modifier(string $pattern): string
168
    {
169
        return $pattern.'u';
52✔
170
    }
171

172
    private static function removeUtf8Modifier(string $pattern): string
173
    {
174
        if ('' === $pattern) {
36✔
175
            return '';
7✔
176
        }
177

178
        $delimiter = $pattern[0];
29✔
179

180
        $endDelimiterPosition = strrpos($pattern, $delimiter);
29✔
181
        \assert(\is_int($endDelimiterPosition));
29✔
182

183
        return substr($pattern, 0, $endDelimiterPosition).str_replace('u', '', substr($pattern, $endDelimiterPosition));
29✔
184
    }
185

186
    /**
187
     * Create the generic PregException message and tell more about such kind of error in the message.
188
     */
189
    private static function newPregException(int $error, string $errorMsg, string $method, string $pattern): PregException
190
    {
191
        $result = null;
17✔
192
        $errorMessage = null;
17✔
193

194
        try {
195
            $result = ExecutorWithoutErrorHandler::execute(static fn () => preg_match($pattern, ''));
17✔
196
        } catch (ExecutorWithoutErrorHandlerException $e) {
17✔
197
            $result = false;
17✔
198
            $errorMessage = $e->getMessage();
17✔
199
        }
200

201
        if (false !== $result) {
17✔
202
            return new PregException(\sprintf('Unknown error occurred when calling %s: %s.', $method, $errorMsg), $error);
×
203
        }
204

205
        $code = preg_last_error();
17✔
206

207
        $message = \sprintf(
17✔
208
            '(code: %d) %s',
17✔
209
            $code,
17✔
210
            preg_replace('~preg_[a-z_]+[()]{2}: ~', '', $errorMessage)
17✔
211
        );
17✔
212

213
        return new PregException(
17✔
214
            \sprintf('%s(): Invalid PCRE pattern "%s": %s (version: %s)', $method, $pattern, $message, \PCRE_VERSION),
17✔
215
            $code
17✔
216
        );
17✔
217
    }
218
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc