PHP的SPL擴充套件庫(四)函式

硬核專案經理發表於2021-10-27

今天我們繼續來學習 SPL 中的內容,這篇文章的內容是比較簡單的關於 SPL 中所提供的一系列函式相關的內容。其實在之前的不少文章中我們都已經接觸過一些 SPL 中提供的函式了。這次我們就詳細地再好好學習一下。

類資訊相關函式

類資訊相關的函式主要都是檢視一些類的資訊的函式,並沒有什麼操作類功能的函式。

類的繼承、介面、特性檢視

首先,我們來看一下,如果想要獲得當前類實現了哪個介面,那麼我們就直接使用一個 class_implements() 就可以了。

interface A {}
interface B {}

class TestA implements A,B {}

var_dump(class_implements(new TestA));
// array(2) {
//     ["A"]=>
//     string(1) "A"
//     ["B"]=>
//     string(1) "B"
//   }

可以看到,它返回的是一個陣列,顯示的就是我們當前這個類物件所實現的介面名稱。如果我們查詢的類沒有實現任何介面,那麼它返回的就是空的陣列。

var_dump(class_implements(new stdClass()));
// array(0) {
// }

同樣,我們還可以檢視某個類物件的父類。

class C{}

class TestB extends C {}

var_dump(class_parents(new TestB));
// array(1) {
//     ["C"]=>
//     string(1) "C"
//   }

雖說 PHP 是單繼承型的語言,但使用 class_parents() 這個函式依然返回的是一個陣列。如果類物件沒有父類的話,那麼也同樣返回的是一個空的陣列。

class TestC {
    use D, E;
}

var_dump(class_uses(new TestC));
// array(2) {
//     ["D"]=>
//     string(1) "D"
//     ["E"]=>
//     string(1) "E"
//   }

最後,我們還可以通過 class_uses() 函式來獲取到當前類物件所使用的 trait 特性資訊。

類的雜湊及類ID

做過 Java 開發的同學一定都見過所有的類都會有一個 hashCode() 方法。這個方法在 Java 中的作用就是返回一個物件的 Hash 碼值。通常用於物件是否相等以及唯一的判斷,在 Java 中,所有的類都會預設繼承自 Object 這個基類,而這個基類中就自帶這個方法。

但是,在 PHP 中,類是沒有這樣一個全域性基類的,自然也就沒有這樣的方法。顯然,只能靠其他的擴充套件工具幫我們提供這樣的能力了。好巧不巧,SPL 中正好就提供了這樣的功能。

var_dump(spl_object_hash(new TestA));
// string(32) "000000000ed109570000000025e36d74"

$a = new TestA;
var_dump(spl_object_hash($a));
// string(32) "000000000ed109570000000025e36d74"

var_dump(spl_object_id(new TestA));
// int(2)
var_dump(spl_object_id($a));
// int(1)

spl_object_hash() 函式就是用於獲取一個物件的 Hash 值的,它是完整 Hash 值,不像 Java 的 hashCode() 方法返回的是數字型別的值。同樣的類别範本所例項化的物件返回的內容是一樣的。

spl_object_id() 返回的是物件的 ID 。它的結果對於不同的 new ,也就是例項化的物件來說是不同的。如果物件一直存在,那麼它的 ID 值是不會發生變化的,而物件被銷燬的話,則 ID 值也會被回收並交給其它物件使用。其實直接列印物件我們就可以看到這個 ID 值。

var_dump($a);
// object(TestA)#1 (0) {
// }
var_dump(new TestA);
// object(TestA)#2 (0) {
// }

井號後面的那個數字就是我們物件的 ID 值,也就是 spl_object_id() 所獲得的內容。

獲取 SPL 庫中的所有可用類資訊

這個函式返回的是 SPL 這個庫中所有的可以使用的類名資訊。

var_dump(spl_classes());
// array(55) {
//     ["AppendIterator"]=>
//     string(14) "AppendIterator"
//     ["ArrayIterator"]=>
//     string(13) "ArrayIterator"
//     ["ArrayObject"]=>
//     string(11) "ArrayObject"
//     ["BadFunctionCallException"]=>
//     string(24) "BadFunctionCallException"
//     ["BadMethodCallException"]=>
//     string(22) "BadMethodCallException"
//     ["CachingIterator"]=>
//     string(15) "CachingIterator"
//     ["CallbackFilterIterator"]=>
//     string(22) "CallbackFilterIterator"
//     ["DirectoryIterator"]=>
//     string(17) "DirectoryIterator"
//     ["DomainException"]=>
//     string(15) "DomainException"
//     ["EmptyIterator"]=>
//     string(13) "EmptyIterator"
//     ["FilesystemIterator"]=>
//     string(18) "FilesystemIterator"
//     ["FilterIterator"]=>
//     string(14) "FilterIterator"
//     ["GlobIterator"]=>
// …………………………
// …………………………

