50publicstaticfunctionisUtf8( $value ) {
51return mb_check_encoding( (
string)$value,
'UTF-8' );
66 $subject, $nested =
false ) {
70 $encStart = preg_quote( $startDelim,
'!' );
71 $encEnd = preg_quote( $endDelim,
'!' );
72 $encSep = preg_quote( $separator,
'!' );
73 $len = strlen( $subject );
79"!$encStart|$encEnd|$encSep!S", $subject, $m,
80 PREG_OFFSET_CAPTURE, $inputPos
85 $inputPos = $matchPos + strlen( $match );
86if ( $match === $separator ) {
89 $subject, $lastPos, $matchPos - $lastPos
93 } elseif ( $match === $startDelim ) {
94if ( $depth === 0 || $nested ) {
101 $exploded[] = substr( $subject, $lastPos );
102// This method could be rewritten in the future to avoid creating an 103// intermediate array, since the return type is just an iterator. 104returnnew ArrayIterator( $exploded );
125 $segments =
explode( $startDelim, $subject );
126 $output = array_shift( $segments );
127foreach ( $segments as $s ) {
128 $endDelimPos = strpos( $s, $endDelim );
129if ( $endDelimPos ===
false ) {
130 $output .= $startDelim . $s;
132 $output .= $replace . substr( $s, $endDelimPos + strlen( $endDelim ) );
162privatestaticfunction delimiterReplaceCallback( $startDelim, $endDelim, $callback,
170 $encStart = preg_quote( $startDelim,
'!' );
171 $encEnd = preg_quote( $endDelim,
'!' );
172 $strcmp = strpos( $flags,
'i' ) ===
false ?
'strcmp' :
'strcasecmp';
173 $endLength = strlen( $endDelim );
176while ( $inputPos < strlen( $subject ) &&
177 preg_match(
"!($encStart)|($encEnd)!S$flags", $subject, $m, PREG_OFFSET_CAPTURE, $inputPos )
179 $tokenOffset = $m[0][1];
180if ( $m[1][0] !=
'' ) {
182 $strcmp( $endDelim, substr( $subject, $tokenOffset, $endLength ) ) == 0
184 # An end match is present at the same location 186 $tokenLength = $endLength;
189 $tokenLength = strlen( $m[0][0] );
191 } elseif ( $m[2][0] !=
'' ) {
193 $tokenLength = strlen( $m[0][0] );
195thrownew InvalidArgumentException(
'Invalid delimiter given to ' . __METHOD__ );
198if ( $tokenType ==
'start' ) {
199 # Only move the start position if we haven't already found a start 200 # This means that START START END matches outer pair 203 $inputPos = $tokenOffset + $tokenLength;
204 # Write out the non-matching section 205 $output .= substr( $subject, $outputPos, $tokenOffset - $outputPos );
206 $outputPos = $tokenOffset;
207 $contentPos = $inputPos;
210 # Move the input position past the *first character* of START, 211 # to protect against missing END when it overlaps with START 212 $inputPos = $tokenOffset + 1;
214 } elseif ( $tokenType ==
'end' ) {
217 $output .= $callback( [
218 substr( $subject, $outputPos, $tokenOffset + $tokenLength - $outputPos ),
219 substr( $subject, $contentPos, $tokenOffset - $contentPos )
223 # Non-matching end, write it out 224 $output .= substr( $subject, $inputPos, $tokenOffset + $tokenLength - $outputPos );
226 $inputPos = $outputPos = $tokenOffset + $tokenLength;
228thrownew InvalidArgumentException(
'Invalid delimiter given to ' . __METHOD__ );
231if ( $outputPos < strlen( $subject ) ) {
232 $output .= substr( $subject, $outputPos );
254 $startDelim, $endDelim, $replace, $subject, $flags =
'' 256return self::delimiterReplaceCallback(
257 $startDelim, $endDelim,
258staticfunction ( array
$matches ) use ( $replace ) {
274 $placeholder =
"\x00";
276// Remove placeholder instances 277 $text = str_replace( $placeholder,
'', $text );
279// Replace instances of the separator inside HTML-like tags with the placeholder 280 $cleaned = self::delimiterReplaceCallback(
282staticfunction ( array
$matches ) use ( $search, $placeholder ) {
283return str_replace( $search, $placeholder,
$matches[0] );
288// Explode, then put the replaced separators back in 289 $cleaned = str_replace( $search, $replace, $cleaned );
290 $text = str_replace( $placeholder, $search, $cleaned );
305 AtEase::suppressWarnings();
306// @phan-suppress-next-line PhanParamSuspiciousOrder False positive 307 $isValid = preg_match( $string,
'' );
308 AtEase::restoreWarnings();
309return $isValid !==
false;
320 $string = str_replace(
'\\',
'\\\\', $string );
321return str_replace(
'$',
'\\$', $string );
331publicstaticfunctionexplode( $separator, $subject ) {
332if ( substr_count( $subject, $separator ) > 1000 ) {
335returnnew ArrayIterator(
explode( $separator, $subject ) );
354publicstaticfunctionunpack(
string $format,
string $data, $length =
false ): array {
355 Assert::parameterType( [
'integer',
'false' ], $length,
'$length' );
356if ( $length !==
false ) {
357 $realLen = strlen( $data );
358if ( $realLen < $length ) {
360 .
"string of length $realLen, but needed one " 361 .
"of at least length $length." 366 AtEase::suppressWarnings();
367 $result =
unpack( $format, $data );
368 AtEase::restoreWarnings();
370if ( $result ===
false ) {
371// If it cannot extract the packed data. static delimiterExplode( $startDelim, $endDelim, $separator, $subject, $nested=false)
Explode a string, but ignore any instances of the separator inside the given start and end delimiters...