开发者学堂课程【PHP 进阶教程-由浅入深掌握面向对象开发-第二阶段:lterator 迭代器】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/712/detail/12716
lterator 迭代器
内容介绍:
一、目标
二、概念
三、步骤
四、示例
五、小结
一、目标
目标∶理解 lterator 迭代器与 foreach 的关系,掌握迭代器的工作原理,实现自主控制迭代器。
三、foreach 对于对象的遍历完全受制于访问修饰限定符,我们无法控制 foreach 遍历对象时是怎样实现遍历的,但是如果想要控制,lterator 迭代器能够帮助我们实现对 foreach 不管是内外都可以进行的限定。
二、概念
lterator 迭代器: PHP 内置的一种能够修改 foreach 内部运行机制的接口
·1.lterator 迭代器内置了5个抽象方法,实现迭代器的类必须实现5个抽象方法。只要类实现了 lterator 迭代器,就意味着对这个类的对象进行遍历的时候可以自己控制。
2.实现了 lterator 迭代器的类的对象在进行 foreach 时,不会按照 foreach 原来的机制处理,而是调用迭代器的5个方法
3.foreach 本身的执行步骤
①初始化目标:将对象指针指向第一个属性(执行一次)
②判定指针是否有效:判定当前指针指向的元素是否存在,存在下一步,不存在终止(N+1次执行,有 N 个数据,最简单的1次是没有数据要遍历)
③取出当期指针元素下标(属性名)︰将当前属性的名字取出来存储到变量( N 次执行)
④取出当前指针元素值(属性值)︰将当前属性的值取出来存储到变量( N 次执行)
(③④理论上是一步,键值对是取出元素把键复制给键变量)
⑤将指针指向下一个:将取出元素后的指针指向下一个属性( N 次执行)
五、4.lterator 迭代器的本质就是改变 foreach 内部的5个方式,实现开发者的自定义控制
三、步骤
1、当前类的对象存在遍历需求
2、当前遍历的方式需要自定义控制
3、当前类实现 lterator 迭代器,并实现对应5个方法
4、遍历对象
四、示例
1.简单迭代控制(针对索引|数组)
索引数组是指在类里有私有属性,希望遍历对象时把属性里面的值给遍历出来,而不是遍历公有属性。
current:获取当前数组指针所指向的元素值
key:与当前指针元素所指向的元素的下标
next:将当前元素的指针下移一位
rewind:让当前数组的指针指向第一个元素
valid:判定当前指针的有效性
这五个方法对应 foreach 的五个步骤。
class Person implements Iterator{
#属性列表(索引数组
private $properties = [ 'name ' , 'age ' , ' gender ' , ' height ' , ' weight'];
#下标属性
private $key = 0;
#实现5个方法
public function current(){
#取出当前数组元素值
return $this->properties[$this->key] ;
}
public function key(){
#返回当前下标
return $this->key;
}
public function next(){
#不需要返回值:当前下标+1
$this->key++;
}
public function rewind(){
#不需要返回值:重置数组下标
$this->key = 0;
}
public function valid(){
#需要返回布尔值:判定下标对应的元素是否存在即可
return isset($this->properties[$this->key]);
}
}
定义下标属性叫做 key,原始下标等于零。现在想要遍历属性里的元素,current 方法要获取当前数组元素当前指针位置的元素值,当前元素值即为下标0,所以只要把数组属性取出来,然后再把当前的下标给它就可以了。key 是获取当前指针,只需要把这个返回给外面就可以。next 要做的是 key++。rewind 让下标为0,不需要返回值。valid 是判定当前指针是否有效,判定的方式有很多种,此处 isset 是把下标给你去取,看能不能取到这个值,就可以判定是否有这个下标。
先不考虑实现接口,只有一个类。我希望对私有属性进行遍历,还建了一个属性用来保存下标。
然后进行实例化
$p = new Person();
foreach($p as $k =>$v){
echo $k . ' : ' . $v . '
';
}
效果是遍历不出来任何东西,是因为正常 foreach 遍历是遍历公有属性,但是此处没有公有属性。我们定义的方法也没有改变 foreach 的原理,foreach 还是按照原来的逻辑运行,但是此时如果添加了 implements Iterator,如果没有实现它的方法,系统依然会报错,系统会显示 person 有五个方法必须要实现,做完五个方法后系统就不会报错,即便是不进行 foreach。此时再进行 foreach 会发现输出了数组的下标值,此时就实现了控制。
添加 echo__METHOD__,'
';可以看出方法被运行以及运行了多少次
第一个走的 person 的 rewind,首先第一个 foreach 是重置数组指针,第二步判定是否有效,有效之后取出当前值和当前下标,再指针下移,下移之后又进行 valid,rewind 只进行一次,valid 进行六次,一共有五个元素,所以 valid 是 N+1个,而其他的都只有对应的数组元素个数。这样就实现了想要控制的对象遍历方式,但是此时访问的只是索引数组。索引针对的是简单数组,能够通过下标控制,但是如果下标是指定的而且不是连续的,就不能使用这种方式。所以我们应用一种针对全部数组的复杂形式
2.复杂迭代控制(针对全部数组)
class Person implements Iterator{
private $info = [
' name' =>'',
'age'=>5,
'gender'-> '',
' height'=> 0,
'weight'=> 0
];
#实现5个方法
public function current(){
#返回当前元素当前的值:当前元素是一个数组,那就可以使用数组函数 current 来处理 return current($this->info);
}
public function key(){
#当前方法不需要返回值:说明外部调用的时候不会用到返回值
next($this->info);
}
public function rewind(){
#也不需要返回值
reset($this->info);
}
public function valid(){
# 需要返回布尔值:而且没有直接的函数能够判定:可以通过 key 取出当前指针元素的下标,然后再通过这个下标去判定元素是否存在(因为 false 不能成为数组元素下标,系统会自动转换成0)
return isset($this->info[key($this->info)]);
}
}
此时它的实现方式 current,数组会用数组函数 current 来获取当前数组指针所对应的值,这个时候不去控制而是交给函数来控制。current 取出当前数组的值,key 函数取出当前数组的下标,next 函数让数组指针下移,rewind 调用 reset 函数重置数组指针,所以数组下标关联还是索引,连不联续都不重要。valid 判定需要返回一个布尔值,而且没有直接的函数去判定是否有效,所以一般是通过 key
把当前数据组的指针取出来,再看对应的数组有没有下标。
此时是一样的效果。前一种方式如果索引不连续马上就会报错,因为是从0每次加一,如果不连续就意味着下一个元素就会出错,current 因为下标没有对应的元素而取不到值,但是第二种方法就针对所有的数组,核心是数组里的函数,valid 需要判断取出来的下标,false 不能成为下标元素,所以判定有没有叫 false 的下标,如果没有就返回一个 false,其他的都可以有所以都返回 true。这就是本次遍历的核心,但是最终的本质是因为 lterator 迭代器的五个方法存在,而 foreach 在遍历时自动检测对象实现了迭代器而且有五个方法,所以 foreach 本来的逻辑就变成调用 current、key、next、rewind 和 valid 来实现。
五、小结
1、lterator 迭代器是专门针对有遍历对象控制的类的而存在的
2、迭代器能够有效控制 foreach 对对象的遍历,从而保护好内部的结构,自主控制要遍历的内容。而且本身是从内部操作,所以这种遍历可以完全控制它的逻辑和安全性这就是 lterator 迭代器的存在价值。