在 PHP 中装饰迭代器 - 启用跳过
Decorate an Iterator in PHP - enable skipping
我有一个 class,它基本上是 PHP 的 DirectoryIterator 的装饰器。每个文件的内容都由 class 处理,然后由 current()
方法 return 编辑。目前,当文件是点文件或无法处理文件时,我 return false
来自 current()
方法。但我宁愿跳过点和不可处理的文件,只处理 return 数据。
P.S。下面的代码是一个简化的例子。我不想在构造函数中预处理所有文件。
class Pages implements \Iterator
{
public function __construct(string $path)
{
$this->di = new \DirectoryIterator($path);
}
public function rewind() {
return $this->di->rewind();
}
public function current() {
$file = $this->di->current();
if($file->isDot()) {
return false;
}
$content = file_get_contents($file->getPathName());
if($content === 'Cannot be processed!') {
return false;
}
return $content;
}
public function key() {
return $this->di->key();
}
public function next() {
return $this->di->next();
}
public function valid() {
return $this->di->valid();
}
}
我终于找到了这个解决方案,希望有人觉得它有用。可以将此模式应用于任何修饰的迭代器,有效地以类似于“过滤”迭代器的东西结束。
class FilteredIterator implements \Iterator
{
protected $currentProcessed = null;
protected $key = -1; // Keys are maintained by the decorating class
public function __construct(Iterator $iterator)
{
$this->it = $iterator;
}
// Iterator interface START -->
public function rewind() {
$this->it->rewind();
$this->forward();
}
public function current() {
return $this->currentProcessed;
}
public function key() {
return $this->key;
}
public function next() {
$this->it->next();
$this->forward();
}
public function valid() {
return $this->it->valid();
}
// <-- Iterator interface END
protected function forward() : void
{
if(!$this->it->valid()) {
// Stop when iterator is done
return;
}
// Pseudo code, insert expensive processing function here -->
$content = $this->it->current();
if($content === 5) {
// <-- Pseudo code END
// Skip
$this->next();
return;
}
// Update position
$this->key++;
// Cache successfully processed step
$this->currentProcessed = $content;
}
}
$arrayObject = new ArrayObject([1,2,3,4,5,6]);
$filteredIterator = new FilteredIterator($arrayObject->getIterator());
foreach ($filteredIterator as $key => $value) {
echo $key . ' -> ' . $value . "\n";
}
//0 -> 1
//1 -> 2
//2 -> 3
//3 -> 4
//4 -> 6
您可以通过扩展已经存在的摘要 PHP class FilterIterator
:
来大大简化您自己的答案的代码
class DirectoryFilterIterator
extends FilterIterator
{
public function __construct(string $path) {
parent::__construct(new DirectoryIterator($path));
}
public function accept() : bool {
$current = parent::current();
// let's not only ensure it's not a dot but that it's an actual readable file
if(!$current->isFile() || !$current->isReadable()) {
return false;
}
else {
$contents = file_get_contents($current->getPathname());
if($contents === 'Some value') {
return false;
}
}
return true;
}
}
foreach(new DirectoryFilterIterator('.') as $key => $value) {
echo "$key -> $value\n";
}
如您所见,我不仅要确保该项目不是一个点,还要确保该项目是一个实际可读的文件,因为我认为这更符合您的意图。
PS:由于您的初衷是创建一个 decorator/wrapper,因此您当然可以更改 DirectoryFilterIterator
的构造函数以接受 DirectoryIterator
:
public function __construct(DirectoryIterator $it) {
parent::__construct($it);
}
...然后将实例化更改为:
foreach(new DirectoryFilterIterator(new DirectoryIterator('.')) as $key => $value) {
echo "$key -> $value\n";
}
我有一个 class,它基本上是 PHP 的 DirectoryIterator 的装饰器。每个文件的内容都由 class 处理,然后由 current()
方法 return 编辑。目前,当文件是点文件或无法处理文件时,我 return false
来自 current()
方法。但我宁愿跳过点和不可处理的文件,只处理 return 数据。
P.S。下面的代码是一个简化的例子。我不想在构造函数中预处理所有文件。
class Pages implements \Iterator
{
public function __construct(string $path)
{
$this->di = new \DirectoryIterator($path);
}
public function rewind() {
return $this->di->rewind();
}
public function current() {
$file = $this->di->current();
if($file->isDot()) {
return false;
}
$content = file_get_contents($file->getPathName());
if($content === 'Cannot be processed!') {
return false;
}
return $content;
}
public function key() {
return $this->di->key();
}
public function next() {
return $this->di->next();
}
public function valid() {
return $this->di->valid();
}
}
我终于找到了这个解决方案,希望有人觉得它有用。可以将此模式应用于任何修饰的迭代器,有效地以类似于“过滤”迭代器的东西结束。
class FilteredIterator implements \Iterator
{
protected $currentProcessed = null;
protected $key = -1; // Keys are maintained by the decorating class
public function __construct(Iterator $iterator)
{
$this->it = $iterator;
}
// Iterator interface START -->
public function rewind() {
$this->it->rewind();
$this->forward();
}
public function current() {
return $this->currentProcessed;
}
public function key() {
return $this->key;
}
public function next() {
$this->it->next();
$this->forward();
}
public function valid() {
return $this->it->valid();
}
// <-- Iterator interface END
protected function forward() : void
{
if(!$this->it->valid()) {
// Stop when iterator is done
return;
}
// Pseudo code, insert expensive processing function here -->
$content = $this->it->current();
if($content === 5) {
// <-- Pseudo code END
// Skip
$this->next();
return;
}
// Update position
$this->key++;
// Cache successfully processed step
$this->currentProcessed = $content;
}
}
$arrayObject = new ArrayObject([1,2,3,4,5,6]);
$filteredIterator = new FilteredIterator($arrayObject->getIterator());
foreach ($filteredIterator as $key => $value) {
echo $key . ' -> ' . $value . "\n";
}
//0 -> 1
//1 -> 2
//2 -> 3
//3 -> 4
//4 -> 6
您可以通过扩展已经存在的摘要 PHP class FilterIterator
:
class DirectoryFilterIterator
extends FilterIterator
{
public function __construct(string $path) {
parent::__construct(new DirectoryIterator($path));
}
public function accept() : bool {
$current = parent::current();
// let's not only ensure it's not a dot but that it's an actual readable file
if(!$current->isFile() || !$current->isReadable()) {
return false;
}
else {
$contents = file_get_contents($current->getPathname());
if($contents === 'Some value') {
return false;
}
}
return true;
}
}
foreach(new DirectoryFilterIterator('.') as $key => $value) {
echo "$key -> $value\n";
}
如您所见,我不仅要确保该项目不是一个点,还要确保该项目是一个实际可读的文件,因为我认为这更符合您的意图。
PS:由于您的初衷是创建一个 decorator/wrapper,因此您当然可以更改 DirectoryFilterIterator
的构造函数以接受 DirectoryIterator
:
public function __construct(DirectoryIterator $it) {
parent::__construct($it);
}
...然后将实例化更改为:
foreach(new DirectoryFilterIterator(new DirectoryIterator('.')) as $key => $value) {
echo "$key -> $value\n";
}