<?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; SPL</title>
	<atom:link href="https://www.phppan.com/tag/spl/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 04 Apr 2026 01:19:58 +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>以数组形式访问对象的成员变量</title>
		<link>https://www.phppan.com/2011/08/object-access-array/</link>
		<comments>https://www.phppan.com/2011/08/object-access-array/#comments</comments>
		<pubDate>Tue, 16 Aug 2011 00:56:08 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[ArrayAccess]]></category>
		<category><![CDATA[SPL]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1451</guid>
		<description><![CDATA[在Yii框架中我们可以直接以数组的方式访问对象的成员变量，查看其源码得这些类都实现了ArrayAccess接口 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">在Yii框架中我们可以直接以数组的方式访问对象的成员变量，查看其源码得这些类都实现了ArrayAccess接口。 如果你想让一个类的实例可以以数组的方式访问，实现ArrayAccess接口就可以了。如下<a style="color: #1299da; text-decoration: underline;" href="http://www.php.net/manual/en/class.arrayaccess.php">示例</a>：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #cc7833;">class</span> Foo implements ArrayAccess <span style="color: #ffffff;">{</span>

    <span style="color: #cc7833;">private</span> <span style="color: #6d9cbe;">$_container</span> <span style="color: #e0882f;">=</span> <a style="color: #1299da; text-decoration: none;" href="http://www.php.net/array"><span style="color: #e2392d;">array</span></a><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

    <span style="color: #cc7833;">public</span> <span style="color: #cc7833;">function</span> __construct<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
    <span style="color: #ffffff;">}</span>

    <span style="color: #cc7833;">public</span> <span style="color: #cc7833;">function</span> offsetSet<span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$offset</span><span style="color: #e0882f;">,</span> <span style="color: #6d9cbe;">$value</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span>_container<span style="color: #ffffff;">[</span><span style="color: #6d9cbe;">$offset</span><span style="color: #ffffff;">]</span> <span style="color: #e0882f;">=</span> <span style="color: #6d9cbe;">$value</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>

    <span style="color: #cc7833;">public</span> <span style="color: #cc7833;">function</span> offsetExists<span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$offset</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <span style="color: #ff8400;">return</span> <a style="color: #1299da; text-decoration: none;" href="http://www.php.net/isset"><span style="color: #e2392d;">isset</span></a><span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span>_container<span style="color: #ffffff;">[</span><span style="color: #6d9cbe;">$offset</span><span style="color: #ffffff;">]</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>

    <span style="color: #cc7833;">public</span> <span style="color: #cc7833;">function</span> offsetUnset<span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$offset</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <a style="color: #1299da; text-decoration: none;" href="http://www.php.net/unset"><span style="color: #e2392d;">unset</span></a><span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span>_container<span style="color: #ffffff;">[</span><span style="color: #6d9cbe;">$offset</span><span style="color: #ffffff;">]</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>

    <span style="color: #cc7833;">public</span> <span style="color: #cc7833;">function</span> offsetGet<span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$offset</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <span style="color: #ff8400;">return</span> <a style="color: #1299da; text-decoration: none;" href="http://www.php.net/isset"><span style="color: #e2392d;">isset</span></a><span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span>_container<span style="color: #ffffff;">[</span><span style="color: #6d9cbe;">$offset</span><span style="color: #ffffff;">]</span><span style="color: #ffffff;">)</span> ? <span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span>_container<span style="color: #ffffff;">[</span><span style="color: #6d9cbe;">$offset</span><span style="color: #ffffff;">]</span> <span style="color: #e0882f;">:</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>

<span style="color: #ffffff;">}</span>

<span style="color: #6d9cbe;">$foo</span> <span style="color: #e0882f;">=</span> <span style="color: #cc7833;">new</span> Foo<span style="color: #e0882f;">;</span>

