mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-03 05:48:11 +08:00
Added 'caseless' option to yii\helpers\BaseFileHelper::findFiles()
This commit is contained in:
@ -185,6 +185,7 @@ Yii Framework 2 Change Log
|
|||||||
- Enh #4636: Added `yii\web\Response::setDownloadHeaders()` (pawzar)
|
- Enh #4636: Added `yii\web\Response::setDownloadHeaders()` (pawzar)
|
||||||
- Enh #4644: Added `yii\db\Schema::createColumnSchema()` to be able to customize column schema used (mcd-php)
|
- Enh #4644: Added `yii\db\Schema::createColumnSchema()` to be able to customize column schema used (mcd-php)
|
||||||
- Enh #4656: HtmlPurifier helper config can now be a closure to change the purifier config object after it was created (Alex-Code)
|
- Enh #4656: HtmlPurifier helper config can now be a closure to change the purifier config object after it was created (Alex-Code)
|
||||||
|
- Enh #4062: Added 'caseless' option to `yii\helpers\BaseFileHelper::findFiles()` (klimov-paul)
|
||||||
- Enh #4691: Encoding on `ActiveForm` and `ActiveField` validation errors is now configurable (Alex-Code)
|
- Enh #4691: Encoding on `ActiveForm` and `ActiveField` validation errors is now configurable (Alex-Code)
|
||||||
- Enh #4740: Added `yii\web\Session::addFlash()` (restyler)
|
- Enh #4740: Added `yii\web\Session::addFlash()` (restyler)
|
||||||
- Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue)
|
- Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue)
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class BaseFileHelper
|
|||||||
const PATTERN_ENDSWITH = 4;
|
const PATTERN_ENDSWITH = 4;
|
||||||
const PATTERN_MUSTBEDIR = 8;
|
const PATTERN_MUSTBEDIR = 8;
|
||||||
const PATTERN_NEGATIVE = 16;
|
const PATTERN_NEGATIVE = 16;
|
||||||
|
const PATTERN_CASELESS = 32;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,6 +228,7 @@ class BaseFileHelper
|
|||||||
* apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b';
|
* apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b';
|
||||||
* and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches
|
* and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches
|
||||||
* both '/' and '\' in the paths.
|
* both '/' and '\' in the paths.
|
||||||
|
* - caseless: boolean, whether patterns specified at "only" or "except" should be case insensitive. Defaults to false.
|
||||||
* - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true.
|
* - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true.
|
||||||
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
|
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
|
||||||
* If the callback returns false, the copy operation for the sub-directory or file will be cancelled.
|
* If the callback returns false, the copy operation for the sub-directory or file will be cancelled.
|
||||||
@ -248,7 +250,9 @@ class BaseFileHelper
|
|||||||
throw new InvalidParamException('Unable to open directory: ' . $src);
|
throw new InvalidParamException('Unable to open directory: ' . $src);
|
||||||
}
|
}
|
||||||
if (!isset($options['basePath'])) {
|
if (!isset($options['basePath'])) {
|
||||||
|
// this should be done only once
|
||||||
$options['basePath'] = realpath($src);
|
$options['basePath'] = realpath($src);
|
||||||
|
$options = self::normalizeOptions($options);
|
||||||
}
|
}
|
||||||
while (($file = readdir($handle)) !== false) {
|
while (($file = readdir($handle)) !== false) {
|
||||||
if ($file === '.' || $file === '..') {
|
if ($file === '.' || $file === '..') {
|
||||||
@ -342,6 +346,7 @@ class BaseFileHelper
|
|||||||
* - only: array, list of patterns that the file paths should match if they are to be returned. Directory paths are not checked against them.
|
* - only: array, list of patterns that the file paths should match if they are to be returned. Directory paths are not checked against them.
|
||||||
* Same pattern matching rules as in the "except" option are used.
|
* Same pattern matching rules as in the "except" option are used.
|
||||||
* If a file path matches a pattern in both "only" and "except", it will NOT be returned.
|
* If a file path matches a pattern in both "only" and "except", it will NOT be returned.
|
||||||
|
* - caseless: boolean, whether patterns specified at "only" or "except" should be case insensitive. Defaults to false.
|
||||||
* - recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true.
|
* - 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.
|
* @return array files found under the directory. The file list is sorted.
|
||||||
* @throws InvalidParamException if the dir is invalid.
|
* @throws InvalidParamException if the dir is invalid.
|
||||||
@ -353,22 +358,9 @@ class BaseFileHelper
|
|||||||
}
|
}
|
||||||
$dir = rtrim($dir, DIRECTORY_SEPARATOR);
|
$dir = rtrim($dir, DIRECTORY_SEPARATOR);
|
||||||
if (!isset($options['basePath'])) {
|
if (!isset($options['basePath'])) {
|
||||||
|
// this should be done only once
|
||||||
$options['basePath'] = realpath($dir);
|
$options['basePath'] = realpath($dir);
|
||||||
// this should also be done only once
|
$options = self::normalizeOptions($options);
|
||||||
if (isset($options['except'])) {
|
|
||||||
foreach ($options['except'] as $key => $value) {
|
|
||||||
if (is_string($value)) {
|
|
||||||
$options['except'][$key] = self::parseExcludePattern($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset($options['only'])) {
|
|
||||||
foreach ($options['only'] as $key => $value) {
|
|
||||||
if (is_string($value)) {
|
|
||||||
$options['only'][$key] = self::parseExcludePattern($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$list = [];
|
$list = [];
|
||||||
$handle = opendir($dir);
|
$handle = opendir($dir);
|
||||||
@ -485,7 +477,12 @@ class BaseFileHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fnmatch($pattern, $baseName, 0);
|
$fnmatchFlags = 0;
|
||||||
|
if ($flags & self::PATTERN_CASELESS) {
|
||||||
|
$fnmatchFlags |= FNM_CASEFOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fnmatch($pattern, $baseName, $fnmatchFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -534,7 +531,12 @@ class BaseFileHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fnmatch($pattern, $name, FNM_PATHNAME);
|
$fnmatchFlags = FNM_PATHNAME;
|
||||||
|
if ($flags & self::PATTERN_CASELESS) {
|
||||||
|
$fnmatchFlags |= FNM_CASEFOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fnmatch($pattern, $name, $fnmatchFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -555,7 +557,7 @@ class BaseFileHelper
|
|||||||
{
|
{
|
||||||
foreach (array_reverse($excludes) as $exclude) {
|
foreach (array_reverse($excludes) as $exclude) {
|
||||||
if (is_string($exclude)) {
|
if (is_string($exclude)) {
|
||||||
$exclude = self::parseExcludePattern($exclude);
|
$exclude = self::parseExcludePattern($exclude, false);
|
||||||
}
|
}
|
||||||
if (!isset($exclude['pattern']) || !isset($exclude['flags']) || !isset($exclude['firstWildcard'])) {
|
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.');
|
throw new InvalidParamException('If exclude/include pattern is an array it must contain the pattern, flags and firstWildcard keys.');
|
||||||
@ -582,19 +584,26 @@ class BaseFileHelper
|
|||||||
/**
|
/**
|
||||||
* Processes the pattern, stripping special characters like / and ! from the beginning and settings flags instead.
|
* Processes the pattern, stripping special characters like / and ! from the beginning and settings flags instead.
|
||||||
* @param string $pattern
|
* @param string $pattern
|
||||||
|
* @param boolean $caseLess
|
||||||
|
* @throws \yii\base\InvalidParamException
|
||||||
* @return array with keys: (string) pattern, (int) flags, (int|boolean)firstWildcard
|
* @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)
|
private static function parseExcludePattern($pattern, $caseLess)
|
||||||
{
|
{
|
||||||
if (!is_string($pattern)) {
|
if (!is_string($pattern)) {
|
||||||
throw new InvalidParamException('Exclude/include pattern must be a string.');
|
throw new InvalidParamException('Exclude/include pattern must be a string.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = [
|
$result = [
|
||||||
'pattern' => $pattern,
|
'pattern' => $pattern,
|
||||||
'flags' => 0,
|
'flags' => 0,
|
||||||
'firstWildcard' => false,
|
'firstWildcard' => false,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($caseLess) {
|
||||||
|
$result['flags'] |= self::PATTERN_CASELESS;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($pattern[0])) {
|
if (!isset($pattern[0])) {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
@ -635,4 +644,27 @@ class BaseFileHelper
|
|||||||
|
|
||||||
return array_reduce($wildcards, $wildcardSearch, false);
|
return array_reduce($wildcards, $wildcardSearch, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $options raw options
|
||||||
|
* @return array normalized options
|
||||||
|
*/
|
||||||
|
private static function normalizeOptions(array $options)
|
||||||
|
{
|
||||||
|
if (isset($options['except'])) {
|
||||||
|
foreach ($options['except'] as $key => $value) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
$options['except'][$key] = self::parseExcludePattern($value, !empty($options['caseless']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($options['only'])) {
|
||||||
|
foreach ($options['only'] as $key => $value) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
$options['only'][$key] = self::parseExcludePattern($value, !empty($options['caseless']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -407,6 +407,36 @@ class FileHelperTest extends TestCase
|
|||||||
$this->assertEquals($expect, $foundFiles);
|
$this->assertEquals($expect, $foundFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testFindFilesExclude
|
||||||
|
*/
|
||||||
|
public function testFindFilesCaseLess()
|
||||||
|
{
|
||||||
|
$dirName = 'test_dir';
|
||||||
|
$this->createFileStructure([
|
||||||
|
$dirName => [
|
||||||
|
'lower.txt' => 'lower case filename',
|
||||||
|
'upper.TXT' => 'upper case filename',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$basePath = $this->testFilePath;
|
||||||
|
$dirName = $basePath . DIRECTORY_SEPARATOR . $dirName;
|
||||||
|
|
||||||
|
$options = [
|
||||||
|
'except' => ['*.txt'],
|
||||||
|
'caseless' => true
|
||||||
|
];
|
||||||
|
$foundFiles = FileHelper::findFiles($dirName, $options);
|
||||||
|
$this->assertCount(0, $foundFiles);
|
||||||
|
|
||||||
|
$options = [
|
||||||
|
'only' => ['*.txt'],
|
||||||
|
'caseless' => true
|
||||||
|
];
|
||||||
|
$foundFiles = FileHelper::findFiles($dirName, $options);
|
||||||
|
$this->assertCount(2, $foundFiles);
|
||||||
|
}
|
||||||
|
|
||||||
public function testCreateDirectory()
|
public function testCreateDirectory()
|
||||||
{
|
{
|
||||||
$basePath = $this->testFilePath;
|
$basePath = $this->testFilePath;
|
||||||
|
|||||||
Reference in New Issue
Block a user