好的,我想我终于明白了这一点。这大致是我在伪代码中所做的:
步骤 1
我们需要列出目录内容,因此我们可以执行以下操作:
// Reads through the $dir directory
// traversing children, and returns all contents
$dirIterator = new RecursiveDirectoryIterator($dir);
// Flattens the recursive iterator into a single
// dimension, so it doesn't need recursive loops
$dirContents = new RecursiveIteratorIterator($dirIterator);
第 2 步
我们只需要考虑 PHP 文件
class PhpFileIteratorFilter {
public function accept() {
$current = $this->current();
return $current instanceof SplFileInfo
&& $current->isFile()
&& end(explode('.', $current->getBasename())) == 'php';
}
}
// Extends FilterIterator, and accepts only .php files
$php_files = new PhpFileIteratorFilter($dirContents);
PhpFileIteratorFilter 不能很好地使用可重用代码。更好的方法是能够提供文件扩展名作为构造的一部分,并让过滤器与之匹配。尽管如此,我还是试图摆脱不需要它们的构造参数,而更多地依赖组合,因为这样可以更好地利用策略模式。PhpFileIteratorFilter 可以简单地使用通用 FileExtensionIteratorFilter 并在内部进行设置。
第 3 步
我们现在必须读入文件内容
class SplFileInfoReader extends FilterIterator {
public function accept() {
// make sure we use parent, this one returns the contents
$current = parent::current();
return $current instanceof SplFileInfo
&& $current->isFile()
&& $current->isReadable();
}
public function key() {
return parent::current()->getRealpath();
}
public function current() {
return file_get_contents($this->key());
}
}
// Reads the file contents of the .php files
// the key is the file path, the value is the file contents
$files_and_content = new SplFileInfoReader($php_files);
第 4 步
现在我们要将回调应用到每个项目(文件内容)并以某种方式保留结果。再次,尝试利用策略模式,我已经消除了不必要的构造函数参数,例如$preserveKeys
或类似的
/**
* Applies $callback to each element, and only accepts values that have children
*/
class ArrayCallbackFilterIterator extends FilterIterator implements RecursiveIterator {
public function __construct(Iterator $it, $callback) {
if (!is_callable($callback)) {
throw new InvalidArgumentException('$callback is not callable');
}
$this->callback = $callback;
parent::__construct($it);
}
public function accept() {
return $this->hasChildren();
}
public function hasChildren() {
$this->results = call_user_func($this->callback, $this->current());
return is_array($this->results) && !empty($this->results);
}
public function getChildren() {
return new RecursiveArrayIterator($this->results);
}
}
/**
* Overrides ArrayCallbackFilterIterator to allow a fixed $key to be returned
*/
class FixedKeyArrayCallbackFilterIterator extends ArrayCallbackFilterIterator {
public function getChildren() {
return new RecursiveFixedKeyArrayIterator($this->key(), $this->results);
}
}
/**
* Extends RecursiveArrayIterator to allow a fixed $key to be set
*/
class RecursiveFixedKeyArrayIterator extends RecursiveArrayIterator {
public function __construct($key, $array) {
$this->key = $key;
parent::__construct($array);
}
public function key() {
return $this->key;
}
}
所以,这里我有我的基本迭代器,它将返回$callback
我提供的结果,但我还扩展了它以创建一个也将保留键的版本,而不是为其使用构造函数参数。
因此我们有这个:
// Returns a RecursiveIterator
// key: file path
// value: class name
$class_filter = new FixedKeyArrayCallbackFilterIterator($files_and_content, 'getDefinedClasses');
第5步
现在我们需要将其格式化为合适的方式。我希望文件路径是值,键是类名(即为一个类提供一个直接映射到自动加载器可以在其中找到它的文件)
// Reduce the multi-dimensional iterator into a single dimension
$files_and_classes = new RecursiveIteratorIterator($class_filter);
// Flip it around, so the class names are keys
$classes_and_files = new FlipIterator($files_and_classes);
瞧,我现在可以遍历$classes_and_files
并获取 $dir 下所有已定义类的列表,以及它们在其中定义的文件。几乎所有用于执行此操作的代码也可以在其他上下文中重用. 我没有在定义的迭代器中硬编码任何东西来完成这个任务,我也没有在迭代器之外做任何额外的处理