<span style="color: #6d9cbe;">$foo</span><span style="color: #ffffff;">[</span><span style="color: #56db3a;">'test'</span><span style="color: #ffffff;">]</span> <span style="color: #e0882f;">=</span> <span style="color: #1299da;">100</span><span style="color: #e0882f;">;</span>
<a style="color: #1299da; text-decoration: none;" href="http://www.php.net/echo"><span style="color: #e2392d;">echo</span></a> <span style="color: #6d9cbe;">$foo</span><span style="color: #ffffff;">[</span><span style="color: #99ff00;">"test"</span><span style="color: #ffffff;">]</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">这是官网上的一个例子，修改了一些代码，非常简单，它实现了一个类，这个类实现了ArrayAccess接口。 从而我们可以以数组的方式访问或设置值。</p>
<p style="text-indent: 2em;">什么原因导致可以以这样的方式访问呢？难道仅仅是因为那个接口吗？ 归根结底应该是类中的我们约定好的方法，而这些方法中只是接口的形式表现出来了。 如果我们没有实现这个接口，而仅仅某个类拥有了这些方法呢？ 当程序执行时，程序会输出： Fatal error: Cannot use object of type Foo as array&#8230;</p>
<p style="text-indent: 2em;">这表示实现这个接口是必须的，如果实现这个接口，那么就一定需要实现这个接口定义的所有方法。 比如我们要以数组的方式读取一个成员变量，那在PHP内核中是如何实现的呢？ 通过表象看本质，这里是以数组的方式读取，那么其实现的位置应该还是在方括号符的实现位置。 以VLD查看其中间代码，我们可以得知数组读取变量的中间代码为：ZEND_FETCH_DIM_R 在此中间代码的执行过程中最终都会调用zend_fetch_dimension_address_read函数来读取值。 在这个函数中，它会依据不同容器类型做不同的处理，这些类型包括：数组，字符串、NULL、对象等。 虽然我们是以数组的方式调用对象的属性，但在放对象属性的窗口还是对象。因此，此处程序走的分支是对象， 在此分支中，对于对象，它会调用对象的read_dimension方法，默认情况下，Zend引擎的read_dimension方法默认实现是 zend_std_read_dimension函数（Zend/zend_object_handlers.c）。我们看zend_std_read_dimension函数的实现，如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">zval <span style="color: #e0882f;">*</span>zend_std_read_dimension<span style="color: #ffffff;">(</span>zval <span style="color: #e0882f;">*</span>object<span style="color: #e0882f;">,</span> zval <span style="color: #e0882f;">*</span>offset<span style="color: #e0882f;">,</span> <span style="color: #1299da;">int</span> type TSRMLS_DC<span style="color: #ffffff;">)</span> <span style="color: #bd48b3; font-style: italic;">/* {{{ */</span>
<span style="color: #ffffff;">{</span>
    zend_class_entry <span style="color: #e0882f;">*</span>ce <span style="color: #e0882f;">=</span> Z_OBJCE_P<span style="color: #ffffff;">(</span>object<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    zval <span style="color: #e0882f;">*</span>retval<span style="color: #e0882f;">;</span>

    <span style="color: #bd48b3; font-style: italic;">/* 判断是否为ArrayAccess的子类 */</span>
    <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>instanceof_function_ex<span style="color: #ffffff;">(</span>ce<span style="color: #e0882f;">,</span> zend_ce_arrayaccess<span style="color: #e0882f;">,</span> <span style="color: #1299da;">1</span> TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <span style="color: #ff8400;">if</span><span style="color: #ffffff;">(</span>offset <span style="color: #e0882f;">==</span> <span style="color: #cc7833;">NULL</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
            <span style="color: #bd48b3; font-style: italic;">/* [] construct */</span>
            ALLOC_INIT_ZVAL<span style="color: #ffffff;">(</span>offset<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        <span style="color: #ffffff;">}</span> <span style="color: #ff8400;">else</span> <span style="color: #ffffff;">{</span>
            SEPARATE_ARG_IF_REF<span style="color: #ffffff;">(</span>offset<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        <span style="color: #ffffff;">}</span>
        zend_call_method_with_1_params<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>object<span style="color: #e0882f;">,</span> ce<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"offsetget"</span><span style="color: #e0882f;">,</span> <span style="color: #e0882f;">&amp;</span>retval<span style="color: #e0882f;">,</span> offset<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

        zval_ptr_dtor<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>offset<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

        <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span><span style="color: #e0882f;">!</span>retval<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
            <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span><span style="color: #e0882f;">!</span>EG<span style="color: #ffffff;">(</span>exception<span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
                zend_error<span style="color: #ffffff;">(</span>E_ERROR<span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"Undefined offset for object of type %s used as array"</span><span style="color: #e0882f;">,</span> ce<span style="color: #e0882f;">-&gt;</span>name<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
            <span style="color: #ffffff;">}</span>
            <span style="color: #ff8400;">return</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>
        <span style="color: #ffffff;">}</span>

        <span style="color: #bd48b3; font-style: italic;">/* Undo PZVAL_LOCK() */</span>
        Z_DELREF_P<span style="color: #ffffff;">(</span>retval<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

        <span style="color: #ff8400;">return</span> retval<span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span> <span style="color: #ff8400;">else</span> <span style="color: #ffffff;">{</span>
        zend_error<span style="color: #ffffff;">(</span>E_ERROR<span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"Cannot use object of type %s as array"</span><span style="color: #e0882f;">,</span> ce<span style="color: #e0882f;">-&gt;</span>name<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        <span style="color: #ff8400;">return</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">从上面的代码我们可以看出：程序会先判断所给对象的类是否为ArrayAccess的子类，如果不是，则会显示错误，这在前面已经猜测，在此证实了。 如果是其子类，则调用offsetget方法获取值。</p>
<p style="text-indent: 2em;">虽然我们在使用SPL时会比较简单，但是如果要开发一个SPL有时可能就没这么简单了，特别是那些有语言特性的SPL功能（如ArrayAccess），则在实现时可能就需要调用相关语言实现的代码了， 从上面看SPL与语言结构产生了较为严重的耦合，如果这个SPL要去掉，则需要修改的地方不只一处，是否有其它方案？ SPL现在本来就是以扩展的形式存在于PHP中，以扩展的方式加载，却不能以扩展的方式卸载。优雅乎？</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/08/object-access-array/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
