| 219 | if ( ! function_exists( 'mb_strpos' ) ) : |
| 220 | /** |
| 221 | * Compat function to mimic mb_strpos(). |
| 222 | * |
| 223 | * @ignore |
| 224 | * @since 4.5.0 |
| 225 | * |
| 226 | * @see _mb_strpos() |
| 227 | * |
| 228 | * @param string $haystack The string to search in. |
| 229 | * @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character. |
| 230 | * @param int $offset Optional, search will start this number of characters counted from the beginning of the string. The offset cannot be negative. |
| 231 | * @param string|null $encoding Optional. Character encoding to use. Default null. |
| 232 | * @return int Position of first occurence found of $haystack of `$needle`. |
| 233 | */ |
| 234 | function mb_strpos( $haystack, $needle, $offset = 0, $encoding = null ) { |
| 235 | return _mb_strpos( $haystack, $needle, $offset, $encoding ); |
| 236 | } |
| 237 | endif; |
| 238 | |
| 239 | /** |
| 240 | * Internal compat function to mimic mb_strpos(). |
| 241 | * |
| 242 | * Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit. |
| 243 | * For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence. |
| 244 | * The behavior of this function for invalid inputs is PHP compliant. |
| 245 | * |
| 246 | * @ignore |
| 247 | * @since 4.5.0 |
| 248 | * |
| 249 | * @param string $haystack The string to search in. |
| 250 | * @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character. |
| 251 | * @param int $offset Optional, search will start this number of characters counted from the beginning of the string. The offset cannot be negative. |
| 252 | * @param string|null $encoding Optional. Character encoding to use. Default null. |
| 253 | * |
| 254 | * @return int Position of first occurence found of $haystack of `$needle`. |
| 255 | */ |
| 256 | function _mb_strpos( $haystack, $needle, $offset = 0, $encoding = null ) { |
| 257 | |
| 258 | if ( null === $encoding ) { |
| 259 | $encoding = get_option( 'blog_charset' ); |
| 260 | } |
| 261 | |
| 262 | // The solution below works only for UTF-8, |
| 263 | // So in case of a different charset just use built-in strpos() |
| 264 | if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) { |
| 265 | return $offset === 0 ? strpos( $haystack, $needle ) : strpos( $haystack, $needle, $offset ); |
| 266 | } |
| 267 | |
| 268 | $haystack_len = mb_strlen( $haystack ); |
| 269 | |
| 270 | if ( $offset < (int) 0 || $offset > $haystack_len ) { |
| 271 | trigger_error( 'mb_strpos(): Offset not contained in string', E_USER_WARNING ); |
| 272 | return false; |
| 273 | } |
| 274 | |
| 275 | if ( ! is_string( $needle ) ) { |
| 276 | $needle = (int) $needle; |
| 277 | |
| 278 | if ( ! is_int( $needle ) ) { |
| 279 | trigger_error( 'mb_strpos(): Array to string conversion', E_USER_WARNING ); |
| 280 | return false; |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | if ( empty( $needle ) ) { |
| 285 | trigger_error( 'mb_strpos(): Empty needle', E_USER_WARNING ); |
| 286 | return false; |
| 287 | } |
| 288 | |
| 289 | // Slice off the offset |
| 290 | $haystack_sub = mb_substr( $haystack, $offset ); |
| 291 | |
| 292 | if ( _wp_can_use_pcre_u() ) { |
| 293 | // Use the regex unicode support to separate the UTF-8 characters into an array |
| 294 | preg_match_all( "/./us", $haystack, $match_h ); |
| 295 | preg_match_all( "/$needle/us", $haystack_sub, $match_n ); |
| 296 | |
| 297 | $inter = array_intersect( $match_h[0], $match_n[0] ); |
| 298 | |
| 299 | if ( ! isset( $inter ) ) |
| 300 | return false; |
| 301 | |
| 302 | //* Prevent bugs, (re)assign var. |
| 303 | $pos = null; |
| 304 | |
| 305 | // Find first occurence greater than or equal to offset |
| 306 | foreach ( $inter as $key => $value ) { |
| 307 | if ( $key >= $offset ) { |
| 308 | $pos = $key; |
| 309 | break; |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | //* No key has been found. |
| 314 | if ( ! isset( $pos ) ) |
| 315 | return false; |
| 316 | |
| 317 | return (int) $pos; |
| 318 | } |
| 319 | |
| 320 | $regex = '/( |
| 321 | [\x00-\x7F] # single-byte sequences 0xxxxxxx |
| 322 | | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx |
| 323 | | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2 |
| 324 | | [\xE1-\xEC][\x80-\xBF]{2} |
| 325 | | \xED[\x80-\x9F][\x80-\xBF] |
| 326 | | [\xEE-\xEF][\x80-\xBF]{2} |
| 327 | | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3 |
| 328 | | [\xF1-\xF3][\x80-\xBF]{3} |
| 329 | | \xF4[\x80-\x8F][\x80-\xBF]{2} |
| 330 | )/x'; |
| 331 | |
| 332 | /** |
| 333 | * Place haystack into array |
| 334 | */ |
| 335 | $match_h = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop |
| 336 | do { |
| 337 | // We had some string left over from the last round, but we counted it in that last round. |
| 338 | array_pop( $match_h ); |
| 339 | |
| 340 | // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string) |
| 341 | $pieces = preg_split( $regex, $haystack, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); |
| 342 | |
| 343 | $match_h = array_merge( $match_h, $pieces ); |
| 344 | } while ( count( $pieces ) > 1 && $haystack = array_pop( $pieces ) ); // If there's anything left over, repeat the loop. |
| 345 | |
| 346 | /** |
| 347 | * Place haystack offset into array |
| 348 | */ |
| 349 | $match_hs = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop |
| 350 | do { |
| 351 | // We had some string left over from the last round, but we counted it in that last round. |
| 352 | array_pop( $match_hs ); |
| 353 | |
| 354 | // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string) |
| 355 | $pieces = preg_split( $regex, $haystack_sub, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); |
| 356 | |
| 357 | $match_hs = array_merge( $match_hs, $pieces ); |
| 358 | } while ( count( $pieces ) > 1 && $haystack_sub = array_pop( $pieces ) ); // If there's anything left over, repeat the loop. |
| 359 | |
| 360 | /** |
| 361 | * Put needle into array |
| 362 | */ |
| 363 | $match_n = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop |
| 364 | do { |
| 365 | // We had some string left over from the last round, but we counted it in that last round. |
| 366 | array_pop( $match_n ); |
| 367 | |
| 368 | // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string) |
| 369 | $pieces = preg_split( $regex, $needle, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); |
| 370 | |
| 371 | $match_n = array_merge( $match_n, $pieces ); |
| 372 | } while ( count( $pieces ) > 1 && $needle = array_pop( $pieces ) ); // If there's anything left over, repeat the loop. |
| 373 | |
| 374 | /** |
| 375 | * Compute match of haystack offset with needle |
| 376 | * If passed, return the array key number within the full haystack. |
| 377 | */ |
| 378 | if ( false !== in_array( $match_n[0], $match_hs ) ) { |
| 379 | $inter = array_intersect( $match_h, $match_n ); |
| 380 | |
| 381 | if ( ! isset( $inter ) ) |
| 382 | return false; |
| 383 | |
| 384 | //* Prevent bugs, (re)assign var. |
| 385 | $pos = null; |
| 386 | |
| 387 | // Find first occurence greater than or equal to offset |
| 388 | foreach ( $inter as $key => $value ) { |
| 389 | if ( $key >= $offset ) { |
| 390 | $pos = $key; |
| 391 | break; |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | //* No key has been found. |
| 396 | if ( ! isset( $pos ) ) |
| 397 | return false; |
| 398 | |
| 399 | return (int) $pos; |
| 400 | } else { |
| 401 | return false; |
| 402 | } |
| 403 | } |
| 404 | |
| 405 | |
| 406 | /** |
| 407 | * Internal compat function to mimic mb_strpos(). |
| 408 | * |
| 409 | * Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit. |
| 410 | * For $encoding === UTF-8, the `$str` input is expected to be a valid UTF-8 byte |
| 411 | * sequence. The behavior of this function for invalid inputs is undefined. |
| 412 | * |
| 413 | * @ignore |
| 414 | * @since 4.2.0 |
| 415 | * |
| 416 | * @param string $str The string to retrieve the character length from. |
| 417 | * @param string|null $encoding Optional. Character encoding to use. Default null. |
| 418 | * @return int String length of `$str`. |
| 419 | */ |
| 420 | |