null, 'accept_callback' => null, 'accept_dir_callback' => null, 'accept_file_callback' => null, 'file_callback' => null, 'dir_callback' => null, 'ignore_files' => null, 'ignore_dirs' => null, 'ignore_vcs' => true, 'min_depth' => null, 'max_depth' => null ); /** * @var array */ protected $options; public function __construct() { $this->options = array(); $class = get_class($this); // We build our options array ourselves, because possibly no class or config manifest exists at this point do { $this->options = array_merge(Object::static_lookup($class, 'default_options'), $this->options); } while ($class = get_parent_class($class)); } /** * Returns an option value set on this instance. * * @param string $name * @return mixed */ public function getOption($name) { if (!array_key_exists($name, $this->options)) { throw new InvalidArgumentException("The option $name doesn't exist."); } return $this->options[$name]; } /** * Set an option on this finder instance. See {@link SS_FileFinder} for the * list of options available. * * @param string $name * @param mixed $value */ public function setOption($name, $value) { if (!array_key_exists($name, $this->options)) { throw new InvalidArgumentException("The option $name doesn't exist."); } $this->options[$name] = $value; } /** * Sets several options at once. * * @param array $options */ public function setOptions(array $options) { foreach ($options as $k => $v) $this->setOption($k, $v); } /** * Finds all files matching the options within a directory. The search is * performed depth first. * * @param string $base * @return array */ public function find($base) { $paths = array(array(rtrim($base, '/'), 0)); $found = array(); $fileCallback = $this->getOption('file_callback'); $dirCallback = $this->getOption('dir_callback'); while ($path = array_shift($paths)) { list($path, $depth) = $path; foreach (scandir($path) as $basename) { if ($basename == '.' || $basename == '..') { continue; } if (is_dir("$path/$basename")) { if (!$this->acceptDir($basename, "$path/$basename", $depth + 1)) { continue; } if ($dirCallback) { call_user_func( $dirCallback, $basename, "$path/$basename", $depth + 1 ); } $paths[] = array("$path/$basename", $depth + 1); } else { if (!$this->acceptFile($basename, "$path/$basename", $depth)) { continue; } if ($fileCallback) { call_user_func( $fileCallback, $basename, "$path/$basename", $depth ); } $found[] = "$path/$basename"; } } } return $found; } /** * Returns TRUE if the directory should be traversed. This can be overloaded * to customise functionality, or extended with callbacks. * * @param string $basename * @param string $pathname * @param int $depth * @return bool */ protected function acceptDir($basename, $pathname, $depth) { if ($this->getOption('ignore_vcs') && in_array($basename, self::$vcs_dirs)) { return false; } if ($ignore = $this->getOption('ignore_dirs')) { if (in_array($basename, $ignore)) return false; } if ($max = $this->getOption('max_depth')) { if ($depth > $max) return false; } if ($callback = $this->getOption('accept_callback')) { if (!call_user_func($callback, $basename, $pathname, $depth)) return false; } if ($callback = $this->getOption('accept_dir_callback')) { if (!call_user_func($callback, $basename, $pathname, $depth)) return false; } return true; } /** * Returns TRUE if the file should be included in the results. This can be * overloaded to customise functionality, or extended via callbacks. * * @param string $basename * @param string $pathname * @param int $depth * @return bool */ protected function acceptFile($basename, $pathname, $depth) { if ($regex = $this->getOption('name_regex')) { if (!preg_match($regex, $basename)) return false; } if ($ignore = $this->getOption('ignore_files')) { if (in_array($basename, $ignore)) return false; } if ($minDepth = $this->getOption('min_depth')) { if ($depth < $minDepth) return false; } if ($callback = $this->getOption('accept_callback')) { if (!call_user_func($callback, $basename, $pathname, $depth)) return false; } if ($callback = $this->getOption('accept_file_callback')) { if (!call_user_func($callback, $basename, $pathname, $depth)) return false; } return true; } }