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($string0500)); # 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($m1));
    else    
# captures, don't return first element (matched string)
        
$result array_slice($m1);

    return 
$result;
}