That way we can typehint regexes and have object-oriented access on regexes.
There is no longer a need to add or escape the delimiters, this is all done by the Regex-class.
The modifiers are added via a bitmask as the second parameter:
Valid modifiers are:
Regex::CASE_INSENSITIVE (add i)
Regex::EVAL_REPLACEMENT (add e)
Regex::UNGREEDY (add U)
Regex::NO_ANALYSE (don't add s)
Usage:
```
$regex = new Regex('^http(s)?://(www\.)woltlab\.(com|de|info)/', Regex::CASE_INSENSITIVE);
var_dump($regex->match('http://www.woltlab.com/pluginstore/')); // int(1)
var_dump($regex->getMatches()); // array with all matched substrings
var_dump(Regex::compile('[a-z]')->replace('asdf345', '')); // string(3) "345"
var_dump(Regex::compile('[a-z]')->replace('asdf345', new Callback(function ($matches) {
return 'x';
})); // string(7) "xxxx345"
```
use wcf\system\io\File;
use wcf\system\language\LanguageFactory;
use wcf\system\package\PackageDependencyHandler;
+use wcf\system\Regex;
use wcf\system\WCF;
use wcf\util\XML;
use wcf\util\DirectoryUtil;
if ($languageID != '.*') $languageID = intval($languageID);
if ($packageID != '.*') $packageID = intval($packageID);
- DirectoryUtil::getInstance(WCF_DIR.'language/')->removePattern('~'.$packageID.'_'.$languageID.'_'.$category.'\.php$~');
+ DirectoryUtil::getInstance(WCF_DIR.'language/')->removePattern(new Regex($packageID.'_'.$languageID.'_'.$category.'\.php$'));
}
/**
*/
public function deleteCompiledTemplates() {
// templates
- DirectoryUtil::getInstance(WCF_DIR.'templates/compiled/')->removePattern('~.*_'.$this->languageID.'_.*\.php$~');
+ DirectoryUtil::getInstance(WCF_DIR.'templates/compiled/')->removePattern(new Regex('.*_'.$this->languageID.'_.*\.php$'));
// acp templates
- DirectoryUtil::getInstance(WCF_DIR.'acp/templates/compiled/')->removePattern('~.*_'.$this->languageID.'_.*\.php$~');
+ DirectoryUtil::getInstance(WCF_DIR.'acp/templates/compiled/')->removePattern(new Regex('.*_'.$this->languageID.'_.*\.php$'));
}
/**
namespace wcf\data\template;
use wcf\data\DatabaseObjectEditor;
use wcf\system\io\File;
+use wcf\system\Regex;
use wcf\system\WCF;
use wcf\util\DirectoryUtil;
* Deletes the compiled files of this template.
*/
public function deleteCompiledFiles() {
- DirectoryUtil::getInstance(WCF_DIR . 'templates/compiled/')->removePattern('~' . intval($this->packageID) . '_.*_' . preg_quote($this->templateName, '~') . '.php~');
+ DirectoryUtil::getInstance(WCF_DIR . 'templates/compiled/')->removePattern(new Regex(intval($this->packageID) . '_.*_' . preg_quote($this->templateName) . '.php$'));
}
}
--- /dev/null
+<?php
+namespace wcf\system;
+use \wcf\system\exception\SystemException;
+
+/**
+ * Represents a regex.
+ *
+ * @author Tim Düsterhus
+ * @copyright 2011 Tim Düsterhus
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system
+ * @category Community Framework
+ */
+final class Regex {
+ /**
+ * The delimiter that is used internally.
+ *
+ * @var string
+ */
+ const REGEX_DELIMITER = '/';
+
+ /**
+ * Do not apply any modifiers.
+ *
+ * @var integer
+ */
+ const MODIFIER_NONE = 0;
+
+ /**
+ * Case insensitive matching.
+ *
+ * @var integer
+ */
+ const CASE_INSENSITIVE = 1;
+
+ /**
+ * Ungreedy matching.
+ *
+ * @var integer
+ */
+ const UNGREEDY = 2;
+
+ /**
+ * eval() replacement of Regex::replace()
+ *
+ * @var integer
+ */
+ const EVAL_REPLACEMENT = 4;
+
+ /**
+ * Do not spend extra time on analysing.
+ *
+ * @var integer
+ */
+ const NO_ANALYSE = 8;
+
+ /**
+ * The compiled regex (:D)
+ *
+ * @var string
+ */
+ private $regex = '';
+
+ /**
+ * The last matches
+ *
+ * @var array
+ */
+ private $matches = array();
+
+ /**
+ * Creates a regex.
+ *
+ * @param string $regex
+ * @param integer $modifier
+ */
+ public function __construct($regex, $modifier = self::MODIFIER_NONE) {
+ // escape delimiter
+ $regex = str_replace(self::REGEX_DELIMITER, '\\'.self::REGEX_DELIMITER, $regex);
+
+ // add delimiter
+ $this->regex = self::REGEX_DELIMITER.$regex.self::REGEX_DELIMITER;
+
+ // add modifiers
+ if ($modifier & self::CASE_INSENSITIVE) $this->regex .= 'i';
+ if ($modifier & self::UNGREEDY) $this->regex .= 'U';
+ if ($modifier & self::EVAL_REPLACEMENT) $this->regex .= 'e';
+ if (~$modifier & self::NO_ANALYSE) $this->regex .= 's';
+ }
+
+ /**
+ * @see Regex::__construct()
+ */
+ public static function compile($regex, $modifier = self::MODIFIER_NONE) {
+ return new self($regex, $modifier);
+ }
+
+ /**
+ * @see Regex::match()
+ */
+ public function __invoke($string) {
+ return $this->match($string);
+ }
+
+ /**
+ * Checks whether the regex matches the given string.
+ *
+ * @param string $string String to match.
+ * @param boolean $all Find all matches.
+ * @return integer Return value of preg_match(_all)
+ */
+ public function match($string, $all = false) {
+ if ($all) {
+ $result = preg_match_all($this->regex, $string, $this->matches);
+ }
+ else {
+ $result = preg_match($this->regex, $string, $this->matches);
+ }
+
+ if ($result === false) {
+ throw new SystemException('Could not execute match on '.$this->regex);
+ }
+ return $result;
+ }
+
+ /**
+ * Replaces part of the string with the regex.
+ *
+ * @param string $string String to work on.
+ * @param mixed $replacement Either replacement-string or instance of \wcf\system\Callback
+ * @return string
+ */
+ public function replace($string, $replacement) {
+ if ($replacement instanceof Callback) {
+ $result = preg_replace_callback($this->regex, $replacement, $string);
+ }
+ else {
+ $result = preg_replace($this->regex, $replacement, $string);
+ }
+
+ if ($result === false) {
+ throw new SystemException('Could not execute replace on '.$this->regex);
+ }
+ return $result;
+ }
+
+ /**
+ * Splits the string with the regex.
+ *
+ * @param string $string String to split.
+ * @return array<string>
+ */
+ public function split($string) {
+ $result = preg_split($this->regex, $string);
+
+ if ($result === false) {
+ throw new SystemException('Could not execute split on '.$this->regex);
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the matches of the last string.
+ *
+ * @return array
+ */
+ public function getMatches() {
+ return $this->matches;
+ }
+
+ /**
+ * Returns the compiled regex.
+ *
+ * @return string
+ */
+ public function getRegex() {
+ return $this->regex;
+ }
+}
use wcf\system\io\Tar;
use wcf\system\language\LanguageFactory;
use wcf\system\package\PackageArchive;
+use wcf\system\Regex;
use wcf\system\session\ACPSessionFactory;
use wcf\system\session\SessionHandler;
use wcf\system\setup\Installer;
// delete tmp files
$directory = TMP_DIR.'/';
- DirectoryUtil::getInstance($directory)->removePattern('~\.tar(\.gz)?$~', true);
+ DirectoryUtil::getInstance($directory)->removePattern(new Regex('\.tar(\.gz)?$'), true);
}
/**
use wcf\system\exception\SystemException;
use wcf\system\io\File;
use wcf\system\Callback;
+use wcf\system\Regex;
use wcf\system\WCF;
use wcf\util\FileUtil;
use wcf\util\DirectoryUtil;
if (!@touch($filename, 1)) {
@unlink($filename);
}
- }), '%^'.$directory.$filepattern.'$%i');
+ }), new Regex('^'.$directory.$filepattern.'$', Regex::CASE_INSENSITIVE));
}
/**
while ($row = $statement->fetchArray()) {
$packageDir = FileUtil::getRealPath(WCF_DIR.$row['packageDir']);
$cacheDir = $packageDir.'cache';
- DirectoryUtil::getInstance($cacheDir)->removePattern('~.*\.php$~');
+ DirectoryUtil::getInstance($cacheDir)->removePattern(new Regex('.*\.php$'));
}
}
}
use wcf\system\cache\CacheHandler;
use wcf\system\event\EventHandler;
use wcf\system\exception\SystemException;
+use wcf\system\Regex;
use wcf\system\SingletonFactory;
use wcf\util\HeaderUtil;
use wcf\util\StringUtil;
if (empty($compileDir)) $compileDir = WCF_DIR.'templates/compiled/';
// delete compiled templates
- DirectoryUtil::getInstance($compileDir)->removePattern('~.*_.*_.*\.php$~');
+ DirectoryUtil::getInstance($compileDir)->removePattern(new Regex('.*_.*_.*\.php$'));
}
/**
<?php
namespace wcf\util;
use wcf\system\Callback;
+use wcf\system\Regex;
use wcf\system\exception\SystemException;
/**
/**
* Returns a sorted list of files.
*
- * @param integer $order sort-order
- * @param string $pattern pattern to match
- * @param boolean $negativeMatch true if the pattern should be inversed
+ * @param integer $order sort-order
+ * @param wcf\system\Regex $pattern pattern to match
+ * @param boolean $negativeMatch true if the pattern should be inversed
* @return array<string>
*/
- public function getFiles($order = SORT_ASC, $pattern = '', $negativeMatch = false) {
+ public function getFiles($order = SORT_ASC, Regex $pattern = null, $negativeMatch = false) {
// scan the folder
$this->scanFiles();
$files = $this->files;
// sort out non matching files
- if (!empty($pattern)) {
+ if ($pattern !== null) {
foreach ($files as $filename => $value) {
- if (((bool) preg_match($pattern, $filename)) == $negativeMatch) unset($files[$filename]);
+ if (((bool) $pattern->match($filename)) === $negativeMatch) unset($files[$filename]);
}
}
* Returns a sorted list of files, with DirectoryIterator object as value
*
* @param integer $order sort order
- * @param string $pattern pattern to match
+ * @param wcf\system\Regex $pattern pattern to match
* @param boolean $negativeMatch should the pattern be inversed
* @return array<\DirectoryIterator>
*/
- public function getFileObjects($order = SORT_ASC, $pattern = '', $negativeMatch = false) {
+ public function getFileObjects($order = SORT_ASC, Regex $pattern = null, $negativeMatch = false) {
// scan the folder
$this->scanFileObjects();
$objects = $this->fileObjects;
// sort out non matching files
- if (!empty($pattern)) {
+ if ($pattern !== null) {
foreach ($objects as $filename => $value) {
- if (((bool) preg_match($pattern, $filename)) == $negativeMatch) unset($objects[$filename]);
+ if (((bool) $pattern->match($filename)) === $negativeMatch) unset($objects[$filename]);
}
}
* Executes a callback on each file and returns false if callback is invalid.
*
* @param wcf\system\Callback $callback
- * @param string $pattern callback is only applied to files matching the given pattern
+ * @param wcf\system\Regex $pattern callback is only applied to files matching the given pattern
* @return boolean
*/
- public function executeCallback(Callback $callback, $pattern = '') {
- $files = $this->getFileObjects(self::SORT_NONE, $pattern);
+ public function executeCallback(Callback $callback, Regex $pattern = null) {
+ if ($pattern !== null) $files = $this->getFileObjects(self::SORT_NONE, $pattern);
+ else $files = $this->getFileObjects(self::SORT_NONE);
+
foreach ($files as $filename => $obj) {
$callback($filename, $obj);
}
* Recursive remove of directory.
*/
public function removeAll() {
- $this->removePattern('');
+ $this->removePattern(new Regex('.'));
// destroy cached instance
unset(static::$instances[$this->recursive][$this->directory]);
/**
* Removes all files that match the given pattern.
*
- * @param string $pattern pattern to match
+ * @param wcf\system\Regex $pattern pattern to match
* @param boolean $negativeMatch should the pattern be inversed
*/
- public function removePattern($pattern, $negativeMatch = false) {
+ public function removePattern(Regex $pattern, $negativeMatch = false) {
if (!$this->recursive) throw new SystemException('Removing of files only works in recursive mode');
$files = $this->getFileObjects(self::SORT_NONE, $pattern, $negativeMatch);