最近碰到了一个问题,PHP中如何遍历对象(迭代对象)。
第一部分 遍历对象
一、问题描述
我们知道foreach可以遍历数组,也可以遍历对象,但是默认情况下只能遍历对象public属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } |
以上输入结果:
1 2 3 4 5 6 7 8 9 10 | object(User) #1 (3) { [ "name" ]=> string(7) "salmonl" [ "sex" : "User" :private]=> string(1) "m" [ "age" : "User" :private]=> int(30) } name==>salmonl |
我们可以发现只输出了public权限的属性,那么如果想输出全部属性,怎么处理呢?
二、解决办法一
在类中定义一个公有方法
1 2 3 4 5 6 | public function iterateVisible() { echo "MyClass::iterateVisible:\n" ; foreach ( $this as $key => $value ) { print "$key => $value\n" ; } } |
三、解决办法二
让对象所属的类实现Iterator接口(原理可参考第二部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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都可以。
1 2 3 4 5 6 7 8 | 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一样的迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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; } |
以上代码运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | [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 占用内存差别
1 2 3 4 5 6 7 8 9 10 11 | // 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" ; |
以上代码执行结果
1 2 3 | [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: 迭代器