diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php index af1b93352..62d610b65 100644 --- a/system/src/Grav/Common/Filesystem/Folder.php +++ b/system/src/Grav/Common/Filesystem/Folder.php @@ -19,12 +19,12 @@ abstract class Folder { $last_modified = 0; - $dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); - $filterItr = new RecursiveFolderFilterIterator($dirItr); - $itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST); + $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + $filter = new RecursiveFolderFilterIterator($directory); + $iterator = new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::SELF_FIRST); /** @var \RecursiveDirectoryIterator $file */ - foreach ($itr as $dir) { + foreach ($iterator as $dir) { $dir_modified = $dir->getMTime(); if ($dir_modified > $last_modified) { $last_modified = $dir_modified; @@ -46,12 +46,12 @@ abstract class Folder { $last_modified = 0; - $dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); - $itrItr = new \RecursiveIteratorIterator($dirItr, \RecursiveIteratorIterator::SELF_FIRST); - $itr = new \RegexIterator($itrItr, '/^.+\.'.$extensions.'$/i'); + $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + $recursive = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST); + $iterator = new \RegexIterator($recursive, '/^.+\.'.$extensions.'$/i'); /** @var \RecursiveDirectoryIterator $file */ - foreach ($itr as $filepath => $file) { + foreach ($iterator as $filepath => $file) { $file_modified = $file->getMTime(); if ($file_modified > $last_modified) { $last_modified = $file_modified; @@ -64,8 +64,8 @@ abstract class Folder /** * Get relative path between target and base path. If path isn't relative, return full path. * - * @param string $path - * @param mixed|string $base + * @param string $path + * @param string $base * @return string */ public static function getRelativePath($path, $base = GRAV_ROOT) @@ -81,6 +81,43 @@ abstract class Folder return $path; } + /** + * Get relative path between target and base path. If path isn't relative, return full path. + * + * @param string $path + * @param string $base + * @return string + */ + public static function getRelativePathDotDot($path, $base) + { + $base = preg_replace('![\\\/]+!', '/', $base); + $path = preg_replace('![\\\/]+!', '/', $path); + + if ($path === $base) { + return ''; + } + + $baseParts = explode('/', isset($base[0]) && '/' === $base[0] ? substr($base, 1) : $base); + $pathParts = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path); + + array_pop($baseParts); + $lastPart = array_pop($pathParts); + foreach ($baseParts as $i => $directory) { + if (isset($pathParts[$i]) && $pathParts[$i] === $directory) { + unset($baseParts[$i], $pathParts[$i]); + } else { + break; + } + } + $pathParts[] = $lastPart; + $path = str_repeat('../', count($baseParts)) . implode('/', $pathParts); + + return '' === $path + || '/' === $path[0] + || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) + ? "./$path" : $path; + } + /** * Shift first directory out of the path. * @@ -96,8 +133,6 @@ abstract class Folder return $result ?: null; } - - /** * Return recursive list of all files and directories under given path. * @@ -116,13 +151,17 @@ abstract class Folder $pattern = isset($params['pattern']) ? $params['pattern'] : null; $filters = isset($params['filters']) ? $params['filters'] : null; $recursive = isset($params['recursive']) ? $params['recursive'] : true; + $levels = isset($params['levels']) ? $params['levels'] : -1; $key = isset($params['key']) ? 'get' . $params['key'] : null; $value = isset($params['value']) ? 'get' . $params['value'] : ($recursive ? 'getSubPathname' : 'getFilename'); + $folders = isset($params['folders']) ? $params['folders'] : true; + $files = isset($params['files']) ? $params['files'] : true; if ($recursive) { $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF); $iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST); + $iterator->setMaxDepth(max($levels, -1)); } else { $iterator = new \FilesystemIterator($path); } @@ -131,6 +170,16 @@ abstract class Folder /** @var \RecursiveDirectoryIterator $file */ foreach ($iterator as $file) { + // Ignore hidden files. + if ($file->getFilename()[0] == '.') { + continue; + } + if (!$folders && $file->isDir()) { + continue; + } + if (!$files && $file->isFile()) { + continue; + } if ($compare && $pattern && !preg_match($pattern, $file->{$compare}())) { continue; } @@ -138,7 +187,8 @@ abstract class Folder $filePath = $file->{$value}(); if ($filters) { if (isset($filters['key'])) { - $fileKey = preg_replace($filters['key'], '', $fileKey); + $pre = !empty($filters['pre-key']) ? $filters['pre-key'] : ''; + $fileKey = $pre . preg_replace($filters['key'], '', $fileKey); } if (isset($filters['value'])) { $filter = $filters['value']; @@ -146,12 +196,12 @@ abstract class Folder $filePath = call_user_func($filter, $file); } else { $filePath = preg_replace($filter, '', $filePath); + } } } - } if ($fileKey !== null) { - $results[$fileKey] = $filePath; + $results[$fileKey] = $filePath; } else { $results[] = $filePath; } @@ -163,11 +213,12 @@ abstract class Folder /** * Recursively copy directory in filesystem. * - * @param string $source - * @param string $target + * @param string $source + * @param string $target + * @param string $ignore Ignore files matching pattern (regular expression). * @throws \RuntimeException */ - public static function copy($source, $target) + public static function copy($source, $target, $ignore = null) { $source = rtrim($source, '\\/'); $target = rtrim($target, '\\/'); @@ -177,19 +228,24 @@ abstract class Folder } // Make sure that path to the target exists before copying. - self::mkdir($target); + self::create($target); $success = true; // Go through all sub-directories and copy everything. $files = self::all($source); foreach ($files as $file) { + if ($ignore && preg_match($ignore, $file)) { + continue; + } $src = $source .'/'. $file; $dst = $target .'/'. $file; if (is_dir($src)) { - // Create current directory. - $success &= @mkdir($dst); + // Create current directory (if it doesn't exist). + if (!is_dir($dst)) { + $success &= @mkdir($dst, 0777, true); + } } else { // Or copy current file. $success &= @copy($src, $dst); @@ -208,8 +264,8 @@ abstract class Folder /** * Move directory in filesystem. * - * @param string $source - * @param string $target + * @param string $source + * @param string $target * @throws \RuntimeException */ public static function move($source, $target) @@ -219,7 +275,7 @@ abstract class Folder } // Make sure that path to the target exists before moving. - self::mkdir(dirname($target)); + self::create(dirname($target)); // Just rename the directory. $success = @rename($source, $target); @@ -238,16 +294,16 @@ abstract class Folder * Recursively delete directory from filesystem. * * @param string $target - * @throws \RuntimeException + * @param bool $include_target * @return bool */ - public static function delete($target) + public static function delete($target, $include_target = true) { if (!is_dir($target)) { - return; + return false; } - $success = self::doDelete($target); + $success = self::doDelete($target, $include_target); if (!$success) { $error = error_get_last(); @@ -255,16 +311,31 @@ abstract class Folder } // Make sure that the change will be detected when caching. - @touch(dirname($target)); + if ($include_target) { + @touch(dirname($target)); + } else { + @touch($target); + } + return $success; } /** - * @param string $folder + * @param string $folder * @throws \RuntimeException * @internal */ public static function mkdir($folder) + { + self::create($folder); + } + + /** + * @param string $folder + * @throws \RuntimeException + * @internal + */ + public static function create($folder) { if (is_dir($folder)) { return; @@ -320,10 +391,11 @@ abstract class Folder /** * @param string $folder + * @param bool $include_target * @return bool * @internal */ - protected static function doDelete($folder) + protected static function doDelete($folder, $include_target = true) { // Special case for symbolic links. if (is_link($folder)) { @@ -338,16 +410,16 @@ abstract class Folder /** @var \DirectoryIterator $fileinfo */ foreach ($files as $fileinfo) { if ($fileinfo->isDir()) { - if (false === rmdir($fileinfo->getRealPath())) { + if (false === @rmdir($fileinfo->getRealPath())) { return false; } } else { - if (false === unlink($fileinfo->getRealPath())) { + if (false === @unlink($fileinfo->getRealPath())) { return false; } } } - return rmdir($folder); + return $include_target ? @rmdir($folder) : true; } }