src/includes/classes/Core/Utils/WRegx.php
public function frag($value, string $exclusion_chars = '/', bool $force_match_all = false)
    {
        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key => &$_value) {
                $_value = $this->frag($_value, $exclusion_chars, $force_match_all);
            } // unset($_key, $_value); // Housekeeping.
            return $value;
        }
        if (!($string = (string) $value)) {
            return $string; // Empty.
        }
        if (!$exclusion_chars) { // Must have this.
            throw $this->c::issue('Missing `exclusion_chars`.');
        } elseif ($exclusion_chars !== '/') {
            $_exclusion_chars_array = $this->c::mbStrSplit($exclusion_chars);
            if (array_diff($_exclusion_chars_array, ['⁅', '⒯', '⁆', '?', '*']) !== $_exclusion_chars_array) {
                throw $this->c::issue('Invalid `exclusion_chars`. May not exclude: `⁅`, `⒯`, `⁆`, `?` or `*`.');
            } // unset($_exclusion_chars_array); // Housekeeping.
        }
        $tokens          = []; // Initialize.
        $string          = $this->c::escRegex($string);
        $exclusion_chars = $this->c::escRegex($exclusion_chars);

        // Convert `[!a-z0-9]` into a real regex `[^character class]`.
        // Special characters inside a character class have no further meaning.
        // For this reason, we remove them from the equation by tokenizing them, for now.
        $string = preg_replace_callback('/\\\\\[((?:(?:[^[\]]+)|(?R))*)\\\\\]/u', function ($m) use (&$tokens) {
            $m[1] = mb_strpos($m[1], '\\!') === 0 ? '^'.mb_substr($m[1], 2) : $m[1];
            $m[1] = preg_replace('/([a-z0-9])\\\\\-([a-z0-9])/u', '${1}-${2}', $m[1]);
            $tokens[] = '['.$m[1].']';
            return '⁅⒯'.(count($tokens) - 1).'⒯⁆';
        }, $string); // See restoration below.

        // Convert `{}` into `(?:this|that)`; i.e., a list of alternates.
        $string = preg_replace_callback('/\\\\\{((?:(?:[^{}]+)|(?R))*)\\\\\}/u', function ($m) {
            return '(?:'.str_replace(['\\{', '\\}', ','], ['(?:', ')', '|'], $m[1]).')';
        }, $string); // `,` becomes `|` for regex alternation.

        // Deal with `^` and `$`, depending on `$force_match_all`.
        if (!$force_match_all) { // Must come before `$exclusion_chars` below.
            // If not forcing `^$`, we need to treat them as special chars here.
            $string = preg_replace(['/\\\\\^/u', '/\\\\\$/u'], ['^', '$'], $string);
        }
        // `???` (or more) should be treated like `?` instead of as a partial `??` operator.
        $string = preg_replace_callback('/(?:\\\\\?){3,}/u', function ($m) use ($exclusion_chars) {
            return '[^'.$exclusion_chars.']{'.strlen(str_replace('\\', '', $m[0])).'}';
        }, $string); // i.e., Treat as three or more instances of `?` by itself.

        // Now convert `??` and `**` (or more) after having already converted `???`... above.
        $string = preg_replace(['/(?:\\\\\?){2}/u', '/(?:\\\\\*){2,}/u'], ['[\s\S]', '[\s\S]*?'], $string);

        // Now convert any remaining `?` and/or `*` operators after having dealt with `??` and `**` (or more) above.
        $string = preg_replace(['/\\\\\?/u', '/\\\\\*/u'], ['[^'.$exclusion_chars.']', '[^'.$exclusion_chars.']*?'], $string);

        // Restore special characters tokenized when we began above.
        foreach (array_reverse($tokens, true) as $_token => $_brackets) {
            // Must go in reverse order so nested tokens unfold properly.
            $string = str_replace('⁅⒯'.$_token.'⒯⁆', $_brackets, $string);
        } // unset($_token, $_brackets); // Housekeeping.

        // Return value based on `$force_match_all`.
        return $force_match_all ? '^'.$string.'$' : $string;
    }
