Multibyte strings7 min readExamples run on PHP 8.5.10

mb_ereg_search_init()

Learn how mb_ereg_search_init() prepares a string and a multibyte regex so you can step through matches with the mb_ereg_search family in PHP 8.4.

Signature

mb_ereg_search_init(string $string, ?string $pattern = null, ?string $options = null): bool
Available since PHP 4.2.07.48.08.18.28.38.48.5All versions →

Purpose

mb_ereg_search_init() stores a subject string, and optionally a pattern and options, in internal state belonging to the mbstring regex engine. The other functions in the family then work on that stored state: mb_ereg_search(), mb_ereg_search_pos() and mb_ereg_search_regs(). Each call to one of these finds the next match and moves an internal cursor forward. You get an incremental, cursor-style scan over a multibyte string without passing the string and pattern every time.

The character encoding used for matching is the current regex encoding. That is the internal encoding, or whatever you set with mb_regex_encoding().

Signature

Parameters and defaults

  • $string: the subject text to search. It is required.
  • $pattern: the regular expression, written without delimiters. It has been nullable since PHP 8.0.0 and defaults to null.
  • $options: option letters such as i for case-insensitive matching. They are explained under mb_regex_set_options(). It is nullable and defaults to null.

Return value and failure

The function returns true on success and false on failure, for example when the pattern cannot be compiled. It does not return matches. It only prepares the search, so you must call one of the search functions afterwards. Check the result with a strict comparison if your patterns come from outside your code.

Example 1: collecting every match

After initialising, loop on mb_ereg_search_regs(). It returns an array for each match and false once nothing more is found.

Example
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('cat bat rat', '[cbr]at');
$found = [];
while (($regs = mb_ereg_search_regs()) !== false) {
    $found[] = $regs[0];
}
echo json_encode($found), "\n";
Output
["cat","bat","rat"]

Example 2: byte positions in multibyte text

mb_ereg_search_pos() reports a match as a pair of byte offset and byte length. With accented characters that differs from the character count, because é takes two bytes in UTF-8.

Example
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('café olé', 'é');
$positions = [];
while (($pos = mb_ereg_search_pos()) !== false) {
    $positions[] = $pos;
}
echo json_encode($positions), "\n";
Output
[[3,2],[8,2]]

Example 3: options and capture groups

The third argument turns on case-insensitive matching here. The capture group is read from index 1 of the array returned by mb_ereg_search_regs().

Example
<?php
mb_regex_encoding('UTF-8');
$ok = mb_ereg_search_init('ID=42; id=7; Id=19', 'id=([0-9]+)', 'i');
$ids = [];
while (($regs = mb_ereg_search_regs()) !== false) {
    $ids[] = $regs[1];
}
echo json_encode(['init' => $ok, 'ids' => $ids]), "\n";
Output
{"init":true,"ids":["42","7","19"]}

Edge cases

  • Shared state. The string, pattern and cursor are stored once for the whole request. Calling mb_ereg_search_init() again replaces them and starts a new scan from the beginning. Do not interleave two independent scans.
  • Omitting the pattern. Because $pattern is nullable, you can supply only a new string. Your code is easier to follow, though, if you pass the pattern explicitly each time you initialise.
  • Byte offsets. Positions from mb_ereg_search_pos() and the cursor are byte-based. Do not use them as character indexes with mb_substr(). Use substr() or convert them first.
  • Empty matches. A pattern that can match an empty string, such as x*, may not advance the way you expect. Prefer patterns that consume at least one character when you loop.
  • Encoding. Match behaviour depends on the regex encoding. If the subject is not valid in that encoding, results can be wrong or the search can fail.

Common mistakes

  1. Not initialising first. Calling a search function without an earlier successful init gives no meaningful scan.
  2. Loose loop conditions. while ($regs = mb_ereg_search_regs()) works for non-empty arrays, but an explicit !== false states the intent more clearly. The same applies to mb_ereg_search(), which returns a boolean.
  3. Using delimiters. The mb_ereg family takes bare patterns, not /pattern/i. Put flags in $options.
  4. Mixing up with preg. preg_match_all() returns every match in one call. The mb_ereg search functions step through them one at a time.
  5. Ignoring the return value. If false comes back and you carry on, later calls run against stale or missing state.
  • mb_ereg_search() tests for the next match and advances the cursor.
  • mb_ereg_search_pos() returns the byte offset and length of the next match.
  • mb_ereg_search_regs() returns the next match with its capture groups.
  • mb_regex_encoding() sets or reads the regex character encoding.
  • mb_regex_set_options() explains the option letters.

Recap

mb_ereg_search_init() loads a string, a pattern and options into shared state and returns true or false. Follow it with mb_ereg_search_regs() or mb_ereg_search_pos() in a loop to walk through matches. Remember that offsets are in bytes, that the state is global and that the pattern has no delimiters.

Tested examples

Collect every match with mb_ereg_search_regs()
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('cat bat rat', '[cbr]at');
$found = [];
while (($regs = mb_ereg_search_regs()) !== false) {
    $found[] = $regs[0];
}
echo json_encode($found), "\n";
Output
["cat","bat","rat"]
Byte positions in multibyte text
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('café olé', 'é');
$positions = [];
while (($pos = mb_ereg_search_pos()) !== false) {
    $positions[] = $pos;
}
echo json_encode($positions), "\n";
Output
[[3,2],[8,2]]
Options and capture groups
<?php
mb_regex_encoding('UTF-8');
$ok = mb_ereg_search_init('ID=42; id=7; Id=19', 'id=([0-9]+)', 'i');
$ids = [];
while (($regs = mb_ereg_search_regs()) !== false) {
    $ids[] = $regs[1];
}
echo json_encode(['init' => $ok, 'ids' => $ids]), "\n";
Output
{"init":true,"ids":["42","7","19"]}