<?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; PHP</title>
	<atom:link href="http://blog.sjinks.pro/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.sjinks.pro</link>
	<description>Quod scripsi, scripsi</description>
	<lastBuildDate>Sat, 19 May 2012 17:55:07 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Доступ к закрытым свойствам класса в PHP</title>
		<link>http://blog.sjinks.pro/php/970-access-private-class-properties/</link>
		<comments>http://blog.sjinks.pro/php/970-access-private-class-properties/#comments</comments>
		<pubDate>Mon, 14 May 2012 15:03:09 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[reflection]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=970</guid>
		<description><![CDATA[Крутили гайку номер шесть ключом на восемнадцать… Иногда встречаются ситуации, когда нужно расширить функциональность класса сторонней библиотеки; при этом нужный класс реализован так, что вместо наследования проще переписать, ибо функциональность завязана на закрытых (private) свойствах класса, для которых он не предоставляет акцессоров. Чтобы далеко не ходить за примером, можно посмотреть на реализацию работы с базой [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/970-access-private-class-properties/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Крутили гайку номер шесть ключом на восемнадцать…</em></h2>
<p>Иногда встречаются ситуации, когда нужно расширить функциональность класса сторонней библиотеки; при этом нужный класс реализован так, что вместо наследования проще переписать, ибо функциональность завязана на закрытых (private) свойствах класса, для которых он не предоставляет акцессоров.</p>
<p>Чтобы далеко не ходить за примером, можно посмотреть на реализацию работы с базой данных в <a href="http://blog.sjinks.pro/tag/yii/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Yii">Yii</a> — если требуется что-то нестандартное и не предусмотренное разработчиками — придётся извращаться. Например, если нужно добавить поддержку <span class="codebox"><code class="mysql"><span class="kw1">SELECT</span> … FOR <span class="kw1">UPDATE</span></code></span>/<span class="codebox"><code class="mysql"><span class="kw1">SELECT</span> … <span class="kw1">LOCK</span> <span class="kw2">IN</span> <span class="kw1">SHARE MODE</span></code></span> (кстати, весьма нужный довесок для высоконагруженных сайтов с активной работой с базой данных), кажется вполне логичным унаследоваться от класса <code>CDbCommand</code> и добавить свои методы для организации блокировки и переопределить метод <code>buildQuery</code>. В теории это выглядит хорошо, но на практике имеем, что вся функциональность завязана на закрытое свойство <code>CDbCommand::_q</code>, в котором хранятся все параметры запроса. Кроме того, для поддержки этого хозяйства в Active Record придётся унаследоваться от CActiveRecord, а там такая же петрушка.</p>
<p><em style="font-size: .8em">На правах лирического отступления: при всей моей любви к Yii, некоторые архитектурные решения выглядят весьма непродуманными и вызывают во мне любимый жест капитана Пикара: множественные косяки с закрытыми свойствами, проверки <span class="codebox"><code class="php"><span class="kw3">method_exists</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> идут вразрез с идеологией <code>CComponent</code> (например, использование mixin (behavior) как метода форматирования в класс, порождённый от <code>CFormatter</code>, закончится исключением), неудачное разбиение на методы, из-за которого наследование временами превращается в копипаст и т.д.</em></p>
<p><a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a> — это не C++, поэтому антипаттерн <a href="http://www.javenue.info/post/56">Паблик Морозов</a> (<span class="codebox"><code class="c"><span class="co2">#define private public</span></code></span>) здесь не пройдёт. К счастью, костылестроение в PHP поставлено на поток, поэтому решение всё же есть…<span id="more-970"></span></p>
<p>В PHP есть достаточно мощное <a href="http://php.net/manual/en/book.reflection.php">Reflection API</a> (хотя местами плохо документированное), позволяющее разработчику творить чудеса.</p>
<p>Например, есть у нас такой фрагмент класса:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p9704">
        <div class="code php" id="p970code4">
<span class="kw2">class</span> CDbCommand <span class="kw2">extends</span> CComponent<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">private</span> <span class="re0">$_query</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>API этого класса не предоставляет никакого доступа к закрытому свойству <code>$_query</code>, но, тем не менее, доступ к нему очень нужен, чтобы не переписывать большую часть исходного класса.</p>
<p>Для начала нужно создать отражение класса: <span class="codebox"><code class="php"><span class="re0">$reflected</span> <span class="sy0">=</span> <span class="kw2">new</span> ReflectionClass<span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#41;</span><span class="sy0">;</span></code></span><br />
Затем нужно переместиться в класс-предок: <span class="codebox"><code class="php"><span class="re0">$reflected</span> <span class="sy0">=</span> <span class="re0">$reflected</span><span class="sy0">=&gt;</span>getParentClass<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></code></span><br />
Потом получаем свойство <code>$_query</code>: <span class="codebox"><code class="php"><span class="re0">$p_query</span> <span class="sy0">=</span> <span class="re0">$reflected</span><span class="sy0">-&gt;</span><span class="me1">getProperty</span><span class="br0">&#40;</span><span class="st_h">'_query'</span><span class="br0">&#41;</span><span class="sy0">;</span></code></span><br />
После чего делаем его доступным для чтения/изменения <strong>через</strong> <a href="http://blog.sjinks.pro/tag/reflection/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  reflection">Reflection</a> API: <span class="codebox"><code class="php"><span class="re0">$p_query</span><span class="sy0">-&gt;</span><span class="me1">setAccessible</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span></code></span></p>
<p><strong>Чтение свойства:</strong> <span class="codebox"><code class="php"><span class="re0">$p_query</span><span class="sy0">-&gt;</span><span class="me1">getValue</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#41;</span><span class="sy0">;</span></code></span><br />
<strong>Изменение свойства:</strong> <span class="codebox"><code class="php"><span class="re0">$p_query</span><span class="sy0">-&gt;</span><span class="me1">setValue</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="re0">$new_value</span><span class="br0">&#41;</span><span class="sy0">;</span></code></span></p>
<p>Полный пример (с минимальным количеством кода):</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p9705">
        <div class="code php" id="p970code5">
<span class="kw2">class</span> DbCommand <span class="kw2">extends</span> CDbCommand<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">const</span> LOCK_SHARED &nbsp; &nbsp;<span class="sy0">=</span> <span class="st_h">'r'</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">const</span> LOCK_EXCLUSIVE <span class="sy0">=</span> <span class="st_h">'w'</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">private</span> <span class="re0">$p_query</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span>CDbConnection <span class="re0">$connection</span><span class="sy0">,</span> <span class="re0">$query</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span>__construct<span class="br0">&#40;</span><span class="re0">$connection</span><span class="sy0">,</span> <span class="re0">$query</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$reflected</span> &nbsp; &nbsp; <span class="sy0">=</span> <span class="kw2">new</span> ReflectionClass<span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$reflected</span> &nbsp; &nbsp; <span class="sy0">=</span> <span class="re0">$reflected</span><span class="sy0">-&gt;</span><span class="me1">getParentClass</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">p_query</span> <span class="sy0">=</span> <span class="re0">$reflected</span><span class="sy0">-&gt;</span><span class="me1">getProperty</span><span class="br0">&#40;</span><span class="st_h">'_query'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">p_query</span><span class="sy0">-&gt;</span><span class="me1">setAccessible</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> sharedLock<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$v</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">p_query</span><span class="sy0">-&gt;</span><span class="me1">getValue</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$v</span><span class="br0">&#91;</span><span class="st_h">'lock'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw2">self</span><span class="sy0">::</span><span class="me2">LOCK_SHARED</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">p_query</span><span class="sy0">-&gt;</span><span class="me1">setValue</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="re0">$v</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>При использовании Reflection API в конструкторе для получения информации о родительском классе следует помнить о значении <code>$this</code>: если у класса нет детей, то <code>$this</code> будет соответствовать данному классу; если же потомки есть и создаётся именно класс-потомок, то <code>$this</code> будет соответствовать именно тому потомку.</p>
<p>Решить это можно так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p9706">
        <div class="code php" id="p970code6">
<span class="re0">$reflected</span> <span class="sy0">=</span> <span class="kw2">new</span> ReflectionClass<span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="re0">$me</span> &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> strtlower<span class="br0">&#40;</span><span class="kw4">__CLASS__</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="kw1">while</span> <span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="re0">$reflected</span><span class="sy0">-&gt;</span><span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">!=</span> <span class="re0">$me</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$reflected</span> <span class="sy0">=</span> <span class="re0">$reflected</span><span class="sy0">-&gt;</span><span class="me1">getParentClass</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Безусловно, Reflection API в PHP — мощный инструмент, но, как и все подобные инструменты, применять его нужно осторожно и с умом</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/970-access-private-class-properties/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/970-access-private-class-properties/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQLMon для Yii</title>
		<link>http://blog.sjinks.pro/php/yii/947-sqlmon-for-yii/</link>
		<comments>http://blog.sjinks.pro/php/yii/947-sqlmon-for-yii/#comments</comments>
		<pubDate>Tue, 22 Nov 2011 15:02:18 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[Yii]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQLMon]]></category>
		<category><![CDATA[лог]]></category>
		<category><![CDATA[производительность]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=947</guid>
		<description><![CDATA[Расширение для анализа производительности SQL-запросов для Yii Одной из вещей, которых мне очень не хватало при разработке сайтов на Yii — нормального отображения всех запросов к базе данных, что дало бы возможность их последующего анализа. Ситуацию частично исправило расширение под названием Yii DB profiler. Но остались некоторые неудобства: Отображение запросов в порядке убывания времени выполнения — в принципе, это [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/yii/947-sqlmon-for-yii/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Расширение для анализа производительности SQL-запросов для <a href="http://blog.sjinks.pro/tag/yii/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Yii">Yii</a></em></h2>
<p>Одной из вещей, которых мне очень не хватало при разработке сайтов на Yii — нормального отображения всех запросов к базе данных, что дало бы возможность их последующего анализа.</p>
<p>Ситуацию частично исправило расширение под названием <a href="http://rmcreative.ru/blog/post/yii-db-profiler">Yii DB profiler</a>. Но остались некоторые неудобства:</p>
<ul>
<li>Отображение запросов в порядке убывания времени выполнения — в принципе, это дело вкуса: при таком порядке сразу видны проблемные запросы. С другой стороны, лично мне более привычен хронологический порядок — так чётче прослеживается логика работы;</li>
<li>Prepared statements. Это просто здорово, но если повторять запрос в phpMyAdmin (например, если интересует план выполнения запроса), бывает очень муторно заменять все связанные значения. Например, для запросов вида
          
<div class="codebox">
    <div class="the_code" style="" id="p94711">
        <div class="code mysql" id="p947code11">
<span class="kw1">SELECT</span> <span class="st0">'t'</span>.<span class="st0">&quot;object<span class="es1">_</span>id&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c0&quot;</span><span class="sy2">,</span> <span class="st0">'t'</span>.<span class="st0">&quot;ymd&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c1&quot;</span><span class="sy2">,</span> <span class="st0">'t'</span>.<span class="st0">&quot;black&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c2&quot;</span><span class="sy2">,</span> <span class="st0">'t'</span>.<span class="st0">&quot;brown&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c3&quot;</span><span class="sy2">,</span> <span class="st0">'t'</span>.<span class="st0">&quot;yellow&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c4&quot;</span><span class="sy2">,</span> <br />
<span class="st0">'t'</span>.<span class="st0">&quot;neutral&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c5&quot;</span><span class="sy2">,</span> <span class="st0">'t'</span>.<span class="st0">&quot;white&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c6&quot;</span><span class="sy2">,</span> <span class="st0">'t'</span>.<span class="st0">&quot;unknown&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c7&quot;</span><span class="sy2">,</span> <span class="st0">'t'</span>.<span class="st0">&quot;error&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t0<span class="es1">_</span>c8&quot;</span><span class="sy2">,</span> <span class="st0">'object'</span>.<span class="st0">&quot;object&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t1<span class="es1">_</span>c2&quot;</span><span class="sy2">,</span><br />
<span class="st0">'object'</span>.<span class="st0">&quot;id&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;t1<span class="es1">_</span>c0&quot;</span><br />
<span class="kw1">FROM</span> <span class="st0">'dnsbl<span class="es1">_</span>summary'</span> <span class="st0">'t'</span> <br />
<span class="kw13">LEFT</span> <span class="kw1">OUTER</span> <span class="kw1">JOIN</span> <span class="st0">'objects'</span> <span class="st0">'object'</span> <span class="kw1">ON</span> <span class="br0">&#40;</span><span class="st0">'t'</span>.<span class="st0">&quot;object<span class="es1">_</span>id&quot;</span><span class="sy1">=</span><span class="st0">'object'</span>.<span class="st0">&quot;id&quot;</span><span class="br0">&#41;</span> <br />
<span class="kw1">WHERE</span> <span class="br0">&#40;</span><span class="br0">&#40;</span><span class="br0">&#40;</span>black <span class="sy1">&gt;</span> 0<span class="br0">&#41;</span> <span class="kw10">OR</span> <span class="br0">&#40;</span>brown <span class="sy1">&gt;</span> 0<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <br />
<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="br0">&#40;</span><span class="br0">&#40;</span><span class="br0">&#40;</span><span class="br0">&#40;</span><span class="br0">&#40;</span><span class="br0">&#40;</span><span class="br0">&#40;</span>ymd<span class="sy1">=</span>:ycp0<span class="br0">&#41;</span> <span class="kw10">AND</span> <span class="br0">&#40;</span>black<span class="sy1">&gt;</span>:ycp1<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <span class="br0">&#40;</span>brown<span class="sy1">=</span>:ycp2<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <span class="br0">&#40;</span>yellow<span class="sy1">=</span>:ycp3<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <br />
<span class="br0">&#40;</span>neutral<span class="sy1">&gt;</span>:ycp4<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <span class="br0">&#40;</span>white<span class="sy1">&gt;</span>:ycp5<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <span class="br0">&#40;</span>unknown<span class="sy1">=</span>:ycp6<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <span class="br0">&#40;</span>black<span class="sy1">&gt;</span>:ycp7<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <br />
<span class="br0">&#40;</span>error<span class="sy1">=</span>:ycp8<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw10">AND</span> <span class="br0">&#40;</span>object.enabled <span class="sy1">=</span> 1<span class="br0">&#41;</span><br />
<span class="kw1">LIMIT</span> <span class="nu0">50</span>
        </div>
    </div>
</div>

заменять все <code>:ycpXXX</code> на их значения немного муторно. В общем случае здесь вряд ли можно что-то сделать — заполнители параметров могут быть любыми (и даже позиционными), поэтому тупое использование <code>str_replace</code> может наделать делов.
</li>
</ul>
<p>Лично мне список запросов нужен обычно только для двух вещей:</p>
<ol>
<li>Оценка работы механизмов кэширования;</li>
<li>Оценка плана выполнения запроса, составленная оптимизатором.</li>
</ol>
<p>Первое обычно не критично (зачастую достаточно посмотреть на количество запросов), а вот второе позволяет выявить многие будущие проблемы с производительностью заранее.</p>
<p>В результате, взяв за основу плагин <a href="http://rmcreative.ru/">Александра</a>, я портировал <a href="http://blog.sjinks.pro/tag/sqlmon/">SQLMon</a> на Yii.<br />
<span id="more-947"></span></p>
<p>В результате получилось расширение, позволяющее получать планы выполнения запросов в реальном времени.</p>
<p>Поддерживаются <a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a> и SQLite, в планах поддержка PgSQL (там не всё просто, ибо Postgres <em>выполняет</em> запрос, что может привести к интересным последствиям при использовании <code>INSERT</code>/<code>UPDATE</code>/<code>DELETE</code>; как следствие, придётся использовать <a href="http://blog.sjinks.pro/php/932-pdo-nested-transactions/">вложенные транзакции</a>).</p>
<p>Вывод результатов осуществляется либо при помощи виджета:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p94712">
        <div class="code php" id="p947code12">
<span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">widget</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; <span class="st_h">'ext.sqlmon.components.SQLMon'</span><br />
&nbsp; &nbsp; <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'explain'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать план выполнения запроса</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'backtrace'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать трассу вызовов</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'dsn'</span> &nbsp; &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать DSN соединения (может быть полезно при использовании нескольких БД)</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'cssFile'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">null</span><span class="sy0">,</span> <span class="co1">// Эстеты могут задать свой файл стилей </span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><br />
<span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Либо при помощи Log Route:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p94713">
        <div class="code php" id="p947code13">
<span class="kw1">return</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; <span class="st_h">'components'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'log'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'routes'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'class'</span> <span class="sy0">=&gt;</span> <span class="st_h">'ext.sqlmon.components.SQLMonLogRoute'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'showExplain'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать план выполнения</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'showDsn'</span> &nbsp; &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать DSN соединения</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'showBacktrace'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать трассу вызовов</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'class'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'ext.sqlmon.components.SQLMonFileLogRoute'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'logFile'</span> <span class="sy0">=&gt;</span> <span class="st_h">'sqlmon.log'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'showExplain'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать план выполнения</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'showDsn'</span> &nbsp; &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать DSN соединения</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'showBacktrace'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="co1">// Показывать трассу вызовов</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
<span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p><code>SQLMonLogRoute</code> наследуется от <code>CWebLogRoute</code>, но, как и <code>CProfileLogRoute</code>, отказывается выдавать данные для AJAX-запросов.</p>
<p><code>SQLMonFileLogRoute</code> наследуется от <code>CFileLogRoute</code> и, как следствие, воспринимает все параметры конфигурации последнего.</p>
<p>Для того, чтобы протоколирование нормально работало, сообщения от <a href="http://blog.sjinks.pro/tag/sqlmon/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  SQLMon">SQLMon</a> попадают в общий протокол и, как следствие, видны при использовании <code>CWebLogRoute</code> (в этом случае выдаётся минимум информации).</p>
<p>Результаты работы выглядят примерно так (пример приведён для SQLite):</p>
<p><a href="http://static.sjinks.info/wp-content/uploads/2011/11/sqlmon-yii.png"><img src="http://static.sjinks.info/wp-content/uploads/2011/11/sqlmon-yii-300x283.png" alt="Пример выдачи SQLMon" title="SQLMon в Yii" width="300" height="283" class="alignnone size-medium wp-image-948" /></a></p>
<p>Реализовано всё через собственный класс <a href="http://blog.sjinks.pro/tag/pdo/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PDO">PDO</a>, поэтому для использования SQLMon нужно в файл конфигурации добавить такие строки:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p94714">
        <div class="code php" id="p947code14">
<br />
<span class="kw1">return</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; <span class="st_h">'import'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'ext.sqlmon.components.*'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
<br />
&nbsp; &nbsp; <span class="st_h">'components'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'db'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
<span class="co1">// ...</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'pdoClass'</span> <span class="sy0">=&gt;</span> <span class="st_h">'SQLMonPDO'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
<span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Реализация просмотра выполнения плана выполнения запроса осуществляется при помощи класса-наследника от <code>PDOStatement</code> и переопределения метода <code>PDO::prepare()</code>.</p>
<p>Планы на будущее:</p>
<ul>
<li>поддержка <a href="http://blog.sjinks.pro/tag/postgresql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PostgreSQL">PostgreSQL</a>;</li>
<li>возможность расширения классов <code>SQLMonPDO</code> и <code>SQLMonPDOStatement</code> (возможно, придётся позаимствовать некоторые идеи из <a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a>);</li>
<li>наследование <code>SQLMon_Explainer_Base</code> от <code>CComponent</code>, что даст дополнительные плюшки для расширяемости;</li>
<li>более красивая реализация функции SQLMon::arr2pre(), что позволит рисовать красивые ASCII-таблички;</li>
<li>возможная поддержка FireBug;</li>
<li>протоколирование в HTML-файл;</li>
<li>информация профайлера MySQL (нужность пока под большим вопросом);</li>
<li>рефакторинг, написание документации</li>
</ul>
<p><strong><a href='http://static.sjinks.info/wp-content/uploads/2011/11/sqlmon-0.1.zip'>SQLMon 0.1 for Yii</a></strong> — скачать бесплатно без регистрации без смс <img src='http://static.sjinks.info/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/yii/947-sqlmon-for-yii/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/yii/947-sqlmon-for-yii/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Преобразование ошибок в PHP в исключения</title>
		<link>http://blog.sjinks.pro/php/937-convert-php-errors-into-exceptions/</link>
		<comments>http://blog.sjinks.pro/php/937-convert-php-errors-into-exceptions/#comments</comments>
		<pubDate>Wed, 17 Aug 2011 21:13:06 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=937</guid>
		<description><![CDATA[Механизм трансляции ошибок в исключения с целью последующей обработки Навеяно этим сообщением на форуме. Дано: фреймворк перехватывает все возникающие ошибки (которые разрешены текущим значением error_reporting()) и аварийно завершает работу скрипта (в целях отладки/безопасности/нужное подчеркнуть); имеется код, вызывающий PHP-функцию, которая может сгенерировать предупреждение (в оригинальном сообщении это mkdir() — кидает E_WARNING, если не удаётся создать каталог) местные стандарты [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/937-convert-php-errors-into-exceptions/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Механизм трансляции ошибок в исключения с целью последующей обработки</em></h2>
<p>Навеяно <a href="http://www.yiiframework.com/forum/index.php?/topic/8310-%D0%BE%D1%82%D0%BB%D0%BE%D0%B2-php-%D0%BE%D1%88%D0%B8%D0%B1%D0%BE%D0%BA">этим сообщением</a> на форуме.</p>
<p>Дано:</p>
<ul>
<li>фреймворк перехватывает все возникающие ошибки (которые разрешены текущим значением <code>error_reporting()</code>) и аварийно завершает работу скрипта (в целях отладки/безопасности/нужное подчеркнуть);</li>
<li>имеется код, вызывающий <a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a>-функцию, которая может сгенерировать предупреждение (в оригинальном сообщении это <code>mkdir()</code> — кидает <code>E_WARNING</code>, если не удаётся создать каталог)</li>
<li>местные стандарты кодирования запрещают использование оператора <code>@</code> (довольно распространённая практика).</li>
</ul>
<p>Требуется обработать возникшую ошибку без <acronym title="аварийный останов">авоста</acronym> скрипта.<span id="more-937"></span></p>
<p>Сначала рассмотрим предложенные варианты решений и наведём критику (как же без этого).</p>
<p><strong>Способ 1</strong>:</p>
<blockquote>В PHP существуют специальные функции (is_dir, is_writable, …), в зависимости от возвращаемого значения которых вы можете генерировать соответствующее сообщение.</blockquote>
<p>Идея в том, чтобы проверять все возможные ошибочные ситуации до вызова проблемной функции. С одной стороны по-хорошему так и надо поступать. С другой стороны, всего не предусмотришь — а если создавать обёртку для каждой проблемной функции, код может очень сильно раздуться. К тому же следует помнить, что проверка на возможные ошибки с последующим вызовом функции — операция неатомарная. Иными словами, требуемый каталог может быть создан другим скриптом в следующее мгновение после того, как проверка на существование создаваемого каталога вернула отрицательный результат. Для каталогов это, возможно, не очень актуально, но для операция с сокетами — самое то.</p>
<p><strong>Способ 2</strong>: изменение <code>error_reporting</code>.</p>
<p>Что-то типа:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93722">
        <div class="code php" id="p937code22">
<span class="re0">$level</span><span class="sy0">=</span><span class="kw3">error_reporting</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="kw3">error_reporting</span><span class="br0">&#40;</span><span class="re0">$level</span> <span class="sy0">|</span> ~<span class="kw4">E_WARNING</span> <span class="sy0">|</span> ~<span class="kw4">E_ERROR</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
<span class="kw1">if</span><span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">is_dir</span><span class="br0">&#40;</span><span class="re0">$path</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="kw4">false</span><span class="sy0">===</span><span class="kw3">mkdir</span><span class="br0">&#40;</span><span class="re0">$path</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp;throw <span class="kw2">new</span> CException<span class="br0">&#40;</span>Yii<span class="sy0">::</span><span class="me2">t</span><span class="br0">&#40;</span><span class="st_h">'myapp'</span><span class="sy0">,</span><span class="st_h">'Can not create path &quot;{path}&quot;'</span><span class="sy0">,</span><span class="kw1">array</span><span class="br0">&#40;</span><span class="st_h">'{path}'</span><span class="sy0">=&gt;</span><span class="re0">$path</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
error_reporing<span class="br0">&#40;</span><span class="re0">$level</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Убирать <code>E_ERROR</code> всё же не стоит.<br />
Опять же, создание обёртки для каждой проблемной функции раздует код. К тому же вызов <code>error_reporting()</code> — не самая быстрая операция (по крайней мере раньше было именно так).</p>
<p><strong>Способ 3</strong>:</p>
<blockquote>
Мы в текущем проекте столкнулись с приблизительно такой же проблемой: Memcache генерит PHP-ошибки, а не исключения, которые хотя бы через try/catch можно было отловить.
<p>Пришлось изобретать свой хэндлер и &#8220;оборачивать&#8221; все обращения к мемкешу предвалительно нашим &#8220;молчаливым&#8221; хэндлером:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93723">
        <div class="code php" id="p937code23">
<span class="kw2">function</span> mcSilenceErrorHandler<span class="br0">&#40;</span><span class="re0">$errno</span><span class="sy0">,</span> <span class="re0">$errstr</span><span class="sy0">,</span> <span class="re0">$errfile</span><span class="sy0">,</span> <span class="re0">$errline</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; QLogger<span class="sy0">::</span><span class="kw3">log</span><span class="br0">&#40;</span><span class="st_h">'memcache_errors'</span><span class="sy0">,</span> <span class="re0">$errstr</span> <span class="sy0">.</span> <span class="st_h">' at ('</span> <span class="sy0">.</span> <span class="re0">$errfile</span> <span class="sy0">.</span> <span class="st_h">':'</span> <span class="sy0">.</span> <span class="re0">$errline</span> <span class="sy0">.</span> <span class="st_h">')'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw4">true</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>В самом коде используется так, что перед потенциально &#8220;опасными&#8221; обращениями к мемкешу, мы выставляем свой &#8220;молчаливый&#8221; хэндлер, а потом возвращаем старый &#8211; <a href="http://blog.sjinks.pro/tag/yii/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Yii">Yii</a>&#8217;шный &#8211; хэндлер.</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93724">
        <div class="code php" id="p937code24">
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> <span class="kw3">flush</span><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; &nbsp; &nbsp; <span class="re0">$result</span> <span class="sy0">=</span> <span class="kw4">false</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$old_handler</span> <span class="sy0">=</span> <span class="kw3">set_error_handler</span><span class="br0">&#40;</span><span class="st_h">'mcSilenceErrorHandler'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$result</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_cache<span class="sy0">-&gt;</span><span class="me1">flush</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">set_error_handler</span><span class="br0">&#40;</span><span class="re0">$old_handler</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$result</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
        </div>
    </div>
</div>

</blockquote>
<p>Решение à la Способ №2, обладающее практически теми же недостатками.</p>
<p>Ну а теперь собственно предлагаемое решение.</p>
<p><a href="http://blog.sjinks.pro/php/yii/934-catch-fatal-errors/">Как известно</a>, PHP позволяет пользователю установить собственные обработчики для обработки ошибок и необработанных исключительных ситуаций. Решение же состоит в том, чтобы из обработчика ошибок кинуть исключение. Исключение либо будет перехвачено в блоке <code>try</code>/<code>catch</code> где-то в коде, либо не будет обработано пользовательским кодом и будет перехвачено в обработчике необработанных исключений (что приведёт к аварийному завершению скрипта).</p>
<p>Код будет выглядеть примерно так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93725">
        <div class="code php" id="p937code25">
<span class="kw2">function</span> my_error_handler<span class="br0">&#40;</span><span class="re0">$code</span><span class="sy0">,</span> <span class="re0">$error</span><span class="sy0">,</span> <span class="re0">$file</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="sy0">,</span> <span class="re0">$line</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">error_reporting</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">&amp;</span> <span class="re0">$code</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; throw <span class="kw2">new</span> ErrorException<span class="br0">&#40;</span><span class="re0">$error</span><span class="sy0">,</span> <span class="re0">$code</span><span class="sy0">,</span> 0<span class="sy0">,</span> <span class="re0">$file</span><span class="sy0">,</span> <span class="re0">$line</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw4">true</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw2">function</span> my_shutdown_handler<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span><span class="re0">$error</span> <span class="sy0">=</span> <span class="kw3">error_get_last</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="kw3">in_array</span><span class="br0">&#40;</span><span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'type'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="kw4">E_PARSE</span><span class="sy0">,</span> <span class="kw4">E_ERROR</span><span class="sy0">,</span> <span class="kw4">E_USER_ERROR</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; my_display_error<span class="br0">&#40;</span><span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'type'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'message'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'file'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'line'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">die</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw2">function</span> my_display_error<span class="br0">&#40;</span><span class="re0">$code</span><span class="sy0">,</span> <span class="re0">$error</span><span class="sy0">,</span> <span class="re0">$file</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="sy0">,</span> <span class="re0">$line</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
<span class="co1">// Возникла фатальная ошибка, которую обработать штатными средствами нельзя.</span><br />
<span class="co1">// Вывод информации об ошибке и завершение работы скрипта</span><br />
<span class="br0">&#125;</span><br />
<br />
<br />
<span class="co1">// Выполняется где-то во время инициализации приложения</span><br />
<span class="kw3">set_error_handler</span><span class="br0">&#40;</span><span class="st_h">'my_error_handler'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="kw3">register_shutdown_function</span><span class="br0">&#40;</span><span class="st_h">'my_shutdown_handler'</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>После такого можно писать:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93726">
        <div class="code php" id="p937code26">
try <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw3">mkdir</span><span class="br0">&#40;</span><span class="re0">$dir</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
catch <span class="br0">&#40;</span>ErrorException <span class="re0">$e</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">echo</span> <span class="st_h">'Всё плохо при создании каталога: '</span><span class="sy0">,</span> <span class="re0">$e</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Так как тема взята с форума Yii, рассмотрим, как интегрировать всё это безобразие с Yii:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93727">
        <div class="code php" id="p937code27">
<span class="kw2">&lt;?php</span><br />
<span class="kw2">class</span> MyWebApplication <span class="kw2">extends</span> CWebApplication<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="re0">$config</span><span class="sy0">=</span><span class="kw4">null</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span>__construct<span class="br0">&#40;</span><span class="re0">$config</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">register_shutdown_function</span><span class="br0">&#40;</span><span class="kw1">array</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="st_h">'shutdown'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>YII_ENABLE_EXCEPTION_HANDLER <span class="sy0">&amp;&amp;</span> <span class="sy0">!</span>YII_ENABLE_ERROR_HANDLER<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">set_error_handler</span><span class="br0">&#40;</span><span class="kw1">array</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="st_h">'myErrorHandler'</span><span class="br0">&#41;</span><span class="br0">&#41;</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="kw2">public</span> <span class="kw2">function</span> shutdown<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>YII_ENABLE_EXCEPTION_HANDLER <span class="sy0">&amp;&amp;</span> <span class="br0">&#40;</span><span class="re0">$error</span> <span class="sy0">=</span> <span class="kw3">error_get_last</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="kw3">in_array</span><span class="br0">&#40;</span><span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'type'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="kw4">E_PARSE</span><span class="sy0">,</span> <span class="kw4">E_ERROR</span><span class="sy0">,</span> <span class="kw4">E_USER_ERROR</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">handleException</span><span class="br0">&#40;</span><span class="kw2">new</span> ErrorException<span class="br0">&#40;</span><span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'message'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'type'</span><span class="br0">&#93;</span><span class="sy0">,</span> 0<span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'file'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'line'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">die</span><span class="br0">&#40;</span><span class="br0">&#41;</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="kw2">public</span> <span class="kw2">function</span> myErrorHandler<span class="br0">&#40;</span><span class="re0">$code</span><span class="sy0">,</span> <span class="re0">$error</span><span class="sy0">,</span> <span class="re0">$file</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="sy0">,</span> <span class="re0">$line</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">error_reporting</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">&amp;</span> <span class="re0">$code</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw <span class="kw2">new</span> ErrorException<span class="br0">&#40;</span><span class="re0">$error</span><span class="sy0">,</span> <span class="re0">$code</span><span class="sy0">,</span> 0<span class="sy0">,</span> <span class="re0">$file</span><span class="sy0">,</span> <span class="re0">$line</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="kw1">return</span> <span class="kw4">true</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Использование:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93728">
        <div class="code php" id="p937code28">
<span class="kw1">define</span><span class="br0">&#40;</span><span class="st_h">'YII_ENABLE_ERROR_HANDLER'</span><span class="sy0">,</span> <span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="kw1">define</span><span class="br0">&#40;</span><span class="st_h">'YII_ENABLE_EXCEPTION_HANDLER'</span><span class="sy0">,</span> <span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
<span class="kw1">require_once</span> <span class="st_h">'protected/components/MyWebApplication.php'</span><span class="sy0">;</span><br />
Yii<span class="sy0">::</span><span class="me2">createApplication</span><span class="br0">&#40;</span><span class="st_h">'MyWebApplication'</span><span class="sy0">,</span> <span class="re0">$config</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">run</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/937-convert-php-errors-into-exceptions/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/937-convert-php-errors-into-exceptions/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Перехват фатальных ошибок в Yii</title>
		<link>http://blog.sjinks.pro/php/yii/934-catch-fatal-errors/</link>
		<comments>http://blog.sjinks.pro/php/yii/934-catch-fatal-errors/#comments</comments>
		<pubDate>Sun, 07 Aug 2011 08:06:17 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[Yii]]></category>
		<category><![CDATA[Kohana]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=934</guid>
		<description><![CDATA[Облегчаем жизнь разработчику Как и Kohana, Yii помогает разработчику, отлавливая неперехваченные исключения и ошибки в коде. Как и в Kohana, реализуется это одинаково — через функции PHP set_exception_handler() и set_error_handler(). Но, в отличие от Kohana, Yii не умеет перехватывать фатальные ошибки (E_ERROR в терминах PHP). Как следствие, если приложение сгенерирует фатальную ошибку (например, вызов несуществующей функции), у [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/yii/934-catch-fatal-errors/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Облегчаем жизнь разработчику</em></h2>
<p>Как и <a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a>, <a href="http://blog.sjinks.pro/tag/yii/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Yii">Yii</a> помогает разработчику, отлавливая неперехваченные исключения и ошибки в коде. Как и в <a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a>, реализуется это одинаково — через функции <a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a> <code><a href="http://www.php.net/manual/en/function.set-exception-handler.php">set_exception_handler()</a></code> и <code><a href="http://www.php.net/manual/en/function.set-error-handler.php">set_error_handler()</a></code>. Но, в отличие от Kohana, Yii не умеет перехватывать фатальные ошибки (<code>E_ERROR</code> в терминах PHP).</p>
<p>Как следствие, если приложение сгенерирует фатальную ошибку (например, вызов несуществующей функции), у разработчика все шансы об этом не узнать (например, на production-сервере с <code>display_errors</code> установленным <code>Off</code>). Конечно, если приложение хорошо протестировано шансы возникновения такой ситуации близки к нулю, но если приложение позволяет пользователю устанавливать какие-либо свои дополнения, то лучше узнавать о проблемах прежде, чем начальник получит кучу гневных писем <img src='http://static.sjinks.info/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> <span id="more-934"></span></p>
<p>Причина, по которой Yii не может обработать фатальные ошибки заключается в том, что PHP принципиально не вызывает функции, установленные <code>set_error_handler()</code>, при возникновении ошибок типа <code>E_ERROR</code>, <code>E_PARSE</code>, <code>E_CORE_ERROR</code>, <code>E_CORE_WARNING</code>, <code>E_COMPILE_ERROR</code>, <code>E_COMPILE_WARNING</code>.</p>
<p>Тем не менее, Kohana умеет перехватывать <code>E_ERROR</code>, поэтому было бы неплохо добавить такую поддержку и в Yii.</p>
<p>Вся магия заключается в том, что функции, зарегистрированные при помощи <code>register_shutdown_function()</code> вызываются даже в случае <code>E_ERROR</code>. А определить наличие ошибки можно при помощи функции <code><a href="http://www.php.net/manual/en/function.error-get-last.php">error_get_last()</a></code>.</p>
<p>У меня получился такой код:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93431">
        <div class="code php" id="p934code31">
<span class="kw2">&lt;?php</span><br />
<br />
<span class="kw2">class</span> MyWebApplication <span class="kw2">extends</span> CWebApplication<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="re0">$config</span><span class="sy0">=</span><span class="kw4">null</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span>__construct<span class="br0">&#40;</span><span class="re0">$config</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">register_shutdown_function</span><span class="br0">&#40;</span><span class="kw1">array</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="st_h">'shutdown'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> shutdown<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>YII_ENABLE_ERROR_HANDLER <span class="sy0">&amp;&amp;</span> <span class="br0">&#40;</span><span class="re0">$error</span> <span class="sy0">=</span> <span class="kw3">error_get_last</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">handleError</span><span class="br0">&#40;</span><span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'type'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'message'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'file'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re0">$error</span><span class="br0">&#91;</span><span class="st_h">'line'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">die</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Пользоваться так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93432">
        <div class="code php" id="p934code32">
<span class="kw1">require_once</span> <span class="st_h">'protected/components/MyWebApplication.php'</span><span class="sy0">;</span><br />
Yii<span class="sy0">::</span><span class="me2">createApplication</span><span class="br0">&#40;</span><span class="st_h">'MyWebApplication'</span><span class="sy0">,</span> <span class="re0">$config</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">run</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/yii/934-catch-fatal-errors/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/yii/934-catch-fatal-errors/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Вложенные транзакции в PDO</title>
		<link>http://blog.sjinks.pro/php/932-pdo-nested-transactions/</link>
		<comments>http://blog.sjinks.pro/php/932-pdo-nested-transactions/#comments</comments>
		<pubDate>Sat, 06 Aug 2011 07:21:34 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PDO]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=932</guid>
		<description><![CDATA[Использование вложенных транзакций в MySQL и PostgreSQL с PDO В PDO есть такая особенность: отсутствие поддержки вложенных транзакций. Вложенные транзакции бывают весьма полезны: например, у нас есть два сервиса, которые могут взаимодействовать между собой. Сервис А начинает транзакцию, что-то делает, затем вызывает сервис Б, затем подтверждает транзакцию. Может так случиться, что сервисы А и Б разрабатываются разными людьми и разработчик [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/932-pdo-nested-transactions/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Использование вложенных транзакций в <a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a> и <a href="http://blog.sjinks.pro/tag/postgresql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PostgreSQL">PostgreSQL</a> с <a href="http://blog.sjinks.pro/tag/pdo/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PDO">PDO</a></em></h2>
<p>В PDO есть такая особенность: отсутствие поддержки вложенных транзакций.</p>
<p>Вложенные транзакции бывают весьма полезны: например, у нас есть два сервиса, которые могут взаимодействовать между собой. Сервис А начинает транзакцию, что-то делает, затем вызывает сервис Б, затем подтверждает транзакцию.</p>
<p>Может так случиться, что сервисы А и Б разрабатываются разными людьми и разработчик сервиса Б может не знать, что сервис А уже начал выполнение транзакции. Как следствие, при использовании PDO как только сервис Б попытается начать транзакцию, PDO выбросит исключение, предупреждающее о наличии активной транзакции.</p>
<p>Тем не менее, такие СУБД как MySQL, PostgreSQL и SQLite поддерживают вложенные транзакции (о чём PDO не знает). Поэтому было бы неплохо добавить поддержку вложенных транзакций для данных СУБД в PDO.<span id="more-932"></span></p>
<p>Ниже приведена очень простая реализация поддержки вложенных транзакций в PDO:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93234">
        <div class="code php" id="p932code34">
<span class="kw2">class</span> MyPDO <span class="kw2">extends</span> PDO<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">protected</span> <span class="kw2">static</span> <span class="re0">$_supported_drivers</span> <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'mysql'</span> &nbsp;<span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'mysqli'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'pgsql'</span> &nbsp;<span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'sqlite'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">protected</span> <span class="re0">$_nest_level</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">protected</span> <span class="kw2">function</span> nestedTransactionsSupported<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="sy0">!</span><span class="kw1">empty</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="sy0">::</span><span class="re0">$_supported_drivers</span><span class="br0">&#91;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getAttribute</span><span class="br0">&#40;</span>PDO<span class="sy0">::</span><span class="me2">ATTR_DRIVER_NAME</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> beginTransaction<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level <span class="sy0">||</span> <span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">nestedTransactionsSupported</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span><span class="me2">beginTransaction</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">exec</span><span class="br0">&#40;</span><span class="st0">&quot;SAVEPOINT LEVEL<span class="es4">{$this-&gt;_nest_level}</span>&quot;</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="sy0">++</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> commit<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">--</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level <span class="sy0">||</span> <span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">nestedTransactionsSupported</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span><span class="me2">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">exec</span><span class="br0">&#40;</span><span class="st0">&quot;RELEASE SAVEPOINT LEVEL<span class="es4">{$this-&gt;_nest_level}</span>&quot;</span><span class="br0">&#41;</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="kw2">public</span> <span class="kw2">function</span> rollBack<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">--</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level <span class="sy0">||</span> <span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">nestedTransactionsSupported</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span><span class="me2">rollBack</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">exec</span><span class="br0">&#40;</span><span class="st0">&quot;ROLLBACK TO SAVEPOINT LEVEL<span class="es4">{$this-&gt;_nest_level}</span>&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p><strong>Подводные камни:</strong> в MySQL есть такое понятие как <dfn>неявное подтверждение транзакции</dfn> (<a href="http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html">implicit commit</a>). На практике это ломает уровень вложенности транзакций, поддерживаемый данной реализацией. С другой стороны, в web-приложениях редко возникает необходимость использования транзакций (тем более вложенных) там, где присутствуют операторы, вызывающие неявное подтверждение транзакции.</p>
<p><strong>UPDATE May 19, 2012:</strong> добавил поддержку SQLite.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/932-pdo-nested-transactions/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/932-pdo-nested-transactions/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Автоматическая проверка репутации IP-адреса</title>
		<link>http://blog.sjinks.pro/php/886-automatic-ip-reputation-check/</link>
		<comments>http://blog.sjinks.pro/php/886-automatic-ip-reputation-check/#comments</comments>
		<pubDate>Thu, 03 Feb 2011 02:51:44 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[IP]]></category>
		<category><![CDATA[IP Reputation]]></category>
		<category><![CDATA[XPath]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=886</guid>
		<description><![CDATA[На примере SendMail, Reputation Authority, SenderBase и SenderScore Репутация IP-адреса — одна из очень важных составляющих, используемых для борьбы с почтовым спамом. Письма приходят с IP-адресов, а репутация этих адресов может сказать, является ли данный адрес ответственным за рассылку спама или нет. По некоторым данным проверка репутации IP-адресов позволяет остановить порядка 80% спама. Как следствие, компании, занимающиеся [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/886-automatic-ip-reputation-check/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>На примере SendMail, Reputation Authority, SenderBase и SenderScore</em></h2>
<p>Репутация <a href="http://blog.sjinks.pro/tag/ip/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  IP">IP</a>-адреса — одна из очень важных составляющих, используемых для борьбы с почтовым спамом.</p>
<p>Письма приходят с IP-адресов, а репутация этих адресов может сказать, является ли данный адрес ответственным за рассылку спама или нет. По некоторым данным проверка репутации IP-адресов позволяет остановить порядка 80% спама.</p>
<p>Как следствие, компании, занимающиеся массовой рассылкой электронных писем, должны заботиться о поддержании хорошей репутации адресов своих почтовых серверов.<span id="more-886"></span></p>
<p>Очень часто для отправки писем используется множество адресов — в частности из-за того, что крупные почтовые сервисы (GMail, Yahoo!, Hotmail, AOL, ….) имеют различные ограничения на количество сообщений, принимаемых с одного адреса, а также на скорость отправки почтовых сообщений. Например, компания, которую мы консультируем, рассылает более 100,000,000 писем в месяц, для чего используется не один десяток IP-адресов.</p>
<p>Естественно, что ручная проверка сотен адресов в десятке различных сервисов — занятие неблагодарное и утомительное. Но, к счастью, всё автоматизируется, что будет рассмотрено на примере <a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a>.</p>
<p>Очень часто для извлечения данных из web-страницы (data scraping) используются регулярные выражения. Но это вчерашний день <img src='http://static.sjinks.info/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  К тому же не всегда просто составить эффективное регулярное выражение, которое сразу выберет все необходимые данные — с большой вероятностью могут потребоваться несколько выражений и доводка результата напильником.</p>
<p>Простая и достойная альтернатива регулярным выражениям — <a href="http://blog.sjinks.pro/tag/xpath/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  XPath">XPath</a>. Тем более, что построить нужный <a href="http://blog.sjinks.pro/tag/xpath/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  XPath">XPath</a> можно при помощи FireBug (он позволяет скопировать адрес выделенного элемента как <a href="http://blog.sjinks.pro/tag/xpath/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  XPath">XPath</a>).</p>
<p>Рассмотрим на примере SenderScore. Репутация проверяется по ссылке вида <span class="codebox"><code class="text">https://senderscore.org/lookup.php?lookup=1.1.1.1</code></span>, где вместо 1.1.1.1 задаётся IP-адрес.</p>
<p><a href="http://static.sjinks.info/wp-content/uploads/2011/02/senderscore.png"><img src="http://static.sjinks.info/wp-content/uploads/2011/02/senderscore.png" alt="Проверка репутации IP-адреса при помощи SenderScore" title="Проверка репутации IP-адреса при помощи SenderScore" width="473" height="320" class="alignnone size-full wp-image-887" /></a></p>
<p>Как видно на скриншоте, FireBug показывает XPath требуемого элемента. Но при знании XPath можно использовать и более простое выражение.</p>
<p>Как это выглядит на PHP:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p88636">
        <div class="code php" id="p886code36">
<span class="kw2">&lt;?php</span><br />
&nbsp; &nbsp; <span class="re0">$oldSetting</span> <span class="sy0">=</span> <span class="kw3">libxml_use_internal_errors</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw3">libxml_clear_errors</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="re0">$html</span> <span class="sy0">=</span> <span class="kw2">new</span> DOMDocument<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="re0">$html</span><span class="sy0">-&gt;</span><span class="me1">loadHtmlFile</span><span class="br0">&#40;</span><span class="st_h">'https://senderscore.org/lookup.php?lookup=1.1.1.1'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="re0">$xpath</span> <span class="sy0">=</span> <span class="kw2">new</span> DOMXPath<span class="br0">&#40;</span><span class="re0">$html</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">echo</span> <span class="st_h">'Sender Score: '</span><span class="sy0">,</span> <span class="re0">$xpath</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="st_h">'//div[@id=&quot;senderscore_number&quot;]'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">item</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">nodeValue</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw3">libxml_clear_errors</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw3">libxml_use_internal_errors</span><span class="br0">&#40;</span><span class="re0">$oldSetting</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<h2>XPath для других сервисов</h2>
<h3>Reputation Authority</h3>
<p><span class="codebox"><code class="text">http://reputationauthority.org/lookup.php?ip=1.1.1.1</code></span></p>
<p>Поиск ведётся относительно вершины <code>//div[@class="LookupIP-content"]/table[2]/tr[4]/td</code></p>
<ul>
<li><strong>Reputation Score:</strong> <code>.//table/tr[2]/td[2]</code></li>
<li><strong>Clean emails:</strong> <code>.//table[2]/tr[2]/td[2]</code></li>
<li><strong>Viruses:</strong> <code>.//table[2]/tr[3]/td[2]</code></li>
<li><strong>Spam:</strong> <code>.//table[2]/tr[4]/td[2]</code></li>
<li><strong>Malformed Emails:</strong> <code>.//table[2]/tr[5]/td[2]</code></li>
<li><strong>Suspicious Emails:</strong> <code>.//table[2]/tr[6]/td[2]</code></li>
<li><strong>Good Users:</strong> <code>.//table[2]/tr[8]/td[2]</code></li>
<li><strong>Bad Users:</strong> <code>.//table[2]/tr[9]/td[2]</code></li>
</ul>
<h3>SenderBase</h3>
<p><span class="codebox"><code class="text">http://www.senderbase.org/senderbase_queries/rep_lookup?search_name=1.1.1.1&amp;action%3ASearch=Search</code></span></p>
<p>При запросе страницы User-Agent должен быть похожим на нормальный браузер.</p>
<ul>
<li><strong>Email Reputation</strong> <code>//div[@id="tab1body"]//span[1]/text()</code></li>
<li><strong>Web Reptation</strong> <code>//div[@id="tab2body"]//span[1]/text()</code></li>
</ul>
<h3>SendMail</h3>
<p><span class="codebox"><code class="text">http://sendmail.com/sm/resources/tools/ip_reputation/ip_reputation_results/index.shtml?IP=1.1.1.1&amp;search.x=1&amp;search.y=1&amp;search=Submit</code></span></p>
<p>При запросе страницы Referer должен указывать на <span class="codebox"><code class="text">http://sendmail.com/sm/resources/tools/ip_reputation/</code></span>.</p>
<p>Поиск ведётся относительно вершины <code>//dev[@id="content"]/table[@class="ip_reputation"]</code></p>
<ul>
<li><strong><a href="http://blog.sjinks.pro/tag/ip-reputation/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  IP Reputation">IP Reputation</a> Class:</strong> <code>.//tr[4]/td[2]/text()</code></li>
<li><strong>Volume</strong> (может отсутствовать) <code>.//tr[5]/td[2]</code></li>
<li><strong>Weighted Risk</strong> <code>.//tr[6]/td[2]</code> (если Volume отсутствует, то <code>.//tr[5]/td[2]</code>)</li>
<li><strong>Spam Ratio</strong> (может отсутствовать) <code>.//tr[7]/td[2]</code></li>
<li><strong>Valid Bulk Ratio</strong> (может отсутствовать) <code>.//tr[8]/td[2]</code></li>
</ul>
<p>Либо можно использовать проверку текста:</p>
<ul>
<li><strong>Volume</strong> <code>.//tr/td[1][normalize-space()="Volume"]/following-sibling::td[1]</code></li>
<li><strong>Weighted Risk</strong> <code>.//tr/td[1][normalize-space()="Weighted Risk"]/following-sibling::td[1]</code></li>
<li><strong>Spam Ratio</strong> <code>.//tr/td[1][normalize-space()="Spam Ratio"]/following-sibling::td[1]</code></li>
<li><strong>Valid Bulk Ratio</strong> <code>.//tr/td[1][normalize-space()="Valid Bulk Ratio"]/following-sibling::td[1]</code></li>
</ul>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/886-automatic-ip-reputation-check/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/886-automatic-ip-reputation-check/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Особенности работы функции checkdnsrr()</title>
		<link>http://blog.sjinks.pro/php/825-checkdnsrr-undocumented-features/</link>
		<comments>http://blog.sjinks.pro/php/825-checkdnsrr-undocumented-features/#comments</comments>
		<pubDate>Fri, 24 Sep 2010 12:49:56 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[DNS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[советы]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=825</guid>
		<description><![CDATA[Иногда «Да» означает «Нет» Функция checkdnsrr() используется для проверки существования записей DNS. Например, фреймворк Kohana использует эту функцию для проверки наличия MX-записи домена, указанного в адресе электронной почты. Тем не менее, существуют некоторые подводные камни, связанные с использованием данной функции, о которых разработчикам нужно знать. В Linux есть магический файл — /etc/resolv.conf; он отвечает за конфигурацию резолвера [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/825-checkdnsrr-undocumented-features/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Иногда «Да» означает «Нет»</em></h2>
<p>Функция <a href="http://php.net/manual/en/function.checkdnsrr.php"><code>checkdnsrr()</code></a> используется для проверки существования записей <a href="http://blog.sjinks.pro/tag/dns/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  DNS">DNS</a>. Например, фреймворк <a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a> использует эту функцию для проверки наличия MX-записи домена, указанного в адресе электронной почты.</p>
<p>Тем не менее, существуют некоторые подводные камни, связанные с использованием данной функции, о которых разработчикам нужно знать.<span id="more-825"></span></p>
<p>В <a href="http://blog.sjinks.pro/tag/linux/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Linux">Linux</a> есть магический файл — <a href="http://linux.die.net/man/5/resolv.conf"><code>/etc/resolv.conf</code></a>; он отвечает за конфигурацию <dfn>резолвера</dfn> (набор функций в библиотеке C, предоставляющих доступ к Internet DNS).</p>
<p>В этом файле может быть задан параметр <code>search</code>, в котором задаётся список дополнительных доменов для поиска (обычно локальное имя домена).</p>
<p>Этот параметр может оказывать очень сильное влияние на результат, возвращаемый <code>checkdnsrr()</code>. Например, при поиске записи типа A для несуществующего домена <code>checkdnsrr()</code> вернёт 0:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p82540">
        <div class="code bash" id="p825code40">
$ php <span class="re5">-r</span> <span class="st_h">'echo (int)checkdnsrr(&quot;x.y&quot;, &quot;A&quot;), &quot;\n&quot;;'</span><br />
<span class="nu0">0</span>
        </div>
    </div>
</div>

<p>Но если вдруг в <code>/etc/resolv.conf</code> задан параметр <code>search</code>, то результат уже не так однозначен. Например, если имеется запись</p>
<p><code>search example.com</code></p>
<p>то алгоритм поиска будет таким:</p>
<ol>
<li>Сначала система будет пытаться найти запись A для несуществующего домена <code>x.y</code>;</li>
<li>Так как домен с таким именем не существует, система будет пробовать суффиксы, перечисленные в параметре <code>search</code>. В нашем случае система будет искать домен <code>x.y.example.com</code></li>
</ol>
<p>Если у домена <code>example.com</code> есть wildcard-запись типа A (<code>*.example.com. IN A 1.2.3.4</code>), то <code>checkdnsrr()</code> вернёт <code>true</code> — хотя это и не тот результат, который ожидается.</p>
<p>С проверкой MX-записей может возникнуть такая же ситуация: например, если имеются такие записи DNS:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p82541">
        <div class="code text" id="p825code41">
*.example.com. IN CNAME example.com.<br />
example.com. &nbsp; IN MX 10 mail.example.com.
        </div>
    </div>
</div>

<p>В этом случае при наличии <code>search example.com</code> в <code>/etc/resolv.conf</code> <code>checkdnsrr()</code> будет возвращать <code>true</code> для любых доменов.</p>
<p>Из приведённого примера видим, что положительный результат может быть на самом деле ложноположительным, что на самом деле очень плохо. Тем не менее, есть очень простой способ, позволяющий решить проблему — добавлять точку в конец доменного имени:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p82542">
        <div class="code php" id="p825code42">
<span class="re0">$result</span> <span class="sy0">=</span> <span class="kw3">checkdnsrr</span><span class="br0">&#40;</span><span class="st0">&quot;x.y.&quot;</span><span class="sy0">,</span> <span class="st0">&quot;A&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>В этом случае над именем не будет проводиться никаких манипуляций, и результат будет не таким неопределённым <img src='http://static.sjinks.info/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/825-checkdnsrr-undocumented-features/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/825-checkdnsrr-undocumented-features/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DoS для PHP через imap_fetchbody</title>
		<link>http://blog.sjinks.pro/php/807-php-dos-with-imap_fetchbody/</link>
		<comments>http://blog.sjinks.pro/php/807-php-dos-with-imap_fetchbody/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 07:04:55 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[IMAP]]></category>
		<category><![CDATA[segfault]]></category>
		<category><![CDATA[ошибка]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=807</guid>
		<description><![CDATA[Пример исследования дампа памяти для нахождения ошибки Как оказалось, если передать функции imap_fetchbody() параметр $secton, длина которого больше 1004 байт, PHP падает по segmentation fault. На некоторых конфигурациях с применением некоторых усилий (подробности, понятное дело, разглашаться не будут) получалось вместе с PHP положить и Apache. Пример кода, убивающего PHP: imap_fetchbody($imap_handle, $message_number, &#34;3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.0&#34;); Предполагается, что $imap_handle — ресурс, возвращенный функцией [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/807-php-dos-with-imap_fetchbody/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Пример исследования дампа памяти для нахождения ошибки</em></h2>
<p>Как оказалось, если передать функции <a href="http://us.php.net/manual/en/function.imap-fetchbody.php"><span class="codebox"><code class="php"><span class="kw3">imap_fetchbody</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span></a> параметр <code>$secton</code>, длина которого больше 1004 байт, <a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a> падает по segmentation fault. На некоторых конфигурациях с применением некоторых усилий (подробности, понятное дело, разглашаться не будут) получалось вместе с <a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a> положить и Apache.<span id="more-807"></span></p>
<p>Пример кода, убивающего PHP:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80753">
        <div class="code php" id="p807code53">
<span class="kw3">imap_fetchbody</span><span class="br0">&#40;</span><span class="re0">$imap_handle</span><span class="sy0">,</span> <span class="re0">$message_number</span><span class="sy0">,</span> <span class="st0">&quot;3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.0&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Предполагается, что <code>$<a href="http://blog.sjinks.pro/tag/imap/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  IMAP">imap</a>_handle</code> — ресурс, возвращенный функцией <code>imap_open()</code>, $message_number — номер существующего сообщения в почтовом ящике.</p>
<p>Анализ посмертного дампа памяти показывает, где происходит сбой:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80754">
        <div class="code text" id="p807code54">
$ gdb /usr/bin/php core<br />
...<br />
Program terminated with signal 11, Segmentation fault.<br />
#0 &nbsp;memcpy () at ../sysdeps/x86_64/memcpy.S:398<br />
398 &nbsp; &nbsp; ../sysdeps/x86_64/memcpy.S: Нет такого файла или каталога.<br />
&nbsp; &nbsp; &nbsp; &nbsp; in ../sysdeps/x86_64/memcpy.S<br />
(gdb) bt full<br />
#0 &nbsp;memcpy () at ../sysdeps/x86_64/memcpy.S:398<br />
No locals.<br />
#1 &nbsp;0x0000000000678788 in _estrndup (s=0x7ff1797aaf4e &quot;&quot;, length=2136037704) at /usr/include/bits/string3.h:52<br />
No locals.<br />
#2 &nbsp;0x00007ff1799ce292 in zif_imap_fetchbody () from /usr/lib/php5/20090626/imap.so<br />
No symbol table info available.<br />
#3 &nbsp;0x00000000006e611a in zend_do_fcall_common_helper_SPEC (execute_data=0x7ff17f3e3068) at /build/buildd/php5-5.3.2/Zend/zend_vm_execute.h:313<br />
&nbsp; &nbsp; &nbsp; &nbsp; opline = 0x7ff17f5141a8<br />
&nbsp; &nbsp; &nbsp; &nbsp; should_change_scope = 200 '\310'<br />
...
        </div>
    </div>
</div>

<p>Видно, что <a href="http://blog.sjinks.pro/tag/bug/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  ошибка">ошибка</a> произошла в функции <code>zif_imap_fetchbody()</code> (что соответствует PHP-функции <span class="codebox"><code class="php"><span class="kw3">imap_fetchbody</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>), когда та вызвала функцию <code>estrndup()</code> для пустой строки (адрес строки — 0x7ff1797aaf4e), но при этом передав ошибочную длину строки (<code>length=2136037704</code>). Значение <code>length</code> примерно равное двум гигабайтам в любом случае должно настораживать.</p>
<p>Пересоберём php5-imap с отладочной информацией:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80755">
        <div class="code bash" id="p807code55">
<span class="re2">CFLAGS</span>=<span class="st0">&quot;-g3 -O2&quot;</span><br />
<span class="kw3">export</span> CFLAGS<br />
phpize<br />
.<span class="sy0">/</span>configure <span class="re5">--with-kerberos</span> <span class="re5">--with-imap-ssl</span> <span class="re2">PHP_OPENSSL</span>=<span class="st0">&quot;yes&quot;</span><br />
<span class="kw2">make</span> <span class="sy0">&amp;&amp;</span> <span class="kw2">sudo</span> <span class="kw2">make</span> <span class="kw2">install</span>
        </div>
    </div>
</div>

<p>Повторим тест. PHP по-прежнему должен упасть. На этот раз у нас будет больше информации о сбое:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80756">
        <div class="code text" id="p807code56">
(gdb) bt full<br />
#0 &nbsp;memcpy () at ../sysdeps/x86_64/memcpy.S:398<br />
No locals.<br />
#1 &nbsp;0x0000000000678788 in _estrndup (s=0x7ff8881ddf4e &quot;&quot;, length=2381609288) at /usr/include/bits/string3.h:52<br />
No locals.<br />
#2 &nbsp;0x00007ff8884013c2 in zif_imap_fetchbody (ht=3, return_value=0x7ff88df465d0, return_value_ptr=&lt;value optimized out&gt;, this_ptr=&lt;value optimized out&gt;, return_value_used=&lt;value optimized out&gt;) at /opt/php-imap-5.3.2/php_imap.c:2361<br />
&nbsp; &nbsp; &nbsp; &nbsp; streamind = 0x7ff88df46670<br />
&nbsp; &nbsp; &nbsp; &nbsp; msgno = 1<br />
&nbsp; &nbsp; &nbsp; &nbsp; flags = 0<br />
&nbsp; &nbsp; &nbsp; &nbsp; body = 0x0<br />
&nbsp; &nbsp; &nbsp; &nbsp; sec = 0x7ff88df468c8 &quot;3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.&quot;...<br />
&nbsp; &nbsp; &nbsp; &nbsp; sec_len = 1005<br />
&nbsp; &nbsp; &nbsp; &nbsp; len = 140705510226248
        </div>
    </div>
</div>

<p>Теперь у нас есть номер строки, где произошел сбой (файл /opt/php-imap-5.3.2/php_imap.c, строка 2361).</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80757">
        <div class="code text" id="p807code57">
(gdb) frame 2<br />
#2 &nbsp;0x00007ff8884013c2 in zif_imap_fetchbody (ht=3, return_value=0x7ff88df465d0, return_value_ptr=&lt;value optimized out&gt;, this_ptr=&lt;value optimized out&gt;, return_value_used=&lt;value optimized out&gt;) at /opt/php-imap-5.3.2/php_imap.c:2361<br />
2361 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RETVAL_STRINGL(body, len, 1);<br />
(gdb) print len<br />
$1 = 140705510226248<br />
(gdb) print &amp;len<br />
$2 = (long unsigned int *) 0x7ffffa9bdd80<br />
(gdb) frame 1<br />
#1 &nbsp;0x0000000000678788 in _estrndup (s=0x7ff8881ddf4e &quot;&quot;, length=2381609288) at /usr/include/bits/string3.h:52<br />
warning: Source file is more recent than executable.<br />
52 &nbsp; &nbsp; &nbsp; &nbsp;return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));<br />
(gdb) print s<br />
$3 = 0x7ff8881ddf4e &quot;&quot;<br />
(gdb) print length<br />
$4 = 2381609288
        </div>
    </div>
</div>

<p>О локальной переменной <code>len</code> сказать что-либо трудно, так как функция <code>__builtin___memcpy_chk()</code> могла ее перетереть. Придется исправлять код.</p>
<p>Находим в php_imap.c в функции <code>imap_fetchbody</code> строку</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80758">
        <div class="code c" id="p807code58">
body <span class="sy0">=</span> mail_fetchbody_full<span class="br0">&#40;</span>imap_le_struct<span class="sy0">-&gt;</span>imap_stream<span class="sy0">,</span> msgno<span class="sy0">,</span> sec<span class="sy0">,</span> <span class="sy0">&amp;</span>len<span class="sy0">,</span> <span class="br0">&#40;</span>argc <span class="sy0">==</span> 4 <span class="sy0">?</span> flags <span class="sy0">:</span> NIL<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>и исправляем её на</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80759">
        <div class="code c" id="p807code59">
<span class="kw3">printf</span><span class="br0">&#40;</span><span class="st0">&quot;len before = %ld<span class="es1">\n</span>&quot;</span><span class="sy0">,</span> len<span class="br0">&#41;</span><span class="sy0">;</span><br />
body <span class="sy0">=</span> mail_fetchbody_full<span class="br0">&#40;</span>imap_le_struct<span class="sy0">-&gt;</span>imap_stream<span class="sy0">,</span> msgno<span class="sy0">,</span> sec<span class="sy0">,</span> <span class="sy0">&amp;</span>len<span class="sy0">,</span> <span class="br0">&#40;</span>argc <span class="sy0">==</span> 4 <span class="sy0">?</span> flags <span class="sy0">:</span> NIL<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="kw3">printf</span><span class="br0">&#40;</span><span class="st0">&quot;len after = %ld<span class="es1">\n</span>&quot;</span><span class="sy0">,</span> len<span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Пересобираем, запускаем, получаем:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80760">
        <div class="code text" id="p807code60">
len before = 140425249339720<br />
len after = 140425249339720<br />
Segmentation fault (core dumped)
        </div>
    </div>
</div>

<p>Становится видно, что по каким-то причинам функция <code>mail_fetchbody_full()</code> может вернуть пустую строку, но при этом не установить len в 0.</p>
<p>Исправление тривиально: находим</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80761">
        <div class="code c" id="p807code61">
<span class="kw4">unsigned</span> <span class="kw4">long</span> len<span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Заменяем на</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80762">
        <div class="code c" id="p807code62">
<span class="kw4">unsigned</span> <span class="kw4">long</span> len <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Пересобираем, устанавливаем, запускаем — voilà, ошибка исчезла, php больше не падает.</p>
<p>Результат: полчаса потраченного времени, ошибка исправлена, php не падает.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/807-php-dos-with-imap_fetchbody/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/807-php-dos-with-imap_fetchbody/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Исправление ошибки в INSERT INTO … SELECT в Kohana 3 при использовании конфигурации базы данных, отличной от default</title>
		<link>http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/</link>
		<comments>http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/#comments</comments>
		<pubDate>Thu, 12 Aug 2010 07:40:55 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[Kohana]]></category>
		<category><![CDATA[Kohana 3]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[база данных]]></category>
		<category><![CDATA[ошибка]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=806</guid>
		<description><![CDATA[Исправление короче описания Ситуация: нужно выполнить запрос вида INSERT INTO `table` (`col1`, `col2`) SELECT * FROM `table` WHERE `col1` &#60;&#62; 0; Если использовать конфигурацию default, то всё отлично работает. Если же использовать другую конфигурацию, то можем получить ошибку соединения с базой данных и исключение. Например: &#60;?php // application/config/database.php // Важно, чтобы в конфигурации default нельзя [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Исправление короче описания</em></h2>
<p><strong>Ситуация:</strong> нужно выполнить запрос вида</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80669">
        <div class="code sql" id="p806code69">
<span class="kw1">INSERT</span> <span class="kw1">INTO</span> <span class="st0">`table`</span> <span class="br0">&#40;</span><span class="st0">`col1`</span><span class="sy0">,</span> <span class="st0">`col2`</span><span class="br0">&#41;</span> <span class="kw1">SELECT</span> <span class="sy0">*</span> <span class="kw1">FROM</span> <span class="st0">`table`</span> <span class="kw1">WHERE</span> <span class="st0">`col1`</span> <span class="sy0">&lt;&gt;</span> <span class="nu0">0</span>;
        </div>
    </div>
</div>

<p>Если использовать конфигурацию <code>default</code>, то всё отлично работает. Если же использовать другую конфигурацию, то можем получить ошибку соединения с базой данных и исключение.<span id="more-806"></span></p>
<p>Например:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80670">
        <div class="code php" id="p806code70">
<span class="kw2">&lt;?php</span><br />
<span class="co1">// application/config/database.php</span><br />
<span class="co1">// Важно, чтобы в конфигурации default нельзя было подключиться к серверу</span><br />
<span class="kw1">return</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; <span class="st_h">'test'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'type'</span> &nbsp; &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'mysql'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'connection'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'hostname'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'localhost'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'database'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'test'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'username'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'username'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'password'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'password'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'persistent'</span> <span class="sy0">=&gt;</span> <span class="kw4">FALSE</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'table_prefix'</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'charset'</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="st_h">'utf8'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'caching'</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw4">FALSE</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'profiling'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw4">TRUE</span><span class="sy0">,</span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="sy1">?&gt;</span><br />
<br />
<span class="kw2">&lt;?php</span><br />
<span class="co1">// application/classes/controller/welcome.php</span><br />
<br />
<span class="kw2">class</span> Controller_Welcome <span class="kw2">extends</span> Controller<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> action_index<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">request</span><span class="sy0">-&gt;</span><span class="me1">response</span> <span class="sy0">=</span> DB<span class="sy0">::</span><span class="me2">insert</span><span class="br0">&#40;</span><span class="st_h">'test'</span><span class="sy0">,</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="st_h">'col1'</span><span class="sy0">,</span> <span class="st_h">'col2'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">select</span><span class="br0">&#40;</span>DB<span class="sy0">::</span><span class="me2">select</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">from</span><span class="br0">&#40;</span><span class="st_h">'test'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">where</span><span class="br0">&#40;</span><span class="st_h">'column1'</span><span class="sy0">,</span> <span class="st_h">'&lt;&gt;'</span><span class="sy0">,</span> <span class="st_h">'0'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">compile</span><span class="br0">&#40;</span>Database<span class="sy0">::</span><span class="me2">instance</span><span class="br0">&#40;</span><span class="st_h">'test'</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">.</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>Важно, чтобы правая часть условия в <code>where()</code> была строкой.</p>
<p>В результате если мы запустим проект, то получим что-то вида:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80671">
        <div class="code text" id="p806code71">
INSERT INTO `test` (`column1`, `column2`) ErrorException [ 2 ]: mysql_connect(): Access denied for user 'www-data'@'localhost' (using password: NO) ~ MODPATH/database/classes/kohana/database/mysql.php [ 56 ]
        </div>
    </div>
</div>

<p>Связано это с особенностью реализации метода <code><a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a>_Database_Query_Builder_Insert::compile()</code>:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80672">
        <div class="code php" id="p806code72">
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> compile<span class="br0">&#40;</span>Database <span class="re0">$db</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Start an insertion query</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">=</span> <span class="st_h">'INSERT INTO '</span><span class="sy0">.</span><span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">quote_table</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_table<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Не интересно</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// …</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">is_array</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="br0">&#41;</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="co1">// Не интересно</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// …</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">'VALUES '</span><span class="sy0">.</span><span class="kw3">implode</span><span class="br0">&#40;</span><span class="st_h">', '</span><span class="sy0">,</span> <span class="re0">$groups</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Add the sub-query</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="br0">&#40;</span>string<span class="br0">&#41;</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$query</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Строка <span class="codebox"><code class="php"><span class="re0">$query</span> <span class="sy0">.=</span> <span class="br0">&#40;</span>string<span class="br0">&#41;</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="sy0">;</span></code></span> вызывает метод <code>compile()</code> для <code>SELECT</code>, но так как конфигурация базы данных не передаётся, используется конфигурация <code>default</code>.</p>
<p>Самое простое исправление очевидно: заменить строку</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80673">
        <div class="code php" id="p806code73">
<span class="re0">$query</span> <span class="sy0">.=</span> <span class="br0">&#40;</span>string<span class="br0">&#41;</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="sy0">;</span>
        </div>
    </div>
</div>

<p>на </p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80674">
        <div class="code php" id="p806code74">
<span class="re0">$query</span> <span class="sy0">.=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="sy0">-&gt;</span><span class="me1">compile</span><span class="br0">&#40;</span><span class="re0">$db</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Тем не менее, если <code>SELECT</code> использует одну базу данных, а <code>INSERT</code> — другую, и при этом используются разные charset&#8217;ы (да, это не самое большое извращение — бывают и хуже), этого будет недостаточно. В этом случае нужно добавить дополнительный параметр в метод <code>select()</code>, указывающий, какую конфигурацию использовать. Требуемые изменения кода тривиальны, посему остаются упражнением интересующемуся читателю.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Поддержка JOIN в запросах DELETE для Kohana 3</title>
		<link>http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/</link>
		<comments>http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/#comments</comments>
		<pubDate>Wed, 11 Aug 2010 10:48:13 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[Kohana]]></category>
		<category><![CDATA[Kohana 3]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[база данных]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=805</guid>
		<description><![CDATA[Добавление поддержки multiple-table DELETE в Query Builder Kohana 3 Иногда бывает полезно выполнить удаление записей в нескольких связанных таблицах, благо синтаксис позволяет. Разумеется, есть такая вещь как каскадное удаление записей, но в случае с MySQL (а именно — таблицами MyISAM) на неё надеяться не приходится. Модуль Database из Kohana 3 не умеет использовать JOIN с операцией DELETE. Нужно [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Добавление поддержки multiple-table DELETE в Query Builder <a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a> 3</em></h2>
<p>Иногда бывает полезно выполнить удаление записей в нескольких связанных таблицах, благо <a href="http://dev.mysql.com/doc/refman/5.0/en/delete.html">синтаксис позволяет</a>. Разумеется, есть такая вещь как каскадное удаление записей, но в случае с <a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a> (а именно — таблицами MyISAM) на неё надеяться не приходится.</p>
<p>Модуль Database из <a href="http://blog.sjinks.pro/tag/kohana-3/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana 3">Kohana 3</a> не умеет использовать JOIN с операцией DELETE. Нужно ему помочь.<span id="more-805"></span></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80580">
        <div class="code php" id="p805code80">
<span class="kw2">&lt;?php</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">class</span> Database_Query_Builder_Delete <span class="kw2">extends</span> Kohana_Database_Query_Builder_Delete<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">protected</span> <span class="re0">$_to_delete</span> <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">protected</span> <span class="re0">$_join</span> <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">protected</span> <span class="re0">$_last_join</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co4">/**<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Adds addition tables to &quot;JOIN ...&quot;.<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param mixed &nbsp; column name or array($column, $alias) or object<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param string &nbsp;join type (LEFT, RIGHT, INNER, etc)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @return Database_Query_Builder_Delete<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> <span class="kw3">join</span><span class="br0">&#40;</span><span class="re0">$table</span><span class="sy0">,</span> <span class="re0">$type</span> <span class="sy0">=</span> <span class="kw4">NULL</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="re0">$this</span><span class="sy0">-&gt;</span>_join<span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_last_join <span class="sy0">=</span> <span class="kw2">new</span> Database_Query_Builder_Join<span class="br0">&#40;</span><span class="re0">$table</span><span class="sy0">,</span> <span class="re0">$type</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$this</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> delete_table<span class="br0">&#40;</span><span class="re0">$table</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="re0">$this</span><span class="sy0">-&gt;</span>_to_delete<span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$table</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co4">/**<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Adds &quot;ON ...&quot; conditions for the last created JOIN statement.<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param &nbsp; mixed &nbsp; column name or array($column, $alias) or object<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param &nbsp; string &nbsp;logic operator<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param &nbsp; mixed &nbsp; column name or array($column, $alias) or object<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @return &nbsp;$this<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> on<span class="br0">&#40;</span><span class="re0">$c1</span><span class="sy0">,</span> <span class="re0">$op</span><span class="sy0">,</span> <span class="re0">$c2</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="re0">$this</span><span class="sy0">-&gt;</span>_last_join<span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="re0">$c1</span><span class="sy0">,</span> <span class="re0">$op</span><span class="sy0">,</span> <span class="re0">$c2</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co4">/**<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Compile the SQL query and return it.<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param Database &nbsp;Database instance<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @return string<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> compile<span class="br0">&#40;</span>Database <span class="re0">$db</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="re0">$query</span> <span class="sy0">=</span> <span class="st_h">'DELETE'</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_to_delete<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' '</span> <span class="sy0">.</span> <span class="kw3">join</span><span class="br0">&#40;</span><span class="st_h">', '</span><span class="sy0">,</span> <span class="kw3">array_map</span><span class="br0">&#40;</span><span class="kw1">array</span><span class="br0">&#40;</span><span class="re0">$db</span><span class="sy0">,</span> <span class="st_h">'quote_identifier'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_to_delete<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' FROM '</span> <span class="sy0">.</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">quote_table</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_table<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_join<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' '</span> <span class="sy0">.</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_compile_join<span class="br0">&#40;</span><span class="re0">$db</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_join<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_where<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' WHERE '</span> <span class="sy0">.</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_compile_conditions<span class="br0">&#40;</span><span class="re0">$db</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_where<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_join<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_order_by<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' '</span> <span class="sy0">.</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_compile_order_by<span class="br0">&#40;</span><span class="re0">$db</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_order_by<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_limit <span class="sy0">!==</span> <span class="kw4">null</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' LIMIT '</span> <span class="sy0">.</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_limit<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw <span class="kw2">new</span> Kohana_Exception<span class="br0">&#40;</span><span class="st_h">'Cannot use ORDER BY or LIMIT in a multiple-table DELETE'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$query</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> <span class="kw3">reset</span><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="kw2">parent</span><span class="sy0">::</span><span class="kw3">reset</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_to_delete <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_join &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_last_join <span class="sy0">=</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 />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>Данный класс добавляет функциональность, связанную с удалением данных из нескольких таблиц, в Query Builder. Использование покажу на примерах:</p>
<p>Запросу</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80581">
        <div class="code mysql" id="p805code81">
<span class="kw1">DELETE</span> t1<span class="sy2">,</span> t2 <span class="kw1">FROM</span> t1 <span class="kw1">INNER</span> <span class="kw1">JOIN</span> t2 <span class="kw1">INNER</span> <span class="kw1">JOIN</span> t3<br />
<span class="kw1">WHERE</span> t1.id<span class="sy1">=</span>t2.id <span class="kw10">AND</span> t2.id<span class="sy1">=</span>t3.id<span class="sy2">;</span>
        </div>
    </div>
</div>

<p>соответствует код</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80582">
        <div class="code php" id="p805code82">
DB<span class="sy0">::</span><span class="me2">delete</span><span class="br0">&#40;</span><span class="st_h">'t1'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">join</span><span class="br0">&#40;</span><span class="st_h">'t2'</span><span class="sy0">,</span> <span class="st_h">'INNER'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="st_h">'t1.id'</span><span class="sy0">,</span> <span class="st_h">'='</span><span class="sy0">,</span> <span class="st_h">'t2.id'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">join</span><span class="br0">&#40;</span><span class="st_h">'t3'</span><span class="sy0">,</span> <span class="st_h">'INNER'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="st_h">'t2.id'</span><span class="sy0">,</span> <span class="st_h">'='</span><span class="sy0">,</span> <span class="st_h">'t3.id'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">delete_table</span><span class="br0">&#40;</span><span class="st_h">'t1'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">delete_table</span><span class="br0">&#40;</span><span class="st_h">'t2'</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Запросу </p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80583">
        <div class="code mysql" id="p805code83">
<span class="kw1">DELETE</span> <span class="kw1">FROM</span> t1 <span class="kw1">INNER</span> <span class="kw1">JOIN</span> t2 <span class="kw1">INNER</span> <span class="kw1">JOIN</span> t3<br />
<span class="kw1">WHERE</span> t1.id<span class="sy1">=</span>t2.id <span class="kw10">AND</span> t2.id<span class="sy1">=</span>t3.id<span class="sy2">;</span>
        </div>
    </div>
</div>

<p>соответствует код</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80584">
        <div class="code php" id="p805code84">
DB<span class="sy0">::</span><span class="me2">delete</span><span class="br0">&#40;</span><span class="st_h">'t1'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">join</span><span class="br0">&#40;</span><span class="st_h">'t2'</span><span class="sy0">,</span> <span class="st_h">'INNER'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="st_h">'t1.id'</span><span class="sy0">,</span> <span class="st_h">'='</span><span class="sy0">,</span> <span class="st_h">'t2.id'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">join</span><span class="br0">&#40;</span><span class="st_h">'t3'</span><span class="sy0">,</span> <span class="st_h">'INNER'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="st_h">'t2.id'</span><span class="sy0">,</span> <span class="st_h">'='</span><span class="sy0">,</span> <span class="st_h">'t3.id'</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Как видим, метод <code>Database_Query_Builder_Delete::delete_table()</code> задаёт имена таблиц, которые нужно удалить; если данный метод не вызывать, будут удалены <strong>все</strong> таблицы, перечисленные в запросе.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

