<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; foreach</title>
	<atom:link href="https://www.phppan.com/tag/foreach/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sun, 12 Apr 2026 03:47:23 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>foreach的指针问题</title>
		<link>https://www.phppan.com/2013/04/foreach-exception/</link>
		<comments>https://www.phppan.com/2013/04/foreach-exception/#comments</comments>
		<pubDate>Sun, 14 Apr 2013 23:46:51 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[foreach]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1821</guid>
		<description><![CDATA[在PHP中，foreach 语法结构提供了遍历数组的简单方式。 foreach 仅能够应用于数组和对象，如果尝 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>在PHP中，foreach 语法结构提供了遍历数组的简单方式。 foreach 仅能够应用于数组和对象，如果尝试应用于其他数据类型的变量，或者未初始化的变量，将导致错误。 foreach每次循环时，当前单元的值被赋给 $value 并且数组内部的指针向前移一步（因此下一次循环中将会得到下一个单元）。</p>
<p>但是手册中提醒我们：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="shell" style="font-family:monospace;">Note:
当 foreach 开始执行时，数组内部的指针会自动指向第一个单元。这意味着不需要在 foreach 循环之前调用 reset()。
在循环中修改 foreach 依赖其内部数组指针将可能导致意外的行为。</pre></td></tr></table></div>

<p>这里我们所要说的是foreach可能导致的意外情况。如代码1示例：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$arr</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">2</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">3</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">4</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">5</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$key</span> <span style="color: #339933;">=&gt;</span> <span style="color: #339933;">&amp;</span><span style="color: #000088;">$row</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #b1b100;">echo</span> <span style="color: #990000;">key</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'=&gt;'</span><span style="color: #339933;">,</span> <span style="color: #990000;">current</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>会输出什么？</p>
<p>如代码2示例呢？</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$arr</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">2</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">3</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">4</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">5</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$key</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$row</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #b1b100;">echo</span> <span style="color: #990000;">key</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'=&gt;'</span><span style="color: #339933;">,</span> <span style="color: #990000;">current</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>会输出什么？</p>
<p>代码1会依次输出变量，但是第一个元素并没有在输出结果中出现。</p>
<p>代码2只会输出数组的第二个元素。</p>
<p>为什么呢？</p>
<p>将代码2在VLD扩展中查看，</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="shell" style="font-family:monospace;">number of ops:  22
compiled vars:  !0 = $arr, !1 = $key, !2 = $row
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   2     0  &gt;   INIT_ARRAY                                       ~0      1
         1      ADD_ARRAY_ELEMENT                                ~0      2
         2      ADD_ARRAY_ELEMENT                                ~0      3
         3      ADD_ARRAY_ELEMENT                                ~0      4
         4      ADD_ARRAY_ELEMENT                                ~0      5
         5      ASSIGN                                                   !0, ~0
   4     6    &gt; FE_RESET                                         $2      !0, -&gt;20
         7  &gt; &gt; FE_FETCH                                         $3      $2, -&gt;20
         8  &gt;   ZEND_OP_DATA                                     ~5      
         9      ASSIGN                                                   !2, $3
        10      ASSIGN                                                   !1, ~5
   5    11      SEND_REF                                                 !0
        12      DO_FCALL                                      1  $7      'key'
        13      ECHO                                                     $7
        14      ECHO                                                     '%3D%3E'
        15      SEND_REF                                                 !0
        16      DO_FCALL                                      1  $8      'current'
        17      ECHO                                                     $8
        18      ECHO                                                     '%0D%0A'
   6    19    &gt; JMP                                                      -&gt;7
        20  &gt;   SWITCH_FREE                                              $2
   8    21    &gt; RETURN                                                   1</pre></td></tr></table></div>

<p>从上面VLD扩展输出结果结合PHP的源代码可以知道，在foreach遍历之前, PHP内核首先会有个FE_RESET操作来重置数组的内部指针，也就是pInternalPointer, 然后通过每次FE_FETCH将pInternalPointer指向数组的下一个元素，从而实现顺序遍历。<br />
并且每次FE_FETCH的结果都会被一个全局的中间变量存储，以给下一次的获取元素使用。</p>
<p>从这两个例子可以引申出三个问题：</p>
<p><strong>1、为什么foreach循环体中执行key或current会显示第二个元素（非引用情况）？</strong><br />
以key函数为例，我们执行函数调用时，会执行中间代码SEND_REF，此中间代码会将没有设置引用的变量复制一份并设置为引用。当进入循环体时，PHP内核已经经过了一次fetch操作，相当于执行了一次next操作，当前元素指向第二个元素。因此我们在foreach的循环体中执行key函数时，key中调用的数组变量为PHP执行了一次fetch操作的数组拷贝，此时foreach的内部指针指向第二个元素。</p>
<p><strong>2、为什么在foreach中执行end等操作，其循环过程不变？</strong><br />
在遍历的代码中通过end，next等操作数组的指针，数组的指针不会变化，这是因为在PHP内核进行FETCH操作时，会通过中间变量存储当前操作数组的内部指针，每遍历一个元素，会先获取之前存储的指针位置，获取下一个元素后，再恢复指针位置。</p>
<p><strong>3、为什么$row的引用和非引用情况下输出结果不同？</strong><br />
如果是引用，PHP内核在reset数组时，会直接分裂数组，生成一个数组的拷贝，并将其设置为引用。<br />
如果是非引用，PHP内核在reset数组时，当数组的引用计数大于1，并且不存在引用时，会拷贝数组供foreach使用，其它情况使用原数组，将其引用计数加1。</p>
<p>因为引用的不同，在循环体中给函数传递参数时其结果不同，导致看到的foreach数组内部指针变化的不同。对于非引用且引用计数大于1的情况，其本身就是两个不同的数组，在RESET时就不同了。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2013/04/foreach-exception/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP源码阅读笔记二十四 ：iterator实现中当值为false时无法完成迭代的原因分析</title>
		<link>https://www.phppan.com/2010/04/php-source-24-iterator-false-value/</link>
		<comments>https://www.phppan.com/2010/04/php-source-24-iterator-false-value/#comments</comments>
		<pubDate>Mon, 26 Apr 2010 00:49:11 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[foreach]]></category>
		<category><![CDATA[Iterator]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[迭代器]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=661</guid>
		<description><![CDATA[PHP源码阅读笔记二十四 ：iterator实现中当值为false时无法完成迭代的原因分析 在前面有一篇文章迭 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>PHP源码阅读笔记二十四 ：iterator实现中当值为false时无法完成迭代的原因分析<br />
在前面有一篇文章<a href="http://www.phppan.com/2010/04/php-iterator-and-yii-cmapiterator">迭代器的简单实现及Yii框架中的迭代器实现</a>中有一个简单的迭代器的实现，此处遗留了一个问题，当迭代的值中包含false时，使用foreach循环的时候在这个地方就结束了，原因是什么呢？<br />
<br />
在鸟哥的blog中，很久以前一篇文章对iterator的实现作了一些说明：<a href="http://www.laruence.com/2008/10/31/574.html">http://www.laruence.com/2008/10/31/574.html</a><br />
但是并没有对false的值的处理作相关说明<br />
顺着鸟哥的思路在Zend/zend_vm_execute.h文件的8131行找到相关的线索，如下所示代码：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/*  */</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>iter <span style="color: #339933;">||</span> <span style="color: #009900;">&#40;</span>iter<span style="color: #339933;">-&gt;</span>index <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">&amp;&amp;</span> iter<span style="color: #339933;">-&gt;</span>funcs<span style="color: #339933;">-&gt;</span>valid<span style="color: #009900;">&#40;</span>iter TSRMLS_CC<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> FAILURE<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #808080; font-style: italic;">/* reached end of iteration */</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>EG<span style="color: #009900;">&#40;</span>exception<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
array<span style="color: #339933;">-&gt;</span>refcount<span style="color: #339933;">--;</span>
zval_ptr_dtor<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>array<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
ZEND_VM_NEXT_OPCODE<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
ZEND_VM_JMP<span style="color: #009900;">&#40;</span>EX<span style="color: #009900;">&#40;</span>op_array<span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span>opcodes<span style="color: #339933;">+</span>opline<span style="color: #339933;">-&gt;</span>op2.<span style="color: #202020;">u</span>.<span style="color: #202020;">opline_num</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>对于实现的简单的迭代器，iter->funcs->valid(iter TSRMLS_CC) 方法调用的valid()方法，<br />
如果我们的值为false时，通过current返回的值为false，此时通过foreach访问时，遍历就在此中断了，程序会继续执行下面的代码，而不是这个循环了<br />
<br />
解决方案<br />
将数组中的key和value分开处理<br />
在valid()，rewind(),next()方法中操作key，而不是value<br />
仅在current中返回value<br />
如文章<a href="http://www.phppan.com/2010/04/php-iterator-and-yii-cmapiterator">迭代器的简单实现及Yii框架中的迭代器实现</a>中的Yii框架中的CMapIterator的实现</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2010/04/php-source-24-iterator-false-value/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
