今天我们继续来学习 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


硬核项目经理
90 声望18 粉丝