核心要點
初次接觸“迭代”這個術語,並看到SPL中與之相關的眾多類時,我感到有些不知所措。迭代似乎過於復雜,難以理解。但很快我就意識到,它只是一個我們程序員一直在做的操作的華麗說法。如果您使用PHP,您很可能使用過數組。如果您使用過數組,那麼您肯定遍歷過它的元素。查看任何代碼,幾乎肯定能找到foreach
循環。是的,迭代只是遍歷值列表的過程。迭代器是一個遍歷列表的對象,可以是數組、目錄列表,甚至是數據庫結果集。在本系列文章的第一部分,我將向您介紹迭代以及如何利用標準PHP庫(SPL)中的一些內置類。 SPL包含大量迭代器,在代碼中使用它們可以提高代碼效率,並且在大多數情況下,使代碼更易讀。
何時以及為何使用SPL迭代器
正如您將看到的,迭代迭代器對象與迭代數組基本相同,因此許多人想知道是否一開始就堅持使用數組更容易。然而,迭代器的真正優勢在於遍歷大量數據或比簡單數組更複雜的數據時。 foreach
循環會復制傳遞給它的任何數組。如果您正在處理大量數據,每次在foreach
循環中使用大型數組時都會復制它們,這可能是不可取的。 SPL迭代器封裝了列表並一次隻公開一個元素的可見性,使其效率更高。在創建數據提供程序時,迭代器是一個很好的構造,因為它們允許您延遲加載數據。這裡的延遲加載只是在需要時才檢索所需的數據。您還可以操作(過濾、轉換等)正在處理的數據,然後再將其提供給用戶。但是,使用迭代器的決定始終由您自行決定。迭代器有很多好處,但在某些情況下(例如較小的數組集),可能會導致不必要的開銷。何時使用它們取決於您;您的風格選擇以及它們在特定情況下的適用性,都是您應該考慮的因素。
迭代數組
我想向您介紹的第一個迭代器是ArrayIterator。構造函數接受一個數組作為參數,並提供可用於遍歷它的方法。這是一個示例:
<code class="language-php"><?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>"; }</code>
上述代碼的輸出為:
<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
循環或創建一個遞歸函數來檢查多維數組的所有項。例如:
<code class="language-php"><?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>"; } } else { echo $key . ": " . $value . "<br>"; } }</code>
上述代碼的輸出為:
<code>0: sitepoint 1: phpmaster 0: buildmobile 1: rubysource 0: designfestival 1: cloudspring 3: not an array</code>
更優雅的方法是使用RecursiveArrayIterator:
<code class="language-php"><?php ... $iter = new RecursiveArrayIterator($arr); // 遍历对象 // 我们需要创建一个RecursiveIteratorIterator实例 foreach (new RecursiveIteratorIterator($iter) as $key => $value) { echo $key . ": " . $value . "<br>"; }</code>
輸出與前面的示例相同。請注意,您需要在此處創建一個RecursiveIteratorIterator實例並將其傳遞給RecursiveArrayIterator對象,否則您將只獲得根數組中的值(以及根據您的設置而產生的大量通知)。在處理多維數組時,您應該使用RecursiveArrayIterator,因為它允許您遍歷當前條目,但這取決於您自己去做。 RecursiveIteratorIterator是一個裝飾器,它為您執行此操作。它獲取RecursiveArrayIterator,遍歷它並遍歷它找到的任何可迭代條目(依此類推)。本質上,它“展平”了RecursiveArrayIterator。您可以通過調用RecursiveIteratorIterator::getDepth() 來獲取當前迭代的深度以進行跟踪。但是,如果您想返回對象,請小心使用RecursiveArrayIterator和RecursiveIteratorIterator;對像被視為可迭代的,因此將被迭代。
迭代目錄列表
您無疑需要在某個時間點遍歷目錄及其文件,並且可以使用PHP提供的內置函數(例如scandir()
或glob()
)來實現這一點,但您也可以使用DirectoryIterator。在其最簡單的形式中,DirectoryIterator 非常強大,但它也可以被子類化和增強。這是一個使用DirectoryIterator迭代目錄的示例:
<code class="language-php"><?php // 创建新的DirectoryIterator对象 $dir = new DirectoryIterator("/my/directory/path"); // 遍历目录列表 foreach ($dir as $item) { echo $item . "<br>"; }</code>
輸出顯然取決於您指定的路徑以及目錄的內容。例如:
<code>. .. api index.php lib workspace</code>
不要忘記,對於DirectoryIterator以及許多其他SPL迭代器,您可以使用異常來處理任何錯誤。
<code class="language-php"><?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>"; }</code>
<code>0: sitepoint 1: phpmaster 2: buildmobile 3: rubysource 4: designfestival 5: cloudspring</code>
使用諸如DirectoryIterator::isDot()、DirectoryIterator::getType()和DirectoryIterator::getSize()等許多其他方法,幾乎涵蓋了您所有基本的目錄信息需求。您甚至可以將DirectoryIterator與FilterIterator或RegexIterator組合以返回匹配特定條件的文件。例如:
<code class="language-php"><?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>"; } } else { echo $key . ": " . $value . "<br>"; } }</code>
SPL還提供RecursiveDirectoryIterator,其使用方法與RecursiveArrayIterator相同。遞歸遍歷目錄的函數通常會包含許多用於檢查有效目錄和文件的條件檢查,而RecursiveDirectoryIterator可以為您完成大部分工作,從而產生更簡潔的代碼。但是,有一個警告。 RecursiveDirectoryIterator不返回空目錄;如果目錄包含許多子目錄但沒有文件,它將返回空結果(類似於Git的行為)。
<code>0: sitepoint 1: phpmaster 0: buildmobile 1: rubysource 0: designfestival 1: cloudspring 3: not an array</code>
我的輸出類似於:
<code class="language-php"><?php ... $iter = new RecursiveArrayIterator($arr); // 遍历对象 // 我们需要创建一个RecursiveIteratorIterator实例 foreach (new RecursiveIteratorIterator($iter) as $key => $value) { echo $key . ": " . $value . "<br>"; }</code>
總結
希望您現在意識到迭代並非像我最初認為的那樣複雜,它是我們程序員每天都在做的事情。在本文中,我介紹了迭代以及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中文網其他相關文章!