%PDF-1.4
Directory : /var/www/vhosts/lautnusantara.com/httpdocs/web/system/Files/ |
<?php /** * This file is part of CodeIgniter 4 framework. * * (c) CodeIgniter Foundation <admin@codeigniter.com> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace CodeIgniter\Files; use CodeIgniter\Files\Exceptions\FileException; use CodeIgniter\Files\Exceptions\FileNotFoundException; use Countable; use Generator; use InvalidArgumentException; use IteratorAggregate; /** * File Collection Class * * Representation for a group of files, with utilities for locating, * filtering, and ordering them. */ class FileCollection implements Countable, IteratorAggregate { /** * The current list of file paths. * * @var string[] */ protected $files = []; // -------------------------------------------------------------------- // Support Methods // -------------------------------------------------------------------- /** * Resolves a full path and verifies it is an actual directory. * * @throws FileException */ final protected static function resolveDirectory(string $directory): string { if (! is_dir($directory = set_realpath($directory))) { $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; throw FileException::forExpectedDirectory($caller['function']); } return $directory; } /** * Resolves a full path and verifies it is an actual file. * * @throws FileException */ final protected static function resolveFile(string $file): string { if (! is_file($file = set_realpath($file))) { $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; throw FileException::forExpectedFile($caller['function']); } return $file; } /** * Removes files that are not part of the given directory (recursive). * * @param string[] $files * * @return string[] */ final protected static function filterFiles(array $files, string $directory): array { $directory = self::resolveDirectory($directory); return array_filter($files, static fn (string $value): bool => strpos($value, $directory) === 0); } /** * Returns any files whose `basename` matches the given pattern. * * @param string[] $files * @param string $pattern Regex or pseudo-regex string * * @return string[] */ final protected static function matchFiles(array $files, string $pattern): array { // Convert pseudo-regex into their true form if (@preg_match($pattern, '') === false) { $pattern = str_replace( ['#', '.', '*', '?'], ['\#', '\.', '.*', '.'], $pattern ); $pattern = "#{$pattern}#"; } return array_filter($files, static fn ($value) => (bool) preg_match($pattern, basename($value))); } // -------------------------------------------------------------------- // Class Core // -------------------------------------------------------------------- /** * Loads the Filesystem helper and adds any initial files. * * @param string[] $files */ public function __construct(array $files = []) { helper(['filesystem']); $this->add($files)->define(); } /** * Applies any initial inputs after the constructor. * This method is a stub to be implemented by child classes. */ protected function define(): void { } /** * Optimizes and returns the current file list. * * @return string[] */ public function get(): array { $this->files = array_unique($this->files); sort($this->files, SORT_STRING); return $this->files; } /** * Sets the file list directly, files are still subject to verification. * This works as a "reset" method with []. * * @param string[] $files The new file list to use * * @return $this */ public function set(array $files) { $this->files = []; return $this->addFiles($files); } /** * Adds an array/single file or directory to the list. * * @param string|string[] $paths * * @return $this */ public function add($paths, bool $recursive = true) { $paths = (array) $paths; foreach ($paths as $path) { if (! is_string($path)) { throw new InvalidArgumentException('FileCollection paths must be strings.'); } try { // Test for a directory self::resolveDirectory($path); } catch (FileException $e) { $this->addFile($path); continue; } $this->addDirectory($path, $recursive); } return $this; } // -------------------------------------------------------------------- // File Handling // -------------------------------------------------------------------- /** * Verifies and adds files to the list. * * @param string[] $files * * @return $this */ public function addFiles(array $files) { foreach ($files as $file) { $this->addFile($file); } return $this; } /** * Verifies and adds a single file to the file list. * * @return $this */ public function addFile(string $file) { $this->files[] = self::resolveFile($file); return $this; } /** * Removes files from the list. * * @param string[] $files * * @return $this */ public function removeFiles(array $files) { $this->files = array_diff($this->files, $files); return $this; } /** * Removes a single file from the list. * * @return $this */ public function removeFile(string $file) { return $this->removeFiles([$file]); } // -------------------------------------------------------------------- // Directory Handling // -------------------------------------------------------------------- /** * Verifies and adds files from each * directory to the list. * * @param string[] $directories * * @return $this */ public function addDirectories(array $directories, bool $recursive = false) { foreach ($directories as $directory) { $this->addDirectory($directory, $recursive); } return $this; } /** * Verifies and adds all files from a directory. * * @return $this */ public function addDirectory(string $directory, bool $recursive = false) { $directory = self::resolveDirectory($directory); // Map the directory to depth 2 to so directories become arrays foreach (directory_map($directory, 2, true) as $key => $path) { if (is_string($path)) { $this->addFile($directory . $path); } elseif ($recursive && is_array($path)) { $this->addDirectory($directory . $key, true); } } return $this; } // -------------------------------------------------------------------- // Filtering // -------------------------------------------------------------------- /** * Removes any files from the list that match the supplied pattern * (within the optional scope). * * @param string $pattern Regex or pseudo-regex string * @param string|null $scope The directory to limit the scope * * @return $this */ public function removePattern(string $pattern, ?string $scope = null) { if ($pattern === '') { return $this; } // Start with all files or those in scope $files = $scope === null ? $this->files : self::filterFiles($this->files, $scope); // Remove any files that match the pattern return $this->removeFiles(self::matchFiles($files, $pattern)); } /** * Keeps only the files from the list that match * (within the optional scope). * * @param string $pattern Regex or pseudo-regex string * @param string|null $scope A directory to limit the scope * * @return $this */ public function retainPattern(string $pattern, ?string $scope = null) { if ($pattern === '') { return $this; } // Start with all files or those in scope $files = $scope === null ? $this->files : self::filterFiles($this->files, $scope); // Matches the pattern within the scoped files and remove their inverse. return $this->removeFiles(array_diff($files, self::matchFiles($files, $pattern))); } // -------------------------------------------------------------------- // Interface Methods // -------------------------------------------------------------------- /** * Returns the current number of files in the collection. * Fulfills Countable. */ public function count(): int { return count($this->files); } /** * Yields as an Iterator for the current files. * Fulfills IteratorAggregate. * * @return Generator<File> * * @throws FileNotFoundException */ public function getIterator(): Generator { foreach ($this->get() as $file) { yield new File($file, true); } } }