可以看到,我們前面講過的許多類資訊在這裡都可以看到。

迭代器相關函式

迭代器相關的函式在上一篇文章講迭代器的時候其實已經出現過了,那就是非常好用的 iterator_to_array() 這個函式。

$iterator = new ArrayIterator(['a'=>'a1', 'b'=>'b1', 'c'=>'c1']);

var_dump(iterator_to_array($iterator, true));
// array(3) {
//     ["a"]=>
//     string(2) "a1"
//     ["b"]=>
//     string(2) "b1"
//     ["c"]=>
//     string(2) "c1"
//   }

我們可以想象成在這個函式內部,其實就是使用 foreach() 遍歷了一下這個迭代器,並將所有的結果放在陣列中返回回來。這個函式還有第二個引數,它的作用是讓鍵不使用原來的鍵值,而是使用預設陣列下標的方式排列。

var_dump(iterator_to_array($iterator, false));
// array(3) {
//     [0]=>
//     string(2) "a1"
//     [1]=>
//     string(2) "b1"
//     [2]=>
//     string(2) "c1"
//   }

除了直接獲得迭代器遍歷的結果之外,我們還可以通過一個函式直接獲取迭代器內部元素的數量。

var_dump(iterator_count($iterator)); // int(3)

其實 iterator_count() 這個函式就像是 count() 函式的迭代器版本。

function printCaps($iterator){
    echo strtoupper($iterator->current()), PHP_EOL;
    return true;
}
iterator_apply($iterator, "printCaps", array($iterator));
// A1
// B1
// C1

最後這個 iterator_apply() 函式就是讓我們可以通過一個指定的回撥函式來遍歷一個迭代器。

自動載入相關函式

對於自動載入函式來說,我們在最早的文章,也就是講 Composer 的那一系列文章中就已經接觸過了。不過當時我們只是學習了一個 spl_autoload_register() 函式。今天我們就來多學習兩個函式,不過首先還是來看看 spl_autoload_register() 函式的使用。

function autoloadA($class){
    if(is_file('./autoloadA/' . $class . '.php')){
        require_once './autoloadA/' . $class . '.php';
    }
    
}
spl_autoload_register('autoloadA');

spl_autoload_register(function($class){
    if(is_file('./autoloadB/' . $class . '.php')){
        require_once './autoloadB/' . $class . '.php';
    }
});

$sky = new Sky();
$sky->result();
// This is Sky PHP!

$planet = new Planet();
$planet->result();
// This is Planet PHP!

在這段測試程式碼中,我們通過回撥函式和匿名函式兩種形式註冊了兩個 spl_autoload_register() 。這樣當我們使用當前檔案中未定義的類時就會去這兩個 autoload 中查詢。在之前講 Composer 時我們就講過,spl_autoload_register() 比 \_\_autolod() 好的地方就是它維護的是一個自動載入列表,相當於是多個 \_\_autoload() 的功能。我們通過另外一個函式就可以看到當前我們已經註冊了多少個自動載入的函式。

var_dump(spl_autoload_functions());
// array(2) {
//     [0]=>
//     string(9) "autoloadA"
//     [1]=>
//     object(Closure)#3 (1) {
//       ["parameter"]=>
//       array(1) {
//         ["$class"]=>
//         string(10) "<required>"
//       }
//     }
//   }

當然,能夠不斷的註冊進來也可以刪除掉。

spl_autoload_unregister('autoloadA');

var_dump(spl_autoload_functions());
// array(1) {
//     [0]=>
//     object(Closure)#3 (1) {
//       ["parameter"]=>
//       array(1) {
//         ["$class"]=>
//         string(10) "<required>"
//       }
//     }
//   }

關於 spl_autoload_register() 在實際工程中的應用,也就是 Composer 中的應用,有興趣的同學可以移步之前的文章。

總結

怎麼樣,一路看下來是不是發現其實不少的功能大家在日常的開發學習中都已經接觸過了。這些函式就是 SPL 擴充套件庫中所提供的功能了,其實通過這幾篇文章的學習,我們就已經發現了,SPL 擴充套件庫為我們提供的都是很基礎的一些 資料結構 、迭代器、設計模式 之類的功能封裝,有很多東西真的比自己實現要簡單方便很多,包括我們下一篇還要繼續學習的檔案操作以及設計模式的實現。

測試程式碼:

https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/01/source/6.PHP的SPL擴充套件庫(四)函式.php

參考文件:

https://www.php.net/manual/zh/ref.spl.php

相關文章