<?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>Mon, 06 Feb 2012 17:56:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<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="p9475">
        <div class="code mysql" id="p947code5">
<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="p9476">
        <div class="code php" id="p947code6">
<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="p9477">
        <div class="code php" id="p947code7">
<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="p9478">
        <div class="code php" id="p947code8">
<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="p93716">
        <div class="code php" id="p937code16">
<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>Пришлось изобретать свой хэндлер и &laquo;оборачивать&raquo; все обращения к мемкешу предвалительно нашим &laquo;молчаливым&raquo; хэндлером:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93717">
        <div class="code php" id="p937code17">
<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>В самом коде используется так, что перед потенциально &laquo;опасными&raquo; обращениями к мемкешу, мы выставляем свой &laquo;молчаливый&raquo; хэндлер, а потом возвращаем старый &#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="p93718">
        <div class="code php" id="p937code18">
&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="p93719">
        <div class="code php" id="p937code19">
<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="p93720">
        <div class="code php" id="p937code20">
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="p93721">
        <div class="code php" id="p937code21">
<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="p93722">
        <div class="code php" id="p937code22">
<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> помогает разработчику, отлавливая неперехваченные исключения и ошибки в коде. Как и в Kohana, реализуется это одинаково — через функции <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="p93425">
        <div class="code php" id="p934code25">
<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="p93426">
        <div class="code php" id="p934code26">
<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 поддерживают вложенные транзакции (о чём PDO не знает). Поэтому было бы неплохо добавить поддержку вложенных транзакций для данных СУБД в PDO.<span id="more-932"></span></p>
<p>Ниже приведена очень простая реализация поддержки вложенных транзакций в PDO:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p93228">
        <div class="code php" id="p932code28">
<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> <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> <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>© 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="p88630">
        <div class="code php" id="p886code30">
<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="p82534">
        <div class="code bash" id="p825code34">
$ 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="p82535">
        <div class="code text" id="p825code35">
*.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="p82536">
        <div class="code php" id="p825code36">
<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="p80747">
        <div class="code php" id="p807code47">
<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="p80748">
        <div class="code text" id="p807code48">
$ 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="p80749">
        <div class="code bash" id="p807code49">
<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="p80750">
        <div class="code text" id="p807code50">
(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="p80751">
        <div class="code text" id="p807code51">
(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="p80752">
        <div class="code c" id="p807code52">
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="p80753">
        <div class="code c" id="p807code53">
<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="p80754">
        <div class="code text" id="p807code54">
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="p80755">
        <div class="code c" id="p807code55">
<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="p80756">
        <div class="code c" id="p807code56">
<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="p80663">
        <div class="code sql" id="p806code63">
<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="p80664">
        <div class="code php" id="p806code64">
<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="p80665">
        <div class="code text" id="p806code65">
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="p80666">
        <div class="code php" id="p806code66">
&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="p80667">
        <div class="code php" id="p806code67">
<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="p80668">
        <div class="code php" id="p806code68">
<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="p80574">
        <div class="code php" id="p805code74">
<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="p80575">
        <div class="code mysql" id="p805code75">
<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="p80576">
        <div class="code php" id="p805code76">
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="p80577">
        <div class="code mysql" id="p805code77">
<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="p80578">
        <div class="code php" id="p805code78">
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>
		<item>
		<title>Патч для кэширования пустых результатов запросов в Kohana</title>
		<link>http://blog.sjinks.pro/php/kohana/804-patch-cache-empty-query-results-in-kohana/</link>
		<comments>http://blog.sjinks.pro/php/kohana/804-patch-cache-empty-query-results-in-kohana/#comments</comments>
		<pubDate>Wed, 11 Aug 2010 08:25:23 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[Kohana]]></category>
		<category><![CDATA[Kohana 3]]></category>
		<category><![CDATA[база данных]]></category>
		<category><![CDATA[кэш]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=804</guid>
		<description><![CDATA[Простое исправление досадного недоразумения Проблема: Kohana 3 (3.0.7, возможно, ранние версии) не может брать результаты запросов из кэша, если запрос вернул пустой результат. Решение проблемы. Разгадка кроется в файле modules/database/classes/kohana/database/query.php. Дело в том, что в PHP проверки вида if ($condition) возвращают false, если вычисленное значение $condition — 0, null, false, &#34;&#34;, array(), …. Поэтому в такой проверке: if [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/804-patch-cache-empty-query-results-in-kohana/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Простое исправление досадного недоразумения</em></h2>
<p><strong>Проблема:</strong> <a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a> 3 (3.0.7, возможно, ранние версии) не может брать результаты запросов из кэша, если запрос вернул пустой результат.</p>
<p><strong>Решение проблемы.</strong><span id="more-804"></span></p>
<p>Разгадка кроется в файле <code>modules/database/classes/kohana/database/query.<a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">php</a></code>.  Дело в том, что в PHP проверки вида <span class="codebox"><code class="php"><span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$condition</span><span class="br0">&#41;</span></code></span> возвращают <code>false</code>, если вычисленное значение <code>$condition</code> — <a href="http://php.net/manual/en/language.types.boolean.php#language.types.boolean.casting">0, null, false, <span class="codebox"><code class="php"><span class="st0">&quot;&quot;</span></code></span>, array(), …</a>.</p>
<p>Поэтому в такой проверке:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80481">
        <div class="code php" id="p804code81">
<span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$result</span> <span class="sy0">=</span> Kohana<span class="sy0">::</span><span class="me2">cache</span><span class="br0">&#40;</span><span class="re0">$cache_key</span><span class="sy0">,</span> <span class="kw4">NULL</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_lifetime<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co1">// Return a cached result</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">new</span> Database_Result_Cached<span class="br0">&#40;</span><span class="re0">$result</span><span class="sy0">,</span> <span class="re0">$sql</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_as_object<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>условие всегда будет ложно для пустых результатов.</p>
<p>Исправляется просто: из исходного кода <code>Kohana::cache()</code> видно, что <code>Kohana::cache()</code> возвращает <code>null</code>, если объект в кэше не найден либо произошла <a href="http://blog.sjinks.pro/tag/bug/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  ошибка">ошибка</a>. Следовательно,</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80482">
        <div class="code php" id="p804code82">
<span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span><span class="re0">$result</span> <span class="sy0">=</span> Kohana<span class="sy0">::</span><span class="me2">cache</span><span class="br0">&#40;</span><span class="re0">$cache_key</span><span class="sy0">,</span> <span class="kw4">NULL</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_lifetime<span class="br0">&#41;</span><span class="br0">&#41;</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="co1">// Return a cached result</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">new</span> Database_Result_Cached<span class="br0">&#40;</span><span class="re0">$result</span><span class="sy0">,</span> <span class="re0">$sql</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_as_object<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>даст корректный результат.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/804-patch-cache-empty-query-results-in-kohana/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/kohana/804-patch-cache-empty-query-results-in-kohana/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

