最近碰到了一个问题,PHP中如何遍历对象(迭代对象)。
第一部分 遍历对象
一、问题描述
我们知道foreach可以遍历数组,也可以遍历对象,但是默认情况下只能遍历对象public属性。
class User { public $name = 'salmonl'; private $sex = 'm'; private $age = 30; public static function getAge() { return $this->age; } } $user = new User(); var_dump($user); foreach ($user as $key => $value) { echo $key, '==>', $value, PHP_EOL; }
以上输入结果:
object(User)#1 (3) { ["name"]=> string(7) "salmonl" ["sex":"User":private]=> string(1) "m" ["age":"User":private]=> int(30) } name==>salmonl
我们可以发现只输出了public权限的属性,那么如果想输出全部属性,怎么处理呢?
二、解决办法一
在类中定义一个公有方法
public function iterateVisible() { echo "MyClass::iterateVisible:\n"; foreach($this as $key => $value) { print "$key => $value\n"; } }
三、解决办法二
让对象所属的类实现Iterator接口(原理可参考第二部分)
class Test implements Iterator { private $item = [ 'id' => 1, 'name' => 'php' ]; public function __construct($item) { if (is_array($item)) { $this->item = $item; } } public function rewind() { reset($this->item); } public function current() { return current($this->item); } public function key() { return key($this->item); } public function next() { return next($this->item); } public function valid() { return ($this->current()!== false); } } //测试 $test = new Test(['php', 'golang', 'lua', 'python']); foreach ($test as $k => $v) { echo $k, '=>', $v, PHP_EOL; }
以上输出:
0=>php
1=>golang
2=>lua
3=>python
第二部分 迭代器
一、相关概念和原理
0、什么是迭代器
PHP通过实现Iterator接口, 封装一个自己的类,这个类是可迭代的,称为迭代器
1、迭代器的好处。
可以让对象自行决定如何遍历以及每次遍历时那些值可用。遍历迭代器对象,用foreach、while都可以。
Iterator extends Traversable { /* 方法 */ abstract public current ( void ) : mixed abstract public key ( void ) : scalar abstract public next ( void ) : void abstract public rewind ( void ) : void abstract public valid ( void ) : bool }
接口中定义了5个方法:
Iterator::current — 返回当前元素
Iterator::key — 返回当前元素的键
Iterator::next — 向前移动到下一个元素
Iterator::rewind — 返回到迭代器的第一个元素
Iterator::valid — 检查当前位置是否有效
3、通过foreach来遍历迭代器对象的时候会自动调用Iterator接口提供的5个方法
调用顺序: rewind/next->valid->current->key(如果是第一次就是rewind, 否则是next)。实际例子可参考官网
通过在相关方法中设置返回那些属性
二、迭代器应用场景
0、遍历对象
可见第一部分相关内容
1、要处理的结果集占用大量内存
1.0 定义一个功能和range一样的迭代器
class Xrange implements Iterator { protected $start; protected $limit; protected $step; protected $current; public function __construct($start, $limit, $step = 1) { $this->start = $start; $this->limit = $limit; $this->step = $step; } public function rewind() { $this->current = $this->start; } public function next() { $this->current += $this->step; } public function current() { return $this->current; } public function key() { return $this->current + 1; } public function valid() { return $this->current <= $this->limit; } } // test case foreach (new Xrange(0, 9) as $key => $val) { echo $key, ' => ', $val, PHP_EOL; } echo '******************************', PHP_EOL; // range function foreach (range(0, 9) as $key => $number) { echo $key, ' => ', $number, PHP_EOL; }
以上代码运行结果
[root@izj6cfhaw27k49x8usszs3z coroutine]# php xrange.php 1 => 0 2 => 1 3 => 2 4 => 3 5 => 4 6 => 5 7 => 6 8 => 7 9 => 8 10 => 9 ****************************** 0 => 0 1 => 1 2 => 2 3 => 3 4 => 4 5 => 5 6 => 6 7 => 7 8 => 8 9 => 9
1.1 占用内存差别
// range $startMemory = memory_get_usage(); $arr = range(0, 500000); echo 'range(): ', memory_get_usage() - $startMemory, " bytes\n"; unset($arr); // xrange $startMemory = memory_get_usage(); $arr = new Xrange(0, 500000); echo 'xrange(): ', memory_get_usage() - $startMemory, " bytes\n";
以上代码执行结果
[root@izj6cfhaw27k49x8usszs3z coroutine]# php xrange.php range(): 72194960 bytes xrange(): 440 bytes
1.2、执行结果分析
原生的range函数返回的是全量的数组,占用内存比较多;迭代器Xrange返回的是对象,只是当前对象占用的内存。
三、常用迭代器
0、官方SPL中定义了很多常用迭代器(点击查看)
参考:
PHP手册遍历对象
PHP协程
segmentfault:php 迭代接口的作用
Wikipedia: Iterator
Wikipedia: 迭代器