From 86b4c4ee0acb114c396593d3d143cde5ba5bf6ff Mon Sep 17 00:00:00 2001 From: John Was Date: Mon, 20 Jan 2014 12:24:59 +0100 Subject: [PATCH] improved comments, method visiblity and exceptions --- framework/helpers/BaseFileHelper.php | 96 ++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 25 deletions(-) diff --git a/framework/helpers/BaseFileHelper.php b/framework/helpers/BaseFileHelper.php index ca9328784f..d540accc72 100644 --- a/framework/helpers/BaseFileHelper.php +++ b/framework/helpers/BaseFileHelper.php @@ -10,6 +10,7 @@ namespace yii\helpers; use Yii; +use yii\base\InvalidParamException; /** * BaseFileHelper provides concrete implementation for [[FileHelper]]. @@ -251,17 +252,27 @@ class BaseFileHelper * both '/' and '\' in the paths. * - recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true. * @return array files found under the directory. The file list is sorted. + * @throws InvalidParamException if the dir is invalid. */ public static function findFiles($dir, $options = []) { + if (!is_dir($dir)) { + throw new InvalidParamException('The dir argument must be a directory.'); + } if (!isset($options['basePath'])) { $options['basePath'] = realpath($dir); // this should also be done only once if (isset($options['except'])) { - $options['except'] = array_map(array(__CLASS__, 'parseExcludePattern'), $options['except']); + foreach($options['except'] as $key=>$value) { + if (is_string($value)) + $options['except'][$key] = static::parseExcludePattern($value); + } } if (isset($options['only'])) { - $options['only'] = array_map(array(__CLASS__, 'parseExcludePattern'), $options['only']); + foreach($options['only'] as $key=>$value) { + if (is_string($value)) + $options['only'][$key] = static::parseExcludePattern($value); + } } } $list = []; @@ -304,9 +315,6 @@ class BaseFileHelper } $path = str_replace('\\', '/', $path); - /*if ($isDir = is_dir($path)) { - $path .= '/'; - }*/ if (!empty($options['except'])) { if (($except=self::lastExcludeMatchingFromList($options['basePath'], $path, $options['except'])) !== null) { @@ -350,9 +358,20 @@ class BaseFileHelper return $result; } - public static function matchBasename($baseName, $pattern, $hasWildcard, $flags) + /** + * Performs a simple comparison of file or directory names. + * + * Based on match_basename() from dir.c of git 1.8.5.3 sources. + * + * @param string $baseName file or directory name to compare with the pattern + * @param string $pattern the pattern that $baseName will be compared against + * @param integer|boolean $firstWildcard location of first wildcard character in the $pattern + * @param integer $flags pattern flags + * @return boolean wheter the name matches against pattern + */ + private static function matchBasename($baseName, $pattern, $firstWildcard, $flags) { - if ($hasWildcard === false) { + if ($firstWildcard === false) { if ($pattern === $baseName) { return true; } @@ -366,33 +385,45 @@ class BaseFileHelper return fnmatch($pattern, $baseName, 0); } - public static function matchPathname($path, $basePath, $pattern, $prefix, $flags) + /** + * Compares a path part against a pattern with optional wildcards. + * + * Based on match_pathname() from dir.c of git 1.8.5.3 sources. + * + * @param string $path full path to compare + * @param string $basePath base of path that will not be compared + * @param string $pattern the pattern that path part will be compared against + * @param integer|boolean $firstWildcard location of first wildcard character in the $pattern + * @param integer $flags pattern flags + * @return boolean wheter the path part matches against pattern + */ + private static function matchPathname($path, $basePath, $pattern, $firstWildcard, $flags) { // match with FNM_PATHNAME; the pattern has base implicitly in front of it. if (isset($pattern[0]) && $pattern[0] == '/') { $pattern = StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern)); - if ($prefix !== 0) { - $prefix--; + if ($firstWildcard !== false && $firstWildcard !== 0) { + $firstWildcard--; } } $namelen = StringHelper::byteLength($path) - (empty($basePath) ? 0 : StringHelper::byteLength($basePath) + 1); $name = StringHelper::byteSubstr($path, -$namelen, $namelen); - if ($prefix !== 0) { - if ($prefix === false) { - $prefix = StringHelper::byteLength($pattern); + if ($firstWildcard !== 0) { + if ($firstWildcard === false) { + $firstWildcard = StringHelper::byteLength($pattern); } // if the non-wildcard part is longer than the remaining pathname, surely it cannot match. - if ($prefix > $namelen) { + if ($firstWildcard > $namelen) { return false; } - if (strncmp($pattern, $name, $prefix)) { + if (strncmp($pattern, $name, $firstWildcard)) { return false; } - $pattern = StringHelper::byteSubstr($pattern, $prefix, StringHelper::byteLength($pattern)); - $name = StringHelper::byteSubstr($name, $prefix, $namelen); + $pattern = StringHelper::byteSubstr($pattern, $firstWildcard, StringHelper::byteLength($pattern)); + $name = StringHelper::byteSubstr($name, $firstWildcard, $namelen); // If the whole pattern did not have a wildcard, then our prefix match is all we need; we do not need to call fnmatch at all. if (empty($pattern) && empty($name)) { @@ -415,40 +446,50 @@ class BaseFileHelper * @param string $path * @param array $excludes list of patterns to match $path against * @return string null or one of $excludes item as an array with keys: 'pattern', 'flags' + * @throws InvalidParamException if any of the exclude patterns is not a string or an array with keys: pattern, flags, firstWildcard. */ - public static function lastExcludeMatchingFromList($basePath, $path, $excludes) + private static function lastExcludeMatchingFromList($basePath, $path, $excludes) { foreach(array_reverse($excludes) as $exclude) { if (is_string($exclude)) { $exclude = self::parseExcludePattern($exclude); } + if (!isset($exclude['pattern']) || !isset($exclude['flags']) || !isset($exclude['firstWildcard'])) { + throw new InvalidParamException('If exclude/include pattern is an array it must contain the pattern, flags and firstWildcard keys.'); + } if ($exclude['flags'] & self::EXC_FLAG_MUSTBEDIR && !is_dir($path)) { continue; } if ($exclude['flags'] & self::EXC_FLAG_NODIR) { - if (self::matchBasename(basename($path), $exclude['pattern'], $exclude['hasWildcard'], $exclude['flags'])) { + if (self::matchBasename(basename($path), $exclude['pattern'], $exclude['firstWildcard'], $exclude['flags'])) { return $exclude; } continue; } - if (self::matchPathname($path, $basePath, $exclude['pattern'], $exclude['hasWildcard'], $exclude['flags'])) { + if (self::matchPathname($path, $basePath, $exclude['pattern'], $exclude['firstWildcard'], $exclude['flags'])) { return $exclude; } } return null; } - public static function parseExcludePattern($pattern) + /** + * Processes the pattern, stripping special characters like / and ! from the beginning and settings flags instead. + * @param string $pattern + * @return array with keys: (string)pattern, (int)flags, (int|boolean)firstWildcard + * @throws InvalidParamException if the pattern is not a string. + */ + private static function parseExcludePattern($pattern) { if (!is_string($pattern)) { - throw new \yii\base\Exception('Exclude/include pattern must be a string.'); + throw new InvalidParamException('Exclude/include pattern must be a string.'); } $result = array( 'pattern' => $pattern, 'flags' => 0, - 'hasWildcard' => false, + 'firstWildcard' => false, ); if (!isset($pattern[0])) return $result; @@ -465,14 +506,19 @@ class BaseFileHelper } if (strpos($pattern, '/') === false) $result['flags'] |= self::EXC_FLAG_NODIR; - $result['hasWildcard'] = self::firstWildcardInPattern($pattern); + $result['firstWildcard'] = self::firstWildcardInPattern($pattern); if ($pattern[0] == '*' && self::firstWildcardInPattern(StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern))) === false) $result['flags'] |= self::EXC_FLAG_ENDSWITH; $result['pattern'] = $pattern; return $result; } - public static function firstWildcardInPattern($pattern) + /** + * Searches for the first wildcard character in the pattern. + * @param string $pattern the pattern to search in + * @return integer|boolean position of first wildcard character or false if not found + */ + private static function firstWildcardInPattern($pattern) { $wildcards = array('*','?','[','\\'); $wildcardSearch = function($r, $c) use ($pattern) {