/** * Decides for the given $pattern whether its a valid regular expression acceptable for * Arrays parser functions or not. * * @param string $pattern regular expression including delimiters and optional flags * @param bool $forRegexFun whether the regular expression is inteded to be used with 'Regex Fun' * if supported by the wikis infrastructure. In case 'Regex Fun' is not available, * the default validation will be used. * * @return boolean */ static function isValidRegEx($pattern, $forRegexFun = false) { if ($forRegexFun && self::hasRegexFunSupport()) { return ExtRegexFun::validateRegex($pattern); } if (!preg_match('/^([\\/\\|%]).*\\1[imsSuUx]*$/', $pattern)) { return false; } wfSuppressWarnings(); // instead of using the evil @ operator! $isValid = false !== preg_match($pattern, ' '); // preg_match returns false on error wfRestoreWarnings(); return $isValid; }
/** * 'preg_replace'-like function but can handle special modifiers 'e' and 'r'. * * @since 1.1 * * @param string &$pattern * @param PPNode|string $replacement should be a PPNode in case 'e' flag might be used since in that * case the string will be expanded after back-refs are inserted. Otherwise string is ok. * @param string $subject * @param int $limit * @param Parser &$parser if 'e' flag should be allowed, a parser object for parsing is required. * @param PPFrame $frame which keeps template parameters which should be used in case 'e' flag is set. * @param array $allowedSpecialFlags all special flags that should be handled, by default 'e' and 'r'. */ public static function doPregReplace($pattern, $replacement, $subject, $limit = -1, &$parser = null, $frame = null, array $allowedSpecialFlags = array(self::FLAG_REPLACEMENT_PARSE, self::FLAG_NO_REPLACE_NO_OUT)) { static $lastPattern = null; static $activePattern = null; static $specialFlags = null; /* * cache validated pattern and use it as long as nothing has changed, this makes things * faster in case we do a lot of stuff with the same regex. */ if ($lastPattern === null || $lastPattern !== $pattern) { // remember pattern without validation $lastPattern = $pattern; // if allowed special flags change, we have to validate again^^ $lastFlags = implode(',', $allowedSpecialFlags); // validate regex and get special flags 'e' and 'r' if given: if (!self::validateRegex($pattern, $specialFlags)) { // invalid regex! $lastPattern = null; return false; } // set validated pattern as active one $activePattern = $pattern; // filter unwanted special flags: $allowedSpecialFlags = array_flip($allowedSpecialFlags); $specialFlags = array_intersect_key($specialFlags, $allowedSpecialFlags); } else { // set last validated pattern without flags 'e' and 'r' $pattern = $activePattern; } // make sure we have a frame to expand the $replacement (necessary for 'e' flag support!) if ($frame === null) { // new frame without template parameters then $frame = $parser->getPreprocessor()->newCustomFrame(array()); } // FLAG 'e' (parse replace after match) handling: if (!empty($specialFlags[self::FLAG_REPLACEMENT_PARSE])) { // 'e' requires a Parser for parsing! if (!$parser instanceof Parser) { // no valid Parser object, without, we can't parse anything! throw new MWException("Regex Fun 'e' flag discovered but no Parser object given!"); } // if 'e' flag is set, each replacement has to be parsed after matches are inserted but before replacing! self::$tmpRegexCB = array('replacement' => $replacement, 'parser' => &$parser, 'frame' => $frame, 'internal' => isset($parser->mExtRegexFun['lastMatches']) && $parser->mExtRegexFun['lastMatches'] === false); // do the actual replacement with special 'e' flag handling $output = preg_replace_callback($pattern, array(__CLASS__, 'doPregReplace_eFlag_callback'), $subject, $limit, $count); } else { $replacement = trim($frame->expand($replacement)); // no 'e' flag, we can perform the standard function $output = preg_replace($pattern, $replacement, $subject, $limit, $count); } // FLAG 'r' (no replacement - no output) handling: if (!empty($specialFlags[self::FLAG_NO_REPLACE_NO_OUT])) { /* * only output replacement result if there actually was a match and therewith a replacement happened * (otherwise the input string would be returned) */ if ($count < 1) { return ''; } } return $output; }