public function frag($value, string $exclusion_chars = '/', bool $force_match_all = false)
    {
        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key => &$_value) {
                $_value = $this->frag($_value, $exclusion_chars, $force_match_all);
            } // unset($_key, $_value); // Housekeeping.
            return $value;
        }
        if (!($string = (string) $value)) {
            return $string; // Empty.
        }
        if (!$exclusion_chars) { // Must have this.
            throw $this->c::issue('Missing `exclusion_chars`.');
        } elseif ($exclusion_chars !== '/') {
            $_exclusion_chars_array = $this->c::mbStrSplit($exclusion_chars);
            if (array_diff($_exclusion_chars_array, ['⁅', '⒯', '⁆', '?', '*']) !== $_exclusion_chars_array) {
                throw $this->c::issue('Invalid `exclusion_chars`. May not exclude: `⁅`, `⒯`, `⁆`, `?` or `*`.');
            } // unset($_exclusion_chars_array); // Housekeeping.
        }
        $tokens          = []; // Initialize.
        $string          = $this->c::escRegex($string);
        $exclusion_chars = $this->c::escRegex($exclusion_chars);

        // Convert `[!a-z0-9]` into a real regex `[^character class]`.
        // Special characters inside a character class have no further meaning.
        // For this reason, we remove them from the equation by tokenizing them, for now.
        $string = preg_replace_callback('/\\\\\[((?:(?:[^[\]]+)|(?R))*)\\\\\]/u', function ($m) use (&$tokens) {
            $m[1] = mb_strpos($m[1], '\\!') === 0 ? '^'.mb_substr($m[1], 2) : $m[1];
            $m[1] = preg_replace('/([a-z0-9])\\\\\-([a-z0-9])/u', '${1}-${2}', $m[1]);
            $tokens[] = '['.$m[1].']';
            return '⁅⒯'.(count($tokens) - 1).'⒯⁆';
        }, $string); // See restoration below.

        // Convert `{}` into `(?:this|that)`; i.e., a list of alternates.
        $string = preg_replace_callback('/\\\\\{((?:(?:[^{}]+)|(?R))*)\\\\\}/u', function ($m) {
            return '(?:'.str_replace(['\\{', '\\}', ','], ['(?:', ')', '|'], $m[1]).')';
        }, $string); // `,` becomes `|` for regex alternation.

        // Deal with `^` and `$`, depending on `$force_match_all`.
        if (!$force_match_all) { // Must come before `$exclusion_chars` below.
            // If not forcing `^$`, we need to treat them as special chars here.
            $string = preg_replace(['/\\\\\^/u', '/\\\\\$/u'], ['^', '$'], $string);
        }
        // `???` (or more) should be treated like `?` instead of as a partial `??` operator.
        $string = preg_replace_callback('/(?:\\\\\?){3,}/u', function ($m) use ($exclusion_chars) {
            return '[^'.$exclusion_chars.']{'.strlen(str_replace('\\', '', $m[0])).'}';
        }, $string); // i.e., Treat as three or more instances of `?` by itself.

        // Now convert `??` and `**` (or more) after having already converted `???`... above.
        $string = preg_replace(['/(?:\\\\\?){2}/u', '/(?:\\\\\*){2,}/u'], ['[\s\S]', '[\s\S]*?'], $string);

        // Now convert any remaining `?` and/or `*` operators after having dealt with `??` and `**` (or more) above.
        $string = preg_replace(['/\\\\\?/u', '/\\\\\*/u'], ['[^'.$exclusion_chars.']', '[^'.$exclusion_chars.']*?'], $string);

        // Restore special characters tokenized when we began above.
        foreach (array_reverse($tokens, true) as $_token => $_brackets) {
            // Must go in reverse order so nested tokens unfold properly.
            $string = str_replace('⁅⒯'.$_token.'⒯⁆', $_brackets, $string);
        } // unset($_token, $_brackets); // Housekeeping.

        // Return value based on `$force_match_all`.
        return $force_match_all ? '^'.$string.'$' : $string;
    }
Convert watered-down regex to real regex fragment.

@since 150424 Watered-down regex.

@param mixed  $value           Input value(s) w/ watered-down regex.
@param string $exclusion_chars The behavior of `*` & `?`. Defaults to excluding `/`.
@param bool   $force_match_all Force `^$` into play & don't treat `^$` as special chars.

@internal `$exclusion_chars` may not contain: `⁅`, `⒯`, `⁆`, `?` or `*`

@return string|array|object Value(s) as true regex fragments.