Class it:

/**
 * Try to match string against regex. Case insensitive by default.
 * @param $pattern Regex to match against
 * @param $string String to match
 * @param $p['offset_capture'] Set flag preg_offset_capture (returns offsets with the matches).
 * @param $p['all'] Return every match as array instead of first match.
 * @param $p contains pattern modifiers, @see convertregex()
 * @return Matched string or null
 */
static function match($pattern, $string, $p = null)
{
    if (!preg_match('/\\\\[wb]|[!\x80-\xff]|\[\[:/i', $pattern) && !$p)
        $r = preg_match('!' . $pattern . '!i' . (ini_get('default_charset') == 'utf-8' ? 'u' : ''), $string, $m); # fast path for simple patterns
    else
    {
        $flags = $p['offset_capture'] ? PREG_OFFSET_CAPTURE : 0;

        if ($p['all'])
            $r = preg_match_all(it::convertregex($pattern, $p), $string, $m, $flags | PREG_PATTERN_ORDER, $p['offset']);
        else
            $r = preg_match(it::convertregex($pattern, $p), $string, $m, $flags, $p['offset']);
    }

    if (!$r)    # no match
    {
        if (preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR)
            it::error("Exceeded pcre.backtrack_limit of " . ini_get('pcre.backtrack_limit') . " bytes");
        else if (preg_last_error() == PREG_BAD_UTF8_ERROR)
            it::error("Invalid utf-8 in it::match haystack: " . substr($string, 0, 500)); # UTF8SAFE

        $result = $p['all'] ? array() : null;
    }
    else if (count($m) == 1)    # no capture
        $result = $m[0];
    else if (count($m) == 2)    # one capture
        $result = $m[1];
    else if ($p['all'] && !$p['pattern_order'])    # captures, reorder pattern_order to set_order but without first element
        $result = array_map(null, ...array_slice($m, 1));
    else    # captures, don't return first element (matched string)
        $result = array_slice($m, 1);

    return $result;
}