/** * Expands GLOB resource patterns. * * @param string $resource Resource name. * @param string $type Type of the resource. * @return array */ public function expand($resource, $type) { list($moduleName, $subDir, $filePattern) = $this->parseResourceName($resource); $resourceLocation = $moduleName . ':' . $subDir . ':'; // read from application dir $appDir = rtrim($this->_application->getApplicationDir(), DS) . DS . 'Resources' . DS; // if a module name was specified then point to a subfolder with the module name if (!empty($moduleName)) { // check for module existence in the first place if (!$this->_application->hasModule($moduleName)) { throw new ResourceNotFoundException('There is no module "' . $moduleName . '" registered, so cannot find its resource.'); } $appDir = $appDir . $moduleName . DS; } $appDir = $this->buildResourcePath($appDir, $type, $subDir, ''); $appFiles = FilesystemUtils::glob($appDir . $filePattern, FilesystemUtils::GLOB_ROOTFIRST | GLOB_BRACE); $resources = array(); foreach ($appFiles as $file) { $resources[] = $resourceLocation . substr($file, mb_strlen($appDir)); } // now take care of the module dir if ($moduleName) { $module = $this->_application->getModule($moduleName); $moduleDir = rtrim($module->getModuleDir(), DS) . DS . 'Resources' . DS; $moduleDir = $this->buildResourcePath($moduleDir, $type, $subDir, ''); $moduleFiles = FilesystemUtils::glob($moduleDir . $filePattern, GLOB_BRACE); foreach ($moduleFiles as $file) { $resources[] = $resourceLocation . substr($file, mb_strlen($moduleDir)); } } $resources = array_unique($resources); return ArrayUtils::sortPaths($resources, true); }
public function testSortPaths() { $paths = array('app.js', 'app.min.js', 'dolor.txt', 'global.js', 'global.min.js', 'ipsum.txt', 'lorem.txt', 'company/bilbo.txt', 'company/dwarves/bifur.txt', 'company/dwarves/bombur.txt', 'company/dwarves/oin.txt', 'company/dwarves/oin.txt', 'company/dwarves/thorin.txt', 'company/wizards/gandalf.txt', 'company/wizards/radagast.txt', 'lipsum/root.js', 'lipsum/dolor/dolor.txt', 'lipsum/dolor/valuptatos.js', 'lipsum/dolor/amet/adipiscit.txt', 'lipsum/dolor/amet/elit.txt', 'lipsum/dolor/amet/lorem.txt', 'newdir/file.txt', 'newdir/dummy/dummy.js', 'newdir/dummy/leaf.txt'); shuffle($paths); $this->assertEquals(array('company/dwarves/bifur.txt', 'company/dwarves/bombur.txt', 'company/dwarves/oin.txt', 'company/dwarves/oin.txt', 'company/dwarves/thorin.txt', 'company/wizards/gandalf.txt', 'company/wizards/radagast.txt', 'company/bilbo.txt', 'lipsum/dolor/amet/adipiscit.txt', 'lipsum/dolor/amet/elit.txt', 'lipsum/dolor/amet/lorem.txt', 'lipsum/dolor/dolor.txt', 'lipsum/dolor/valuptatos.js', 'lipsum/root.js', 'newdir/dummy/dummy.js', 'newdir/dummy/leaf.txt', 'newdir/file.txt', 'app.js', 'app.min.js', 'dolor.txt', 'global.js', 'global.min.js', 'ipsum.txt', 'lorem.txt'), ArrayUtils::sortPaths($paths), 'Failed to sort with child first.'); shuffle($paths); $this->assertEquals(array('app.js', 'app.min.js', 'dolor.txt', 'global.js', 'global.min.js', 'ipsum.txt', 'lorem.txt', 'company/bilbo.txt', 'company/dwarves/bifur.txt', 'company/dwarves/bombur.txt', 'company/dwarves/oin.txt', 'company/dwarves/oin.txt', 'company/dwarves/thorin.txt', 'company/wizards/gandalf.txt', 'company/wizards/radagast.txt', 'lipsum/root.js', 'lipsum/dolor/dolor.txt', 'lipsum/dolor/valuptatos.js', 'lipsum/dolor/amet/adipiscit.txt', 'lipsum/dolor/amet/elit.txt', 'lipsum/dolor/amet/lorem.txt', 'newdir/file.txt', 'newdir/dummy/dummy.js', 'newdir/dummy/leaf.txt'), ArrayUtils::sortPaths($paths, true), 'Failed to sort with root first.'); }
/** * Extended `glob()` functionality that supports double star `**` (globstar) wildcard. * * PHP's `glob()` implementation doesn't allow for `**` wildcard. In Bash 4 it can be enabled with `globstar` setting. * * In case the `**` wildcard is not used in the pattern then this method just calls PHP's `glob()`. * * For full documentation see PHP's [`glob()` documentation](http://php.net/manual/en/function.glob.php). * * It's worth noting that if you want to find files inside current directory and their subdirectories, * then you have to use a `GLOB_BRACE` flag and pattern, e.g.: * * echo \MD\Foundation\Utils\FilesystemUtils::glob('{,** /}*.js', GLOB_BRACE); // note: remove space between * and / * // -> array( * // 'main.js', * // 'dir/script.js', * // 'dir/more/scripts.js' * // ); * * Implementation of this convention varies between libs in various languages and `MD\Foundation` sticks * with what [Bash manual states](http://www.gnu.org/software/bash/manual/bashref.html#Pattern-Matching). * More about this is explained in [#2](https://github.com/michaldudek/Foundation/issues/2). * * This function also supports its own implementation of `GLOB_BRACE` flag on systems that do not support it * (e.g. Alpine Linux, popular base for Docker containers). Because it's impossible to detect if that flag was * passed or not (as it has `null` or `0` value), on such systems the function assumes that yes, this flag was * passed if there are any `{}` braces used in the pattern. This implementation might not be so fast as a system * implementation, so use with caution or switch to "fuller" distro. * * Additionally it provides sorting option to the results, which you can pass along with * other flags. Constants `FilesystemUtils::GLOB_ROOTFIRST` and `FilesystemUtils::GLOB_CHILDFIRST` * sort the results either as "root first" where files in a directory are listed before directories and * subdirectories, or "child first" where subdirectories are listed before files. * * @param string $pattern The pattern. Supports `**` wildcard. * @param int $flags [optional] `glob()` flags. See `glob()`'s documentation. Default: `0`. * @return array|bool */ public static function glob($pattern, $flags = 0) { // turn off custom flags $globFlags = ($flags | static::GLOB_CHILDFIRST | static::GLOB_ROOTFIRST) ^ (static::GLOB_CHILDFIRST | static::GLOB_ROOTFIRST); // our custom implementation will be expanding some patterns (namely globstar and braces) so we gather them all $patterns = array($pattern); // expand GLOB_BRACE if it's not defined on the system (e.g. Alpine Linux) // let's assume that it's passed always in such cases (most common usage) // and keeping the original pattern will make us safe to detect the desired files with {} in name anyway if (!defined('GLOB_BRACE') || !GLOB_BRACE) { $patterns = array_merge($patterns, self::globBraceExpand($patterns)); } // expand globstar if added if (stripos($pattern, '**') !== false) { $patterns = array_merge($patterns, self::globStarExpand($patterns, $globFlags)); } // finally when all patterns expanded, just rerun them and merge results $files = array(); foreach ($patterns as $pat) { $files = array_merge($files, glob($pat, $globFlags)); } // fix some paths as they might have gotten double // due to not-perfect pattern expansion $files = array_map(function ($file) { return str_replace('//', '/', $file); }, $files); // make sure no repetitions from all the patterns provided $files = array_unique($files); // sort by root first? if ($flags & static::GLOB_ROOTFIRST) { $files = ArrayUtils::sortPaths($files, true); } else { if ($flags & static::GLOB_CHILDFIRST) { $files = ArrayUtils::sortPaths($files, false); } elseif (!($flags & GLOB_NOSORT)) { // default sort order is alphabetically sort($files); } } return $files; }