深入理解预定义接口
场景:平常工作中写的都是业务模块,很少会去实现这样的接口,但是在框架里面用的倒是很多。
1. Traversable(遍历)接口
该接口不能被类直接实现,如果直接写了一个普通类实现了该遍历接口,是会直接报致命的错误,提示使用 Iterator(迭代器接口)或者 IteratorAggregate(聚合迭代器接口)来实现,这两个接口后面会介绍;所有通常情况下,我们只是会用来判断该类是否可以使用 foreach 来进行遍历;
1 class Test implements Traversable
2 {
3 }
4 上面这个是错误示范,该代码会提示这样的错误:
5 Fatal error: Class Test must implement interface Traversable as part of either Iterator or
6 IteratorAggregate in Unknown on line 0
上面的大致意思是说如要实现这个接口,必须同Iterator或者IteratorAggregate来实现
正确的做法:
当我们要判断一个类是否可以使用foreach来进行遍历,只需要判断是否是traversable的实例
1 class Test
2 {
3 }
4 $test = new Test;
5 var_dump($test instanceOf Traversable);
2. Iterator(迭代器)接口
迭代器接口其实实现的原理就是类似指针的移动,当我们写一个类的时候,通过实现对应的 5 个方法:key(),current(),next(),rewind(),valid(),就可以实现数据的迭代移动,具体看以下代码
1 <?php
2 class Test implements Iterator
3 {
4 private $key;
5 private $val = [
6 'one', 7 'two', 8 'three', 9 ];
10
11 public function key()
12 {
13 return $this->key;
14 }
15
16 public function current()
17 {
18 return $this->val[$this->key];
19 }
20
21 public function next()
22 {
23 ++$this->key;
24 }
25
26 public function rewind()
27 {
28 $this->key = 0;
29 }
30
31 public function valid()
32 {
33 return isset($this->val[$this->key]);
34 }
35 }
36
37 $test = new Test;
38
39 $test->rewind();
40
41 while($test->valid()) {
42 echo $test->key . ':' . $test->current() . PHP_EOL;
43 $test->next();
44 }
## 该输出结果 :
0: one
1: two
2: three
看了这个原理我们就知道,其实迭代的移动方式:rewind()-> valid()->key() -> current() -> next() -> valid()-> key() ....-> valid();
好的,理解了上面,我们打开Iterator的接口,发现它是实现了Traversable(遍历)接口的,接下来我们来证明下:
var_dump($test instanceOf Traversable);
结果返回的是true,证明这个类的对象是可以进行遍历的。
1 foreach ($test as $key => $value){
2 echo $test->key . ':' . $test->current() . PHP_EOL;
3 }
这个的结果跟while循环实现的模式是一样的。
3. IteratorAggregate(聚合迭代器) 接口
聚合迭代器和迭代器的原理是一样的,只不过聚合迭代器已经实现了迭代器原理,你只需要实现一个 getIterator()方法来实现迭代,具体看以下代码
1 <?php
2 class Test implements IteratorAggregate
3 {
4 public $one = 1;
5 public $two = 2;
6 public $three = 3;
7
8 public function __construct()
9 {
10 $this->four = 4;
11 }
12
13 public function getIterator()
14 {
15 return new AraayIterator($this);
16 }
17 }
18
19 $test = (new Test())->getIterator();
20 $test->rewind();
21 while($test->valid()) {
22 echo $test->key() . ' : ' . $test->current() . PHP_EOL;
23 $test->next();
24 }
25
26 从上面的代码,我们可以看到我们将Test类的对象传进去当做迭代器,通过while循环的话,我们必须通过调用getIterator()方法获取到迭代器对象,然后直接进行迭代输出,而不需要去实现相关的key()等方法。
27 当然这个时候,我们肯定想知道是否可以直接从foreach进行迭代循环出去呢?那么我们来打印一下结果
28
29 $test = new Test;
30 var_dump($test instanceOf Traversable);
31
32 结果是输出bool true,所以我们接下来是直接用foreach来实现一下。
33
34 $test = new Test;
35 foreach($test as $key => $value) {
36 echo $key . ' : ' . $value . PHP_EOL;
37 }
38
39 接下来,我们看到是对对象进行迭代,这个时候我们是否可以数组进行迭代呢?
40
41 class Test implements IteratorAggregate
42 {
43 public $data;
44
45 public function __construct()
46 {
47 $this->data = [''one' => 1,'two' => 2];
48 }
49
50 public function getIterator()
51 {
52 return new AraayIterator($this->data);
53 }
54 }
55
56 同理实现的方式跟对对象进行迭代是一样的。
57
- 很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的加群(点击→)677079770
4. ArrayAccess(数组式访问)接口
通常情况下,我们会看到 this ['name'] 这样的用法,但是我们知道,$this 是一个对象,是如何使用数组方式访问的?答案就是实现了数据组访问接口 ArrayAccess,具体代码如下
1 <?php
2 class Test implements ArrayAccess
3 {
4 public $container;
5
6 public function __construct()
7 {
8 $this->container = [
9 'one' => 1,10 'two' => 2,11 'three' => 3,12 ];
13 }
14
15 public function offsetExists($offset)
16 {
17 return isset($this->container[$offset]);
18 }
19
20 public function offsetGet($offset)
21 {
22 return isset($this->container[$offset]) ? $this->container[$offset] : null;
23 }
24
25 public function offsetSet($offset,$value)
26 {
27 if (is_null($offset)) {
28 $this->container[] = $value;
29 } else {
30 $this->container[$offset] = $value;
31 }
32 }
33
34 public function offsetUnset($offset)
35 {
36 unset($this->container[$offset]);
37 }
38 }
39 $test = new Test;
40 var_dump(isset($test['one']));
41 var_dump($test['two']);
42 unset($test['two']);
43 var_dump(isset($test['two']));
44 $test['two'] = 22;
45 var_dump($test['two']);
46 $test[] = 4;
47 var_dump($test);
48 var_dump($test[0]);
49
50 当然我们也有经典的一个做法就是把对象的属性当做数组来访问
51
52 class Test implements ArrayAccess
53 {
54 public $name;
55
56 public function __construct()
57 {
58 $this->name = 'gabe';
59 }
60
61 public function offsetExists($offset)
62 {
63 return isset($this->$offset);
64 }
65
66 public function offsetGet($offset)
67 {
68 return isset($this->$offset) ? $this->$offset : null;
69 }
70
71 public function offsetSet($offset,$value)
72 {
73 $this->$offset = $value;
74 }
75
76 public function offsetUnset($offset)
77 {
78 unset($this->$offset);
79 }
80 }
81
82 $test = new Test;
83 var_dump(isset($test['name']));
84 var_dump($test['name']);
85 var_dump($test['age']);
86 $test[1] = '22';
87 var_dump($test);
88 unset($test['name']);
89 var_dump(isset($test['name']));
90 var_dump($test);
91 $test[] = 'hello world';
92 var_dump($test);
93
5. Serializable (序列化)接口
通常情况下,如果我们的类中定义了魔术方法,sleep(),wakeup () 的话,我们在进行 serialize () 的时候,会先调用sleep () 的魔术方法,我们通过返回一个数组,来定义对对象的哪些属性进行序列化,同理,我们在调用反序列化 unserialize () 方法的时候,也会先调用的wakeup()魔术方法,我们可以进行初始化,如对一个对象的属性进行赋值等操作;但是如果该类实现了序列化接口,我们就必须实现 serialize()方法和 unserialize () 方法,同时sleep()和wakeup () 两个魔术方法都会同时不再支持,具体代码看如下;
1 <?php
2 class Test
3 {
4 public $name;
5 public $age;
6
7 public function __construct()
8 {
9 $this->name = 'gabe';
10 $this->age = 25;
11 }
12
13 public function __wakeup()
14 {
15 var_dump(__METHOD__);
16 $this->age++;
17 }
18
19 public function __sleep()
20 {
21 var_dump(__METHOD__);
22 return ['name'];
23 }
24 }
25
26 $test = new Test;
27 $a = serialize($test);
28 var_dump($a);
29 var_dump(unserialize($a));
30
31 //实现序列化接口,发现魔术方法失效了
32
33 class Test implements Serializable
34 {
35 public $name;
36 public $age;
37
38 public function __construct()
39 {
40 $this->name = 'gabe';
41 $this->age = 25;
42 }
43
44 public function __wakeup()
45 {
46 var_dump(__METHOD__);
47 $this->age++;
48 }
49
50 public function __sleep()
51 {
52 var_dump(__METHOD__);
53 return ['name'];
54 }
55
56 public function serialize()
57 {
58 return serialize($this->name);
59 }
60
61 public function unserialize($serialized)
62 {
63 $this->name = unserialize($serialized);
64 $this->age = 1;
65 }
66 }
67 $test = new Test;
68 $a = serialize($test);
69 var_dump($a);
70 var_dump(unserialize($a));
71
6. Closure 类
(编辑:安卓应用网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|