关于PHP中foreach循环修改数组值失效的问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于PHP中foreach循环修改数组值失效的问题相关的知识,希望对你有一定的参考价值。
class IndexAction extends Action
……
//$this->contracts是一个数据库查询后返回的集合
$this->contracts = ……;
//请看以下这个foreach循环
foreach($this->contracts as $k => &$v)
$v["khdq"] = $this->getNameById($v["khdq"],"khdq");
//我测试过,getNameById这个函数是能够输出正确的值的
//但是为什么在这个循环结束后输出$this->contracts依然是循环修改之前的值呢?
private function getNameById($id,$table)……
……
这是我的临时解决方案,这样就可以:
$tmp_data = $this->contracts;
foreach($this->contracts as $k => $v)
//$v["khdq"] = $this->getNameById($v["khdq"],"khdq");(上面的改成$k=>&$v)
//或者:$this->contracts[$k]["khdq"] = $this->getNameById($v["khdq"],"khdq");
//上面这两句代码都是不行的,最后$this->contracts的值没有改变,为什么??
$tmp_data[$k]["khdq"] = $this->getNameById($v["khdq"],"khdq");
$this->contracts = $tmp_data;
unset($tmp_data);
我就是不明白为什么引用赋值会失效。。
$tmp_data = $this->contracts;
foreach($tmp_data as $k => & $v)
$v["khdq"] = $this->getNameById($v["khdq"],"khdq");
$this->contracts = $tmp_data;
unset($tmp_data);
foreach($this->contracts as $k => &$v)
这样不成功是因为 foreach 此方法仅在被遍历的数组可以被引用时才可用(例如是个变量)。
就是说必须是变量才能在foreach 中传引用 参考技术A foreach($this->contracts as $k => &$v)
你这里用了个&$v``一直指向这个地址·····去掉它··或者用楼上的方法·· 参考技术B $v["khdq"] = $this->getNameById($v["khdq"],"khdq");
换成
$this->contracts[$k]["khdq"] = $this->getNameById($v["khdq"],"khdq");
PHP中for和foreach背后发生了什么和关于迭代器的理解
for和foreach简单描述
php中的for
处理方式类似于c语言,只要expr2
表达式为真,则循环会一直继续下去:
for (expr1; expr2; expr3)
statement
php中的foreach
主要是为了遍历数组和对象:
foreach开始执行的时候,数组内部的指针会自动指向第一个单元,每进行一次循环,数组和对象的内部指针为自动移动一位,尽量在移动的时候不要修改其值.
foreach (array_expression as $value)
statement
foreach (array_expression as $key => $value)
statement
迭代器
上述的for
和foreach
描述很简单.
foreach
能够循环任何迭代器对象,而这些对象不仅仅是数组,可以是任何类型的对象.
一个迭代器是一个对象,迭代器对象可能是一个数组,也可能是一个目录,甚至可以是数据库查询结果集.
SPL(the Standard PHP Library)通过内建的类,提供了很大数量的迭代器,从而让代码更有效,更可读.
The DirectoryIterator class
try {
$iterator = new \DirectoryIterator($directory);
foreach ($iterator as $info) {
if (!$info->isDot() && $info->isFile()) {
echo $info->__toString() . "\n";
}
}
} catch (Exception $e) {
echo get_class($e) . ": " . $e->getMessage();
}
ArrayIterator
$arr = ["java", "python", "php"];
$iter = new ArrayIterator($arr);
foreach ($iter as $key => $value) {
echo $key . ": " . $value . "\n";
}
看一个另类的用法:
$val = ["shell", "python", "php"];
$arr = new \ArrayIterator($val);
while ($arr->valid()) {
echo $arr->key() . " : " . $arr->current() . ":" . $arr->offsetGet($arr->key()) . PHP_EOL;
$arr->offsetSet($arr->key(), "ok");
$arr->next();
}
print_r($arr->getArrayCopy());
next()
方法就是进行指针移动的关键
为什么使用迭代器
- 在处理具有大量数据的对象时,使用迭代器比数组更有效,因为在
foreach
迭代数组的时候会拷贝数组对象,而SPL迭代器通过内部封装每一次只会输出一个元素则更有效. - 延迟加载,迭代器在使用的时候能够加载仅仅需要的数据
- 迭代器真正有用点在于用途比较广泛,能够通过实现迭代器接口创造出更多的数据结构
如何创建一个自定义迭代器
SPL迭代器类已经提供了很多功能的迭代器,PHP也提供了接口Iterator
,IteratorAggregate
让程序员创建自定义的迭代器类.
只要实现接口Iterator
的五个方法就可以创建一个迭代器类,rewind()
,current()
, key()
,next()
,valid()
当迭代开始,PHP调用rewind()
方法让指针回到数据集开始处,通过调用valid()
方法检查数据是否可用,假如返回为true,则调用current()
方法获取当前指针对应的值,同时key()
能够获取到当前指针的key,最后再调用next()
方法重复上述的流程.
class myIterator implements Iterator {
}
IteratorAggregate
Iterator
接口比较适合于创建一个内部的迭代器,理想的情况下,使用IteratorAggregate
更加适合创建一个外部的迭代器,只要实现getIterator()
方法即可.
IteratorAggregate
和Iterator
的关系类似于容器和具体容器的关系
class myIteratorAggregate implements IteratorAggregate {
protected $arr = array("php", "python", "shell");
protected $type;
function __construct($type = 1) {
$this->type = $type;
}
public function getIterator() {
if ($this->type == 1)
return new ArrayIterator($this->arr);
else
return new myIterator($this->arr);
}
}
$obj = new myIteratorAggregate ;
foreach ($obj as $v) {
echo $v . "\n";
}
总结:
- 理解迭代器很容易,但是创建一个有意义的迭代器则不容易,这就类似于学习算法可能很容易,但如何实际运用则是另外一回事.
- 通过SPL迭代器,避免了程序员自造轮子,同时也提供了更多的数据结构.
- 至于使用数组还是迭代器,则要根据实际情况,比如从效率,可读性等方面衡量.
- 通过SPL类和接口,PHP提供了更多扩展,这也是PHP越来越现代化的证明.
以上是关于关于PHP中foreach循环修改数组值失效的问题的主要内容,如果未能解决你的问题,请参考以下文章
PHP中for和foreach背后发生了什么和关于迭代器的理解