<?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>Ars Longa, Vita Brevis &#187; магические методы</title>
	<atom:link href="http://blog.sjinks.pro/tag/magic-methods/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.sjinks.pro</link>
	<description>Quod scripsi, scripsi</description>
	<lastBuildDate>Thu, 19 Jan 2012 12:18:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Особенности магического метода __call в PHP</title>
		<link>http://blog.sjinks.pro/php/573-about-call-magic-method/</link>
		<comments>http://blog.sjinks.pro/php/573-about-call-magic-method/#comments</comments>
		<pubDate>Sun, 07 Jun 2009 17:38:42 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[магические методы]]></category>
		<category><![CDATA[ООП]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=573</guid>
		<description><![CDATA[Не всё работает так, как заявлено в документации Те, кто хорошо знают PHP5, наверняка знакомы или хотя бы раз использовали такой мощный инструмент, как магические методы. Один из методов, __call(), согласно документации используется при попытке вызова недоступного метода в контексте объекта. Иными словами, в следующем фрагменте кода &#60;?php class A { public function __call($method, $params) [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/573-about-call-magic-method/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Не всё работает так, как заявлено в документации</em></h2>
<p>Те, кто хорошо знают <a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a>5, наверняка знакомы или хотя бы раз использовали такой мощный инструмент, как <a href="http://php.net/manual/en/language.oop5.magic.php">магические методы</a>.</p>
<p>Один из методов, <span class="codebox"><code class="php">__call<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>, согласно документации используется при попытке вызова недоступного метода в контексте объекта.</p>
<p>Иными словами, в следующем фрагменте кода</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p5736">
        <div class="code php" id="p573code6">
<span class="kw2">&lt;?php</span><br />
&nbsp; &nbsp; <span class="kw2">class</span> A <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __call<span class="br0">&#40;</span><span class="re0">$method</span><span class="sy0">,</span> <span class="re0">$params</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">print</span> <span class="st0">&quot;Attempt to call <span class="es4">{$method}</span><span class="es1">\n</span>&quot;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="re0">$a</span> <span class="sy0">=</span> <span class="kw2">new</span> A<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="re0">$a</span><span class="sy0">-&gt;</span><span class="me1">someMethod</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>будет вызван магический метод <span class="codebox"><code class="php">A<span class="sy0">::</span>__call<span class="br0">&#40;</span><span class="st0">&quot;someMethod&quot;</span><span class="sy0">,</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></code></span>, который напечатает</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p5737">
        <div class="code text" id="p573code7">
Attempt to call someMethod
        </div>
    </div>
</div>

<p>С несуществующими методами всё ясно, но в документации упоминается слово «<strong>недоступные</strong>» (inaccessible).<span id="more-573"></span></p>
<p>А с недоступными методами, к сожалению, не всё так гладко. Рассмотрим пример:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p5738">
        <div class="code php" id="p573code8">
<span class="kw2">&lt;?php</span><br />
&nbsp; &nbsp; <span class="kw2">class</span> A <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> get<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="sy0">&amp;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="st_h">'test'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __call<span class="br0">&#40;</span><span class="re0">$method</span><span class="sy0">,</span> <span class="re0">$params</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw4">true</span> <span class="sy0">==</span> <span class="kw3">method_exists</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="re0">$method</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">call_user_func_array</span><span class="br0">&#40;</span><span class="kw1">array</span><span class="br0">&#40;</span><span class="sy0">&amp;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="re0">$method</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="re0">$params</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">protected</span> <span class="kw2">function</span> test<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">echo</span> <span class="st0">&quot;It makes me money and that's all right<span class="es1">\n</span>&quot;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="re0">$a</span> <span class="sy0">=</span> <span class="kw2">new</span> A<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="re0">$callback</span> <span class="sy0">=</span> <span class="re0">$a</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw3">call_user_func</span><span class="br0">&#40;</span><span class="re0">$callback</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>Метод <span class="codebox"><code class="php">A<span class="sy0">::</span><span class="me2">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> является доступным в контексте <strong>класса</strong> <span class="codebox"><code class="php">A</code></span>, но недоступным в контексте <strong>объекта</strong> <span class="codebox"><code class="php"><span class="re0">$a</span></code></span>. Следуя документации, ожидаемым результатом был бы вызов <span class="codebox"><code class="php">A<span class="sy0">::</span><span class="me2">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> из <span class="codebox"><code class="php">A<span class="sy0">::</span>__call<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>. Но в действительности всё не так, как на самом деле:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p5739">
        <div class="code text" id="p573code9">
Warning: call_user_func(A::test): First argument is expected to be a valid callback in test-visibility.php on line 23
        </div>
    </div>
</div>

<p>А метод <span class="codebox"><code class="php">__call<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> даже не вызывается.</p>
<p>В принципе, я не обратил бы на это внимание, если бы <a href="http://blog.sjinks.pro/tag/magic-methods/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  магические методы">магические методы</a> <span class="codebox"><code class="php">__get<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>/<span class="codebox"><code class="php">__set<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>/<span class="codebox"><code class="php">__isset<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>/<span class="codebox"><code class="php">__unset<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> работали точно так же. Но я знаю, что это не так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p57310">
        <div class="code php" id="p573code10">
<span class="kw2">&lt;?php</span><br />
&nbsp; &nbsp; <span class="kw2">class</span> A<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">private</span> <span class="re0">$x</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __get<span class="br0">&#40;</span><span class="re0">$name</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">print</span> <span class="re0">$name</span> <span class="sy0">.</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw4">null</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="re0">$a</span> <span class="sy0">=</span> <span class="kw2">new</span> A<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="re0">$a</span><span class="sy0">-&gt;</span><span class="me1">x</span><span class="sy0">;</span><br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>В этом случае, хотя <span class="codebox"><code class="php"><span class="re0">$a</span><span class="sy0">-&gt;</span><span class="me1">x</span></code></span> и недоступен в контексте объекта <span class="codebox"><code class="php"><span class="re0">$a</span></code></span>, метод <span class="codebox"><code class="php">A<span class="sy0">::</span>__get<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> <strong>будет</strong> вызван.</p>
<p>Я не знаю, считать ли политику (не)вызова <span class="codebox"><code class="php">__call<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> в PHP ошибкой или нет, ясно одно: подобное поведение не согласуется с политикой вызовов других магических методов.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/573-about-call-magic-method/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/573-about-call-magic-method/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

