核心要点
初次接触“迭代”这个术语,并看到SPL中与之相关的众多类时,我感到有些不知所措。迭代似乎过于复杂,难以理解。但很快我就意识到,它只是一个我们程序员一直在做的操作的华丽说法。如果您使用PHP,您很可能使用过数组。如果您使用过数组,那么您肯定遍历过它的元素。查看任何代码,几乎肯定能找到foreach
循环。是的,迭代只是遍历值列表的过程。迭代器是一个遍历列表的对象,可以是数组、目录列表,甚至是数据库结果集。在本系列文章的第一部分,我将向您介绍迭代以及如何利用标准PHP库(SPL)中的一些内置类。SPL包含大量迭代器,在代码中使用它们可以提高代码效率,并且在大多数情况下,使代码更易读。
何时以及为何使用SPL迭代器
正如您将看到的,迭代迭代器对象与迭代数组基本相同,因此许多人想知道是否一开始就坚持使用数组更容易。然而,迭代器的真正优势在于遍历大量数据或比简单数组更复杂的数据时。foreach
循环会复制传递给它的任何数组。如果您正在处理大量数据,每次在foreach
循环中使用大型数组时都会复制它们,这可能是不可取的。SPL迭代器封装了列表并一次只公开一个元素的可见性,使其效率更高。在创建数据提供程序时,迭代器是一个很好的构造,因为它们允许您延迟加载数据。这里的延迟加载只是在需要时才检索所需的数据。您还可以操作(过滤、转换等)正在处理的数据,然后再将其提供给用户。但是,使用迭代器的决定始终由您自行决定。迭代器有很多好处,但在某些情况下(例如较小的数组集),可能会导致不必要的开销。何时使用它们取决于您;您的风格选择以及它们在特定情况下的适用性,都是您应该考虑的因素。
迭代数组
我想向您介绍的第一个迭代器是ArrayIterator。构造函数接受一个数组作为参数,并提供可用于遍历它的方法。这是一个示例:
<?php // 一个数组(使用PHP 5.4的新简写法) $arr = ["sitepoint", "phpmaster", "buildmobile", "rubysource", "designfestival", "cloudspring"]; // 创建一个新的ArrayIterator并传入数组 $iter = new ArrayIterator($arr); // 遍历对象 foreach ($iter as $key => $value) { echo $key . ": " . $value . "<br></br>"; }
上述代码的输出为:
<code>0: sitepoint 1: phpmaster 2: buildmobile 3: rubysource 4: designfestival 5: cloudspring</code>
通常,您会使用ArrayObject(允许您在某些上下文中将对象作为数组处理的类),而不是直接使用ArrayIterator。当您使用foreach
循环或直接调用ArrayIterator::getIterator()时,它会自动为您创建一个ArrayIterator。请注意,虽然ArrayObject和ArrayIterator在此上下文中表现得像数组,但它们仍然是对象;尝试对它们使用内置的数组函数(如sort()
和array_keys()
)将失败。ArrayIterator 的使用很简单,但仅限于一维数组。有时您会有一个多维数组,并且您希望递归地遍历嵌套数组。在这种情况下,您可以使用RecursiveArrayIterator。一个常见的场景是嵌套foreach
循环或创建一个递归函数来检查多维数组的所有项。例如:
<?php // 一个多维数组 $arr = [ ["sitepoint", "phpmaster"], ["buildmobile", "rubysource"], ["designfestival", "cloudspring"], "not an array" ]; // 遍历对象 foreach ($arr as $key => $value) { // 检查数组 if (is_array($value)) { foreach ($value as $k => $v) { echo $k . ": " . $v . "<br></br>"; } } else { echo $key . ": " . $value . "<br></br>"; } }
上述代码的输出为:
<code>0: sitepoint 1: phpmaster 0: buildmobile 1: rubysource 0: designfestival 1: cloudspring 3: not an array</code>
更优雅的方法是使用RecursiveArrayIterator:
<?php ... $iter = new RecursiveArrayIterator($arr); // 遍历对象 // 我们需要创建一个RecursiveIteratorIterator实例 foreach (new RecursiveIteratorIterator($iter) as $key => $value) { echo $key . ": " . $value . "<br></br>"; }
输出与前面的示例相同。请注意,您需要在此处创建一个RecursiveIteratorIterator实例并将其传递给RecursiveArrayIterator对象,否则您将只获得根数组中的值(以及根据您的设置而产生的大量通知)。在处理多维数组时,您应该使用RecursiveArrayIterator,因为它允许您遍历当前条目,但这取决于您自己去做。RecursiveIteratorIterator是一个装饰器,它为您执行此操作。它获取RecursiveArrayIterator,遍历它并遍历它找到的任何可迭代条目(依此类推)。本质上,它“展平”了RecursiveArrayIterator。您可以通过调用RecursiveIteratorIterator::getDepth() 来获取当前迭代的深度以进行跟踪。但是,如果您想返回对象,请小心使用RecursiveArrayIterator和RecursiveIteratorIterator;对象被视为可迭代的,因此将被迭代。
迭代目录列表
您无疑需要在某个时间点遍历目录及其文件,并且可以使用PHP提供的内置函数(例如scandir()
或glob()
)来实现这一点,但您也可以使用DirectoryIterator。在其最简单的形式中,DirectoryIterator 非常强大,但它也可以被子类化和增强。这是一个使用DirectoryIterator迭代目录的示例:
<?php // 创建新的DirectoryIterator对象 $dir = new DirectoryIterator("/my/directory/path"); // 遍历目录列表 foreach ($dir as $item) { echo $item . "<br></br>"; }
输出显然取决于您指定的路径以及目录的内容。例如:
<code>. .. api index.php lib workspace</code>
不要忘记,对于DirectoryIterator以及许多其他SPL迭代器,您可以使用异常来处理任何错误。
<?php // 一个数组(使用PHP 5.4的新简写法) $arr = ["sitepoint", "phpmaster", "buildmobile", "rubysource", "designfestival", "cloudspring"]; // 创建一个新的ArrayIterator并传入数组 $iter = new ArrayIterator($arr); // 遍历对象 foreach ($iter as $key => $value) { echo $key . ": " . $value . "<br></br>"; }
<code>0: sitepoint 1: phpmaster 2: buildmobile 3: rubysource 4: designfestival 5: cloudspring</code>
使用诸如DirectoryIterator::isDot()、DirectoryIterator::getType()和DirectoryIterator::getSize()等许多其他方法,几乎涵盖了您所有基本的目录信息需求。您甚至可以将DirectoryIterator与FilterIterator或RegexIterator组合以返回匹配特定条件的文件。例如:
<?php // 一个多维数组 $arr = [ ["sitepoint", "phpmaster"], ["buildmobile", "rubysource"], ["designfestival", "cloudspring"], "not an array" ]; // 遍历对象 foreach ($arr as $key => $value) { // 检查数组 if (is_array($value)) { foreach ($value as $k => $v) { echo $k . ": " . $v . "<br></br>"; } } else { echo $key . ": " . $value . "<br></br>"; } }
SPL还提供RecursiveDirectoryIterator,其使用方法与RecursiveArrayIterator相同。递归遍历目录的函数通常会包含许多用于检查有效目录和文件的条件检查,而RecursiveDirectoryIterator可以为您完成大部分工作,从而产生更简洁的代码。但是,有一个警告。RecursiveDirectoryIterator不返回空目录;如果目录包含许多子目录但没有文件,它将返回空结果(类似于Git的行为)。
<code>0: sitepoint 1: phpmaster 0: buildmobile 1: rubysource 0: designfestival 1: cloudspring 3: not an array</code>
我的输出类似于:
<?php ... $iter = new RecursiveArrayIterator($arr); // 遍历对象 // 我们需要创建一个RecursiveIteratorIterator实例 foreach (new RecursiveIteratorIterator($iter) as $key => $value) { echo $key . ": " . $value . "<br></br>"; }
总结
希望您现在意识到迭代并非像我最初认为的那样复杂,它是我们程序员每天都在做的事情。在本文中,我介绍了迭代以及SPL提供的一些类,使迭代更容易和更健壮。当然,我只处理了可用类的一小部分样本;SPL提供了许多其他类,我强烈建议您查看它们。SPL是一个“标准”库。有时您可能会发现这些类过于通用,它们可能并不总是能满足您的需求。在这种情况下,您可以轻松扩展这些类以添加您自己的功能或根据需要调整现有功能。在本系列的下一部分中,我将向您展示如何使用SPL接口来创建您自己的自定义类,这些类可以像数组一样进行遍历和访问。图片来自Mushakesa / Shutterstock
关于使用SPL迭代器的常见问题解答
SPL代表标准PHP库。它是一组旨在解决常见问题的接口和类。SPL提供许多数据结构、接口和异常,可用于更有效地处理复杂任务。它很重要,因为它可以帮助减少您需要编写的代码量,提高性能,并使您的代码更易于阅读和维护。
SPL迭代器用于迭代数据集合。要使用SPL迭代器,您首先需要创建迭代器类的实例。然后,您可以使用迭代器的方法遍历集合。例如,您可以使用current()
方法获取当前项,使用next()
方法移动到下一项,并使用valid()
方法检查是否有更多项要迭代。
有许多不同类型的SPL迭代器,每种迭代器都设计用于特定目的。一些示例包括ArrayIterator(用于迭代数组);DirectoryIterator(用于迭代目录的内容);以及RecursiveArrayIterator(用于递归迭代数组)。
您可以通过使用FilterIterator类来过滤数据。此类允许您定义一个自定义过滤器,该过滤器应用于集合中的每个项目。只有通过过滤器的项目才会被迭代器返回。
您可以通过使用ArrayObject类来排序数据。此类提供了一个asort()
方法,用于对集合中的项目进行排序。您还可以使用uasort()
方法使用自定义比较函数对项目进行排序。
SPL异常是一种专门设计用于SPL的异常类型。它们提供了一种处理使用SPL时发生的错误的方法。要使用SPL异常,您只需在try
块中抛出它,并在catch
块中捕获它。
您可以使用SplFileObject类读取和写入文件。此类提供用于打开文件、从中读取、向其写入和关闭文件的方法。它还提供用于移动文件指针和检查是否已到达文件结尾的方法。
您可以使用DirectoryIterator类处理目录。此类提供用于打开目录、读取其内容和关闭目录的方法。它还提供用于检查文件是否为目录以及获取文件的大小、类型和修改时间的方法。
您可以使用ArrayObject和ArrayIterator类处理数组。这些类提供用于创建数组、向其添加项、从中删除项以及迭代其项的方法。它们还提供用于排序数组和检查数组中是否存在项的方法。
您可以使用SPL提供的各种数据结构类来处理数据结构。这些类包括SplStack、SplQueue、SplHeap和SplPriorityQueue。每个类都提供用于创建数据结构、向其添加项、从中删除项以及迭代其项的方法。
以上是使用SPL迭代器,第1部分的详细内容。更多信息请关注PHP中文网其他相关文章!