<?xml version="1.0" encoding="utf-8"?>
<!-- generator="FeedCreator 1.8.0-dev (info@mypapit.net)" -->
<rss version="2.0" >
    <channel>
        <title>CodeShit</title>
        <description><![CDATA[Shit Happens.]]></description>
        <link>http://codeshit.com</link>
        <lastBuildDate>Sun, 05 Sep 2010 19:54:58 GMT</lastBuildDate>
        <generator>FeedCreator 1.8.0-dev (info@mypapit.net)</generator>
        <item>
            <title>Счётчик оставшихся символов для формы</title>
            <link>http://codeshit.com/post416</link>
            <description><![CDATA[<p><strong>Задача: сделать счётчики оставшихся символов для полей формы ограниченной длины, в частности <code>&lt;input type=&quot;text&quot;&gt;</code> и <a href="http://tinymce.moxiecode.com">TinyMCE</a>.</strong></p><p>Показывать под формой сообщение: &laquo;Осталось 25 символов&raquo;&nbsp;&mdash; и адекватно его обновлять. В решении будут поддерживаться все основные движки браузеров, и изменение содержимого формы будет ловиться гораздо более точно, чем событием <code>onkeyup</code>.</p><p>Пример:</p>
<p>
<input style="width:70%; font-size:14px;" type="text" id="message-subject" value="" maxlength="50" /><br />
<span id="msgCounter_message-subject" style="font-size:12px; color:#666666;" >&nbsp;</span>
</p>
<script type="text/javascript" src="/js/bedetector.js"></script>
<script type="text/javascript">//<![CDATA[
var inputlineMaxChars = 50; // Also update maxlength="..." attribute of <input>.
function inputlineAttachCharCounter(ed) {
    el = document.getElementById(ed);
    if (BEDetector.engine.is.Gecko || BEDetector.engine.is.WebKit || BEDetector.engine.is.Opera) {
        el.addEventListener('input',
            function() { inputlineUpdateCharCounter(this) },
            false);
        // Help Gecko
        el.addEventListener('dragdrop',
            function() { inputlineUpdateCharCounter(this) },
            false);
    } 
    else if (BEDetector.engine.is.MSHTML) {
        el.attachEvent('onpropertychange',
            function() { inputlineUpdateCharCounter(null)} );
    } else {
        el.addEventListener('keyup',
            function() { inputlineUpdateCharCounter(this) },
            false);
        el.addEventListener('DOMCharacterDataModified',
            function() { inputlineUpdateCharCounter(this) },
            false);
    }
}
function updateCounterText(id, n) {
    var el = document.getElementById(id);
    if (n <= 0) {
        el.innerHTML = '<span style="color:#8a1f11">No characters left.</span>';
    } else {
        if (n == 1)
            el.innerHTML = "1 character left.";
        else
            el.innerHTML = n + " characters left.";
    }
}
function inputlineUpdateCharCounter(el) {
    if (!el && window.event && window.event.srcElement) // IE
        el = window.event.srcElement;
    var x = el.value;
    var z = inputlineMaxChars - x.length;
    updateCounterText('msgCounter_' + el.id, z);
}
inputlineAttachCharCounter('message-subject');
inputlineUpdateCharCounter(document.getElementById('message-subject'));
//]]></script>
<p>Итак, главная проблема такого рода скриптов&nbsp;&mdash; поймать событие изменения формы, а оно включает в себя:</p><ul><li>нажатие буквенных клавиш, </li><li>вставка текста из буфера обмена клавиатурой или мышью, </li><li>удаление или вырезание выделенного текста клавиатурой или мышью, </li><li>перемещение мышью выделенного фрагмента текста (drag&rsquo;n&rsquo;drop).</li></ul><p>Для случая с <code>&lt;input&gt;</code> будет изменяться атрибут <code>value</code>, для случая TinyMCE будет изменяться DOM дерево.</p><h2><code>&lt;input type=&quot;text&quot;&gt;</code></h2><p>Движок Gecko поддерживает нестандартное событие <code>input</code>, которое пришло из <a href="http://developer.mozilla.org/en/XUL">XUL</a>:</p><blockquote><p> This event is sent when a user enters text in a textbox. This event is only called when the text displayed would change, thus it is not called when the user presses non-displayable keys.</p><p><em>(<a href="http://developer.mozilla.org/en/XUL/Events">XUL Events</a>.)</em></p></blockquote><p>Также выяснилось, что это событие поддерживают WebKit и Opera. WebKit также поддерживает события <code>DOMSubtreeModified</code> и <code>paste</code>, а Opera&nbsp;&mdash; <code>DOMControlValueChanged</code>. Все они вызываются в нашем случае, но нет необходимости.</p><p>Gecko пропускает событие при перетаскивании текста мышью, поэтому добавим для него событие <code>dragdrop</code>.</p><p>IE поддерживает нестандартное событие <code>onpropertychange</code>: </p><blockquote><p> The <code>onpropertychange</code> event fires when properties of an object, expando, or style sub-object change. &lt;&hellip;&gt; When the <code>onpropertychange</code> event fires, the <code>srcElement</code> property of the event object is set to the object whose property has changed.</p><p><em>(<a href="http://msdn.microsoft.com/en-us/library/ms536956(VS.85).aspx">MSDN onpropertychange Event</a>.)</em></p></blockquote><p>Как fallback будем использовать событие <code>keyup</code> (наиболее простое) и <code>DOMCharacterDataModified</code> (на мой взгляд самое правильное из стандартных событий W3C, определено в <a href="http://www.w3.org/TR/DOM-Level-3-Events/events.html#event-DOMCharacterDataModified">DOM Level 3 Draft, MutationEvent</a>). По факту, необходимый текст является атрибутом элемента <code>&lt;input&gt;</code>, и должен вызываться <code>DOMAttrModified</code>, но ни один из движков с этим не согласился.</p><p>К сожалению, определить программно поддерживает ли браузер событие или нет не просто: нужно создавать тестовый элемент, вешать событие, программно вызывать и смотреть результат. Поэтому будем определять движок на основе <a href="http://freenet-homepage.de/CoolMcKluus/software/kmeleon/tests/browser_engine_detector/">объектной модели JavaScript</a>.</p><div dir="ltr" class="source_code"><div class="source_code_lang">JavaScript</div><pre class="javascript source_code_text"><span class="kw2">function</span> inputlineAttachCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    <span class="co1">// Gecko only suppports 'input' (XUL)</span>
    <span class="co1">// WebKit: 'input', parentNode.'DOMSubtreeModified', 'paste'</span>
    <span class="co1">// IE: onpropertychange</span>
    <span class="co1">// Opera: DOMControlValueChanged, input</span>
&nbsp;
    el <span class="sy0">=</span> document.<span class="me1">getElementById</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// First check most speific engines, and end with generic standard events.</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>BEDetector.<span class="me1">engine</span>.<span class="kw1">is</span>.<span class="me1">Gecko</span> <span class="sy0">||</span> BEDetector.<span class="me1">engine</span>.<span class="kw1">is</span>.<span class="me1">WebKit</span> <span class="sy0">||</span> BEDetector.<span class="me1">engine</span>.<span class="kw1">is</span>.<span class="me1">Opera</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        el.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'input'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> inputlineUpdateCharCounter<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><span class="sy0">,</span>
            <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="co1">// Help Gecko</span>
        el.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'dragdrop'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> inputlineUpdateCharCounter<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><span class="sy0">,</span>
            <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span> 
    <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span>BEDetector.<span class="me1">engine</span>.<span class="kw1">is</span>.<span class="me1">MSHTML</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        el.<span class="me1">attachEvent</span><span class="br0">&#40;</span><span class="st0">'onpropertychange'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> inputlineUpdateCharCounter<span class="br0">&#40;</span><span class="kw2">null</span><span class="br0">&#41;</span><span class="br0">&#125;</span> <span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
        <span class="co1">// Fall to generic standard support</span>
        el.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'keyup'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> inputlineUpdateCharCounter<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><span class="sy0">,</span>
            <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="co1">// maybe KHTML (DOMCharacterDataModified, also DOMSubtreeModified)</span>
        el.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'DOMCharacterDataModified'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> inputlineUpdateCharCounter<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><span class="sy0">,</span>
            <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div><p>Остался простой код по выполнению всяких служебных действий.</p><div dir="ltr" class="source_code"><div class="source_code_lang">JavaScript</div><pre class="javascript source_code_text"><span class="sy0">&lt;</span>script type<span class="sy0">=</span><span class="st0">&quot;text/javascript&quot;</span> src<span class="sy0">=</span><span class="st0">&quot;/js/bedetector.js&quot;</span><span class="sy0">&gt;&lt;/</span>script<span class="sy0">&gt;</span>
<span class="sy0">&lt;</span>script type<span class="sy0">=</span><span class="st0">&quot;text/javascript&quot;</span><span class="sy0">&gt;</span>
<span class="coMULTI">/* If &lt;input&gt; text id is &quot;message-subject&quot;, empty &lt;div&gt; for &quot;... characters left&quot; 
   must have id &quot;msgCounter_message-subject&quot;. The same for tinymce.
*/</span>
<span class="co1">// Constants</span>
<span class="kw2">var</span> tinymceMaxChars <span class="sy0">=</span> <span class="nu0">500</span><span class="sy0">;</span>
<span class="kw2">var</span> inputlineMaxChars <span class="sy0">=</span> <span class="nu0">50</span><span class="sy0">;</span> <span class="co1">// Also update maxlength=&quot;...&quot; attribute of &lt;input&gt;.</span>
<span class="co1">// Prototype library, may be used without it.</span>
document.<span class="me1">observe</span><span class="br0">&#40;</span><span class="st0">&quot;dom:loaded&quot;</span><span class="sy0">,</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
    inputlineAttachCharCounter<span class="br0">&#40;</span><span class="st0">'message-subject'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    inputlineUpdateCharCounter<span class="br0">&#40;</span>$<span class="br0">&#40;</span><span class="st0">'message-subject'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">&lt;/</span>script<span class="sy0">&gt;</span></pre></div><h2>TinyMCE</h2><p>Специфика счётчика для TinyMCE в том, что он модифицирует DOM, и нужно решать иные задачи: повесить событие на изменение DOM, найти длину текста без тегов, корректно запретить превышение лимита.</p><h3>Событие изменения DOM</h3><p>Поскольку нас интересуют только видимые символы, а не теги, самое подходящее событие стандартного W3C DOM&nbsp;&mdash; <code>DOMCharacterDataModified</code>. Его поддерживает Gecko и WebKit, также эти движки поддерживают подходящие для нашего случая <code>DOMSubtreeModified</code> и <code>paste</code>. Но у Gecko обнаружилась проблема, он иногда терял событие, поэтому для него я использовал более общее <code>DOMSubtreeModified</code>.</p><p>IE к сожалению ничего не может предложить для отслеживания изменения DOM, поэтому будем выходить из положения следующим набором: <code>onkeyup</code>, <code>onpaste</code>, <code>oncut</code>, <code>ondragend</code>.</p><p>Как fallback и для Opera (она поддерживает только <code>keyup</code>) будем использовать <code>keyup</code> и <code>DOMCharacterDataModified</code>.</p><div dir="ltr" class="source_code"><div class="source_code_lang">JavaScript</div><pre class="javascript source_code_text"><span class="kw2">function</span> tinymceAttachCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    <span class="coMULTI">/*
       tinymce editor (ed.getBody()), it is a &lt;body&gt; with DOM in editorMode.
       Gecko: DOMCharacterDataModified, DOMSubtreeModified, paste
       WebKit: DOMCharacterDataModified, DOMSubtreeModified, paste
       Opera: keyup only
       IE: onkeyup, onpaste, oncut, ondragend
     */</span>
    el <span class="sy0">=</span> ed.<span class="me1">getBody</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// First check most speific engines, and end with generic standard events.</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>BEDetector.<span class="me1">engine</span>.<span class="kw1">is</span>.<span class="me1">Gecko</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="co1">// Gecko sometimes looses DOMCharacterDataModified</span>
        el.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'DOMSubtreeModified'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
            <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">,</span>
            <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span> 
    <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span>BEDetector.<span class="me1">engine</span>.<span class="kw1">is</span>.<span class="me1">WebKit</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        el.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'DOMCharacterDataModified'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
            <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">,</span>
            <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span> 
    <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span>BEDetector.<span class="me1">engine</span>.<span class="kw1">is</span>.<span class="me1">MSHTML</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        el.<span class="me1">attachEvent</span><span class="br0">&#40;</span><span class="st0">'onkeyup'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
            <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#41;</span><span class="sy0">;</span>
        el.<span class="me1">attachEvent</span><span class="br0">&#40;</span><span class="st0">'onpaste'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
            <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#41;</span><span class="sy0">;</span>
        el.<span class="me1">attachEvent</span><span class="br0">&#40;</span><span class="st0">'oncut'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
            <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#41;</span><span class="sy0">;</span>
        el.<span class="me1">attachEvent</span><span class="br0">&#40;</span><span class="st0">'ondragend'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
            <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
        <span class="co1">// Fall to generic standard support, also Opera is here</span>
        el.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'keyup'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
            <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">,</span>
            <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="co1">// Not Opera, maybe KHTML (DOMCharacterDataModified, also DOMSubtreeModified)</span>
        el.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'DOMCharacterDataModified'</span><span class="sy0">,</span>
            <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
            <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">,</span>
            <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div><p>Отдельно хочу отметить особую конструкцию языка, которую я здесь применил: двойное <em>замыкание</em>. Ниже упрощённый пример конструкции:</p><div dir="ltr" class="source_code"><div class="source_code_lang">JavaScript</div><pre class="javascript source_code_text"><span class="kw2">function</span> tinymceAttachCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    ed.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'DOMSubtreeModified'</span><span class="sy0">,</span>
        <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>e<span class="br0">&#41;</span> <span class="br0">&#125;</span>
        <span class="br0">&#125;</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">,</span>
        <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div><p>Функции в JS имеют доступ к контексту, в котором они определены (за исключением переменных <code>this</code> и <code>arguments</code>). Казалось бы, что мешает сделать просто:</p><div dir="ltr" class="source_code"><div class="source_code_lang">JavaScript</div><pre class="javascript source_code_text"><span class="co1">// Ошибочный пример</span>
<span class="kw2">function</span> tinymceAttachCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    ed.<span class="me1">addEventListener</span><span class="br0">&#40;</span><span class="st0">'DOMSubtreeModified'</span><span class="sy0">,</span>
        <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#125;</span>
        <span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div><p>Но время жизни внутренней функции (обработчика события <code>function()</code>) больше, чем внешней (назначающей обработчики <code>tinymceAttachCharCounter(ed)</code>. Внутренняя функция (замыкание) получает доступ к переменным внешней функции по ссылке, а не по значению. Поэтому в момент исполнения обработчика <code>function() { tinymceUpdateCharCounter(ed) }</code> будет использоваться текущая переменная <code>ed</code>, а не та, что была в момент создания. Если позже во внешней функции или в любом вызове внутренней функции переменная <code>ed</code> изменится, то внутренняя функция перестанет работать. Хотя внешняя функция давно завершилась, время жизни её используемых переменных расширяется до времени жизни внутренних функций.</p><p>Мне нужно привязать созданную функцию к <code>ed</code> в момент вызова, поэтому я создаю функцию, и тут же вызываю её, передавая в неё текущую переменную <code>ed</code>&nbsp;&mdash; двойное замыкание (см. первый пример). Теперь у каждой внутренней функции будет своя, независимая от внешней функции переменная <code>ed</code>.</p><h3>Длина текста без тегов и ограничение длины редактора</h3><p>Здесь код более простой и сильно специфичен для TinyMCE, поэтому привожу его практически без комментариев. </p><p>Ограничение длины текста в редакторе можно было бы сделать через метод <code>ed.execCommand('Undo')</code>, но он отменяет гораздо больше, чем последний нажатый символ, поэтому я реализовал собственный движок для одноуровнего Undo.</p><div dir="ltr" class="source_code"><div class="source_code_lang">JavaScript</div><pre class="javascript source_code_text"><span class="kw2">function</span> updateCounterText<span class="br0">&#40;</span>id<span class="sy0">,</span> n<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    <span class="kw2">var</span> el <span class="sy0">=</span> document.<span class="me1">getElementById</span><span class="br0">&#40;</span>id<span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>n <span class="sy0">&lt;=</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        el.<span class="me1">innerHTML</span> <span class="sy0">=</span> <span class="st0">'&lt;span style=&quot;color:#8a1f11&quot;&gt;No characters left.&lt;/span&gt;'</span><span class="sy0">;</span>
    <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
        <span class="kw1">if</span> <span class="br0">&#40;</span>n <span class="sy0">==</span> <span class="nu0">1</span><span class="br0">&#41;</span>
            el.<span class="me1">innerHTML</span> <span class="sy0">=</span> <span class="st0">&quot;1 character left.&quot;</span><span class="sy0">;</span>
        <span class="kw1">else</span>
            el.<span class="me1">innerHTML</span> <span class="sy0">=</span> n <span class="sy0">+</span> <span class="st0">&quot; characters left.&quot;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">function</span> tinymceGetLength<span class="br0">&#40;</span>oEditor<span class="br0">&#41;</span> 
<span class="br0">&#123;</span>
    <span class="co1">// Get the Editor Area DOM (Document object).</span>
    <span class="kw2">var</span> oDOM <span class="sy0">=</span> oEditor.<span class="me1">getDoc</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">;</span>
&nbsp;
    <span class="kw2">var</span> iLength <span class="sy0">;</span>
    <span class="co1">// The are two diffent ways to get the text (without HTML markups).</span>
    <span class="co1">// It is browser specific.</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>document.<span class="me1">all</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="co1">// If Internet Explorer.</span>
        iLength <span class="sy0">=</span> oDOM.<span class="me1">body</span>.<span class="me1">innerText</span>.<span class="me1">length</span><span class="sy0">;</span>
    <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
        <span class="co1">// If Gecko.</span>
        <span class="kw2">var</span> r <span class="sy0">=</span> oDOM.<span class="me1">createRange</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">;</span>
        r.<span class="me1">selectNodeContents</span><span class="br0">&#40;</span>oDOM.<span class="me1">body</span><span class="br0">&#41;</span><span class="sy0">;</span>
        iLength <span class="sy0">=</span> r.<span class="me1">toString</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">length</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="kw1">return</span> iLength<span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">function</span> tinymceUpdateCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span>
<span class="br0">&#123;</span>
    <span class="co1">// To make things faster, remove bookmarks.</span>
    currCount <span class="sy0">=</span> tinymceGetLength<span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">;</span>
    remainCount <span class="sy0">=</span> tinymceMaxChars <span class="sy0">-</span> currCount <span class="sy0">+</span> <span class="nu0">1</span><span class="sy0">;</span> <span class="co1">//fix: allow +1</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span>remainCount <span class="sy0">&lt;=</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="co1">// Need to use own undo for small events.</span>
        <span class="co1">//ed.execCommand('Undo');</span>
        <span class="kw1">if</span> <span class="br0">&#40;</span>tinymceUndoBuffer<span class="br0">&#91;</span>ed.<span class="me1">editorId</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
            ed.<span class="me1">setContent</span><span class="br0">&#40;</span>tinymceUndoBuffer<span class="br0">&#91;</span>ed.<span class="me1">editorId</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
            ed.<span class="me1">selection</span>.<span class="me1">moveToBookmark</span><span class="br0">&#40;</span>tinymceUndoBookmark<span class="br0">&#91;</span>ed.<span class="me1">editorId</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
        tinymceUndoBuffer<span class="br0">&#91;</span>ed.<span class="me1">editorId</span><span class="br0">&#93;</span> <span class="sy0">=</span> ed.<span class="me1">getContent</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        tinymceUndoBookmark<span class="br0">&#91;</span>ed.<span class="me1">editorId</span><span class="br0">&#93;</span> <span class="sy0">=</span> ed.<span class="me1">selection</span>.<span class="me1">getBookmark</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="co1">//-1 to counteract above +1</span>
    updateCounterText<span class="br0">&#40;</span><span class="st0">'msgCounter_'</span> <span class="sy0">+</span> ed.<span class="me1">editorId</span><span class="sy0">,</span> remainCount <span class="sy0">-</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div><p>При создании TinyMCE я вызываю свои функции инициализации по собственному событию редактора <code>onInit</code>, и поскольку не во всех движках удалось добиться хорошего обнаружения изменений DOM, помогаю внутренним событием <code>onChange</code>, но оно вызывается редко (в момент создания уровня внутреннего Undo).</p><div dir="ltr" class="source_code"><div class="source_code_lang">JavaScript</div><pre class="javascript source_code_text"><span class="sy0">&lt;</span>script type<span class="sy0">=</span><span class="st0">&quot;text/javascript&quot;</span> src<span class="sy0">=</span><span class="st0">&quot;/js/bedetector.js&quot;</span><span class="sy0">&gt;&lt;/</span>script<span class="sy0">&gt;</span>
<span class="sy0">&lt;</span>script type<span class="sy0">=</span><span class="st0">&quot;text/javascript&quot;</span><span class="sy0">&gt;</span>
<span class="coMULTI">/* If &lt;input&gt; text id is &quot;message-subject&quot;, empty &lt;div&gt; for &quot;... characters left&quot; 
   must have id &quot;msgCounter_message-subject&quot;. The same for tinymce.
*/</span>
<span class="co1">// Constants</span>
<span class="kw2">var</span> tinymceMaxChars <span class="sy0">=</span> <span class="nu0">500</span><span class="sy0">;</span>
&nbsp;
tinyMCE.<span class="me1">init</span><span class="br0">&#40;</span><span class="br0">&#123;</span>
    mode <span class="sy0">:</span> <span class="st0">&quot;exact&quot;</span><span class="sy0">,</span>
    elements <span class="sy0">:</span> <span class="st0">&quot;details&quot;</span><span class="sy0">,</span>
    setup <span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#123;</span>
        ed.<span class="me1">onInit</span>.<span class="me1">add</span><span class="br0">&#40;</span><span class="kw2">function</span><span class="br0">&#40;</span>ed<span class="br0">&#41;</span> <span class="br0">&#123;</span>
            tinymceAttachCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">;</span>
            tinymceUpdateCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        ed.<span class="me1">onChange</span>.<span class="me1">add</span><span class="br0">&#40;</span><span class="kw2">function</span><span class="br0">&#40;</span>ed<span class="sy0">,</span> l<span class="sy0">,</span> um<span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="co1">// Help Opera here, will help handle paste, but delayed</span>
            tinymceUpdateCharCounter<span class="br0">&#40;</span>ed<span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">&lt;/</span>script<span class="sy0">&gt;</span></pre></div><p>Вот и всё, если я упустил какие-либо события, или есть комментарии, прошу.</p>]]></description>
            <author> v@ua.fm (Vanav)</author>
            <comments>http://codeshit.com/post416</comments>
            <pubDate>Tue, 03 Feb 2009 02:18:54 GMT</pubDate>
            <guid isPermaLink="false">http://codeshit.com/post416</guid>
        </item>
        <item>
            <title>DataSheet</title>
            <link>http://codeshit.com/post413</link>
            <description><![CDATA[<p>При приходе вдохновления, я здесь подробнее о целях, миссии и видении блога. Пока хочу заметить, что это один из тематических блогов, главным является сайт <a href="http://vanav.com.ua">vanav.com.ua</a>.</p><p>Здесь я буду писать про IT, идеи, сравнения, анализ. Частично для себя, частично для тех, кому интересно.</p>]]></description>
            <author> v@ua.fm (Vanav)</author>
            <comments>http://codeshit.com/post413</comments>
            <pubDate>Wed, 19 Nov 2008 01:28:58 GMT</pubDate>
            <guid isPermaLink="false">http://codeshit.com/post413</guid>
        </item>
        <item>
            <title>Сравнение скорости PDO, ADOdb, MDB2, Zend_Db, ADOdb Lite и DbSimple</title>
            <link>http://codeshit.com/post397</link>
            <description><![CDATA[<p>В этом тестировании я решил выяснить, какова производительность различных современных классов абстракции от базы данных. В тестировании приняли участие:</p><ul><li><a href="http://php.net/pdo">PDO</a> (часть PHP 5); </li><li><a href="http://adodb.sourceforge.net">ADOdb 5</a> 5.04a (25.03.2008); </li><li><a href="http://pear.php.net/package/MDB2">MDB2</a> (часть PEAR, наследник и объединение PEAR DB и Metabase) 2.5.0b1 (15.03.2008); </li><li><a href="http://framework.zend.com/manual/ru/zend.db.html">Zend_Db</a> 1.5.2 (15.05.2008); </li><li><a href="http://adodblite.sourceforge.net">ADOdb Lite</a> 1.4.2 (12.01.2007); </li><li><a href="http://dklab.ru/lib/DbSimple/">DbSimple</a> v2 R226 SVN (17.09.2007).</li></ul><p>Версии&nbsp;&mdash; последние на текущий момент.</p><h3>Субъективность и объективность</h3><p>Я раньше в своих проектах использовал собственный класс доступа к БД, потом начал использовать DbSimple, потом перешёл на PHP 5 и стал больше склоняться к решениям без поддержки PHP 4 (например, <a href="http://framework.zend.com">Zend Framework</a>). DbSimple и ADOdb Lite уже больше года не обновлялись, это достаточно плохо с моей точки зрения. На большие и неиспользуемые объёмы кода смотрю подозрительно, удобство интерфейса уважаю. Всегда могу выдавить из тюбика зубной пасты ещё чуть-чуть. К счастью, это всё никакого отношения к тестированию не имеет.</p><h3>Методология</h3><p>Нагрузка на сервер создавалась при помощи <a href="http://www.hpl.hp.com/research/linux/httperf/">httperf</a> от Hewlett-Packard. Тесты запускались несколько раз, после чего отбрасывался худший результат, а остальные усреднялись. Сервер настроен на высокую производительность, программная часть следующая:</p><ul><li>FreeBSD 7.0-RELEASE-p1; </li><li>Apache 2.2.8; </li><li>PHP 5.2.6 with Suhosin-Patch 0.9.6.2; </li><li><a href="http://sysoev.ru/nginx/">nginx</a> 0.6.31; </li><li>MySQL 5.0.51a; </li><li>httperf 0.9.0; </li><li>APC 3.0.19.</li></ul><p>Логи отключены в Apache и nginx, вывода скриптов нет. Текст .htaccess:</p><div dir="ltr" class="source_code"><pre>php_value include_path "/home/test.com/data/library"
php_value display_errors off
php_value error_reporting 2047
php_value max_execution_time 200</pre></div><p>httperf создавал постоянную последовательную нагрузку без пауз в один поток, запускался следующим образом:</p><pre>
httperf --server=test.com --uri=/DBBenchmark/adodb.php --num-conns=1000
</pre><h3>Тест 1. Синтетический</h3><p>В этом тесте каждый класс исполнял 10&nbsp;000 раз цикл из 8 разных запросов, скрипт запускался 1 раз.</p><p>Что должен показать: в первую очередь, это та методология, которую обычно применяют при тестировании производительности классов. В основном нагружается лексический парсер запросов и кеш, если он есть у класса. Тест показать ничего не должен, на то она и синтетика.</p><p>Страницы в секунду&nbsp;&mdash; здесь это расчётный параметр для сравнения, он получается по формуле: 10000 / 8 / (время исполнения тестового скрипты), и отображает, сколько циклов по 8 запросов выполняет класс в секунду.</p>
<script type="text/javascript">
swfobject.embedSWF("/img/open-flash-chart.swf", "chart1", "500", "300", "9.0.0", "/img/expressInstall.swf", {"data-file": "/data/DBAL_test1.txt"});
</script>
<div style="text-align:center">
      <object id="chart1" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="500" height="300">
        <param name="movie" value="/img/open-flash-chart.swf" />
        <param name="flashvars" value="data-file=/data/DBAL_test1.txt" />
        <!--[if !IE]>-->
        <object type="application/x-shockwave-flash" data="/img/open-flash-chart.swf" width="500" height="300">
          <param name="flashvars" value="data-file=/data/DBAL_test1.txt" />
        <!--<![endif]-->
          <p>Установите <a href="http://www.adobe.com/go/getflashplayer">Adobe Flash player</a>, чтобы увидеть график.</p>
        <!--[if !IE]>-->
        </object>
        <!--<![endif]-->
      </object>
</div>
<p>Видно, что у MDB2 достаточно медленный парсер.</p><p>В виде таблицы:</p><table class="center td-right"><tr><th align="center">Класс</th><th>Время, с</th><th>Страниц/с</th></tr><tr><th>PDO</th><td>10,1</td><td>123,8</td></tr><tr><th>ADOdb</th><td>15,4</td><td>81,0</td></tr><tr><th>mdb2</th><td>42,4</td><td>29,5</td></tr><tr><th>Zend_Db</th><td>20,6</td><td>60,8</td></tr><tr><th>ADOdb Lite</th><td>17,2</td><td>72,5</td></tr><tr><th>DbSimple</th><td>32,8</td><td>38,1</td></tr></table><h3>Тест 2. Реальный</h3><p>В этом тесте каждый класс исполнял 2 раза цикл из 8 разных запросов. Скрипт запускался последовательно 1000 раз.</p><p>Что должен показать: как класс реагирует на настоящую нагрузку сайта. При этом будет сильно сказываться скорость загрузки самого класса и время подключения к базе.</p><p>Проводилось два разных измерения: сначала цикл с 8 запросами исполнялся 2 раза на страницу (всего 16 запросов), потом 1 раз (всего 8 запросов). Официальным результатом теста считается первый вариант с 2 проходами цикла. Он показывает также, насколько эффективен кеш подготовленных (<a href="http://dev.mysql.com/doc/refman/5.0/en/sqlps.html">prepared</a>) запросов в классе, и в реальном многомодульном приложении не всегда удаётся избежать повторных запросов. Тест с одним проходом цикла интересен для сравнения, позже я опишу результаты.</p><p>Ещё один проход теста был сделан с включённым opcode кешированием от <a href="http://pecl.php.net/apc">APC</a>. По некоторым сторонним тестам APC даёт очень хороший результат из всех opcode кешей (<a href="http://xcache.lighttpd.net">xCache</a>, <a href="http://eaccelerator.net">eAccelerator</a>, <a href="http://www.zend.com/en/products/platform/">Zend Platform</a>). Тестирование разных opcode кешей выходит за рамки данной статьи, поэтому я использовал только APC. Здесь результаты сильно варьировались от запуска к запуску, пока оптимизировался кеш, методология тестирования игнорирует самые плохие результаты и усредняет хорошие, поэтому можно получить достаточно устойчивую картину.</p><h4>Без APC</h4><p>Будет тестироваться запуск с двумя и одним циклом.</p><p>Что ожидается: в отличии от синтетического теста, здесь достаточно большую долю времени будет компилироваться код класса. Разница в выполнении двух и одного цикла нам покажет, насколько эффективен механизм кеширования скомпилированных запросов. Если разница будет небольшая, значит механизм эффективен, если большая&nbsp;&mdash; то неэффективен или отсутствует. Также по разнице можно примерно оценить, какая доля времени приходится на компиляцию собственно кода класса.</p><p>Результаты без APC:</p>
<script type="text/javascript">
swfobject.embedSWF("/img/open-flash-chart.swf", "chart2", "500", "500", "9.0.0", "/img/expressInstall.swf", {"data-file": "/data/DBAL_test2.txt"});
</script>
<div style="text-align:center">
      <object id="chart2" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="500" height="500">
        <param name="movie" value="/img/open-flash-chart.swf" />
        <param name="flashvars" value="data-file=/data/DBAL_test2.txt" />
        <!--[if !IE]>-->
        <object type="application/x-shockwave-flash" data="/img/open-flash-chart.swf" width="500" height="500">
          <param name="flashvars" value="data-file=/data/DBAL_test2.txt" />
        <!--<![endif]-->
          <p>Установите <a href="http://www.adobe.com/go/getflashplayer">Adobe Flash player</a>, чтобы увидеть график.</p>
        <!--[if !IE]>-->
        </object>
        <!--<![endif]-->
      </object>
</div>
<p>Итак, наиболее эффективные механизмы кеширования у Zend_Db и ADOdb, чуть менее у MDB2. PDO, ADOdb Lite и DbSimple показывают достаточно ощутимую разницу.</p><p>Самое интересное, ради чего и создавался этот тест, выяснить, какая из библиотек эффективнее. Как я и говорил, будем сравнивать результаты с двумя циклами. Итак, из библиотек с полным DBAL лидирует Zend_Db, второе место у ADOdb и замыкает MDB2. Среди более простых библиотек лидер ADOdb Lite, следом DbSimple. PDO, как единственный класс, написанный на Си, вне конкуренции.</p><h4>C APC</h4><p>Я выделил 128 Мб на кеш и поставил большой TTL, чтобы код всех классов мог поместиться одновременно и не выбрасываться до окончания тестов.</p><p>Что ожидается: наиболее тяжёлые библиотеки должны показать наибольший прирост скорости, потому что их код будет теперь доступен в скомпилированном виде.</p><p>Это несколько идеализированный тест, потому что я позволил библиотекам разместиться в кеше целиком и ничем их не ограничивал. Но все в равных условиях, тем не менее.</p>
<script type="text/javascript">
swfobject.embedSWF("/img/open-flash-chart.swf", "chart3", "500", "500", "9.0.0", "/img/expressInstall.swf", {"data-file": "/data/DBAL_test3.txt"});
</script>
<div style="text-align:center">
      <object id="chart3" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="500" height="500">
        <param name="movie" value="/img/open-flash-chart.swf" />
        <param name="flashvars" value="data-file=/data/DBAL_test3.txt" />
        <!--[if !IE]>-->
        <object type="application/x-shockwave-flash" data="/img/open-flash-chart.swf" width="500" height="500">
          <param name="flashvars" value="data-file=/data/DBAL_test3.txt" />
        <!--<![endif]-->
          <p>Установите <a href="http://www.adobe.com/go/getflashplayer">Adobe Flash player</a>, чтобы увидеть график.</p>
        <!--[if !IE]>-->
        </object>
        <!--<![endif]-->
      </object>
</div>
<p>Здесь меня ожидал сюрприз: не все библиотеки одинаково работают с APC, и некоторые результаты не так просто объяснить.</p><p>Во-первых, произошли перестановки среди лидеров: ADOdb поднимается на первое место среди полноценных DBAL, Zend_Db и MDB2 идут следом. Интересно, что кеширование позволило Zend_Db догнать DbSimple. В случае PDO, кешировать было практически нечего, кроме самих тестовых скриптов и файла с запросами. Мне очень понравилось как ADOdb работает с APC.</p><h3>Замечено интересного</h3><ol><li>При попытке тестирования стандартным <a href="http://httpd.apache.org/docs/2.0/programs/ab.html">ab</a>, я получал Segmentation fault от Apache 2. Выяснилось, что так ab реагирует на связку nginx + apache и ломает apache. Причём в комплекте Apache 1 ab рабочий. Но доверие к нему было подорвано, и я использовал httperf. </li><li>Очень сильно на скорость влияет include_path в PHP. Надо папку с библиотеками указывать первой, затем всё остальное. Это потому, что пути include внутри библиотек вида &laquo;Zend/Db/Table.php&raquo; и они начинают поиск по списку include_path. </li><li>При тестировании сам процеcc httperf создаёт достаточно большую нагрузку на CPU. Отчасти поэтому я сделал синтетический тест, где httperf нагрузки практически не создавал. Все классы находились в одинаковых условиях, поэтому большой проблемы это не вызвало, но иметь в виду нужно. </li><li>При тестировании с APC и первом запуске теста заметно значительное среднеквадратичное отклонение скорости коннекта, смотрим:</li></ol><table class="center"><tr><th align="center">Класс</th><th>Время соединения, мс</th></tr><tr><th>PDO</th><td>min 0.7 avg 3.9 max 201.8 median 3.5 stddev 6.3</td></tr><tr><th>ADOdb</th><td>min 4.7, avg 7.2, max 198.9, median 6.5, stddev 10.5</td></tr><tr><th>mdb2</th><td>min 8.9, avg 11.5, max 125.9, median 10.5, stddev 4.6</td></tr><tr><th>Zend_Db</th><td>min 8.9, avg 10.0, max 101.8, median 9.5, stddev 3.5</td></tr><tr><th>ADOdb Lite</th><td>min 1.3, avg 6.0, max 58.9, median 5.5, stddev 2.1</td></tr><tr><th>DbSimple</th><td>min 6.0, avg 9.8, max 134.3, median 9.5, stddev 7.3</td></tr></table><p>Это косвенно показывает, как заполняется кеш opcode в APC. После окончания тестов использование APC этими классами было следующим:</p><pre>
Cached Files: 583 ( 45.0 MBytes)
Hits: 230198
Misses: 586
Request Rate (hits, misses): 138.94 cache requests/second
Hit Rate: 138.59 cache requests/second
Miss Rate: 0.35 cache requests/second
Insert Rate: 0.35 cache requests/second
Cache full count: 0
</pre><h3>Тексты тестов</h3><p>pdo.php:</p><div dir="ltr" class="source_code"><div class="source_code_lang">PHP</div><pre class="php source_code_text"><span class="kw2">&lt;?php</span>
<span class="kw1">require_once</span> <span class="st_h">'sql.php'</span><span class="sy0">;</span>
<span class="re0">$db</span> <span class="sy0">=</span> <span class="kw2">new</span> PDO<span class="br0">&#40;</span><span class="st0">&quot;mysql:dbname=blog;host=127.0.0.1&quot;</span><span class="sy0">,</span> <span class="st0">&quot;blog&quot;</span><span class="sy0">,</span> <span class="st0">&quot;password&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&lt;</span> <span class="re0">$count</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">++</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$select</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$rs</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">fetchAll</span><span class="br0">&#40;</span>PDO<span class="sy0">::</span><span class="me2">FETCH_ASSOC</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$rs</span> <span class="kw1">as</span> <span class="re0">$r</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$update</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$z</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div><p>adodb.php:</p><div dir="ltr" class="source_code"><div class="source_code_lang">PHP</div><pre class="php source_code_text"><span class="kw2">&lt;?php</span>
<span class="kw1">require_once</span> <span class="st_h">'adodb5/adodb.inc.php'</span><span class="sy0">;</span>
<span class="kw1">require_once</span> <span class="st_h">'sql.php'</span><span class="sy0">;</span>
<span class="re0">$ADODB_FETCH_MODE</span> <span class="sy0">=</span> ADODB_FETCH_ASSOC<span class="sy0">;</span>
<span class="re0">$db</span> <span class="sy0">=</span> NewADOConnection<span class="br0">&#40;</span><span class="st0">&quot;mysql://blog:password@127.0.0.1/blog&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span> 
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&lt;</span> <span class="re0">$count</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">++</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$select</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$rs</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">Execute</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$rs</span> <span class="kw1">as</span> <span class="re0">$r</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$update</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$z</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">Execute</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div><p>mdb2.php:</p><div dir="ltr" class="source_code"><div class="source_code_lang">PHP</div><pre class="php source_code_text"><span class="kw2">&lt;?php</span>
<span class="kw1">require_once</span> <span class="st_h">'PEAR/MDB2.php'</span><span class="sy0">;</span>
<span class="kw1">require_once</span> <span class="st_h">'sql.php'</span><span class="sy0">;</span>
<span class="re0">$db</span> <span class="sy0">=&amp;</span> MDB2<span class="sy0">::</span><span class="me2">connect</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span>
    <span class="st_h">'phptype'</span>  <span class="sy0">=&gt;</span> <span class="st_h">'mysql'</span><span class="sy0">,</span>
    <span class="st_h">'hostspec'</span> <span class="sy0">=&gt;</span> <span class="st_h">'127.0.0.1'</span><span class="sy0">,</span>
    <span class="st_h">'username'</span> <span class="sy0">=&gt;</span> <span class="st_h">'blog'</span><span class="sy0">,</span>
    <span class="st_h">'password'</span> <span class="sy0">=&gt;</span> <span class="st_h">'password'</span><span class="sy0">,</span>
    <span class="st_h">'database'</span> <span class="sy0">=&gt;</span> <span class="st_h">'blog'</span>
<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">setFetchMode</span><span class="br0">&#40;</span>MDB2_FETCHMODE_ASSOC<span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&lt;</span> <span class="re0">$count</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">++</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$select</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$rs</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">fetchAll</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$rs</span> <span class="kw1">as</span> <span class="re0">$r</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$update</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$z</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div><p>zend_db.php:</p><div dir="ltr" class="source_code"><div class="source_code_lang">PHP</div><pre class="php source_code_text"><span class="kw2">&lt;?php</span>
<span class="kw1">require_once</span> <span class="st_h">'Zend/Db/Adapter/Pdo/Mysql.php'</span><span class="sy0">;</span>
<span class="kw1">require_once</span> <span class="st_h">'sql.php'</span><span class="sy0">;</span>
<span class="re0">$db</span> <span class="sy0">=</span> <span class="kw2">new</span> Zend_Db_Adapter_Pdo_Mysql<span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span>
    <span class="st_h">'host'</span>     <span class="sy0">=&gt;</span> <span class="st_h">'127.0.0.1'</span><span class="sy0">,</span>
    <span class="st_h">'username'</span> <span class="sy0">=&gt;</span> <span class="st_h">'blog'</span><span class="sy0">,</span>
    <span class="st_h">'password'</span> <span class="sy0">=&gt;</span> <span class="st_h">'password'</span><span class="sy0">,</span>
    <span class="st_h">'dbname'</span>   <span class="sy0">=&gt;</span> <span class="st_h">'blog'</span>
<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&lt;</span> <span class="re0">$count</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">++</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$select</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$rs</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">fetchAll</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$rs</span> <span class="kw1">as</span> <span class="re0">$r</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$update</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$stmt</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="re0">$z</span> <span class="sy0">=</span> <span class="re0">$stmt</span><span class="sy0">-&gt;</span><span class="me1">rowCount</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div><p>adodb_lite.php:</p><div dir="ltr" class="source_code"><div class="source_code_lang">PHP</div><pre class="php source_code_text"><span class="kw2">&lt;?php</span>
<span class="kw1">require_once</span> <span class="st_h">'adodb_lite/adodb.inc.php'</span><span class="sy0">;</span>
<span class="kw1">require_once</span> <span class="st_h">'sql.php'</span><span class="sy0">;</span>
<span class="re0">$ADODB_FETCH_MODE</span> <span class="sy0">=</span> ADODB_FETCH_ASSOC<span class="sy0">;</span>
<span class="re0">$db</span> <span class="sy0">=</span> NewADOConnection<span class="br0">&#40;</span><span class="st0">&quot;mysql://blog:password@127.0.0.1/blog&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span> 
&nbsp;
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&lt;</span> <span class="re0">$count</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">++</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$select</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$rs</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">Execute</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">GetAll</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$rs</span> <span class="kw1">as</span> <span class="re0">$r</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$update</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$z</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">Execute</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div><p>dbsimple.php:</p><div dir="ltr" class="source_code"><div class="source_code_lang">PHP</div><pre class="php source_code_text"><span class="kw2">&lt;?php</span>
<span class="kw1">require_once</span> <span class="st_h">'DbSimple/Generic.php'</span><span class="sy0">;</span>
<span class="kw1">require_once</span> <span class="st_h">'sql.php'</span><span class="sy0">;</span>
<span class="re0">$db</span> <span class="sy0">=</span> DbSimple_Generic<span class="sy0">::</span><span class="me2">connect</span><span class="br0">&#40;</span><span class="st_h">'mysql://blog:password@127.0.0.1/blog'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&lt;</span> <span class="re0">$count</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">++</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$select</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$rs</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$rs</span> <span class="kw1">as</span> <span class="re0">$r</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$update</span> <span class="kw1">as</span> <span class="re0">$s</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re0">$z</span> <span class="sy0">=</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">query</span><span class="br0">&#40;</span><span class="re0">$s</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div><p>sql.php:</p><div dir="ltr" class="source_code"><div class="source_code_lang">PHP</div><pre class="php source_code_text"><span class="kw2">&lt;?php</span>
<span class="re0">$select</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$select</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;SELECT uni_files.id, name, disk_filename, icon, size FROM uni_files INNER JOIN uni_filetypes ON uni_files.filetype_id=uni_filetypes.id WHERE post_id='167' AND blog_id='3'&quot;</span><span class="sy0">;</span>
<span class="re0">$select</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;SELECT p.id, nick, p.poster_id, creation_date, post_title, post_text, CEILING((rgt - lft - 1) / 2) AS comment_count FROM uni_posts p LEFT OUTER JOIN uni_users ON p.poster_id = uni_users.id LEFT OUTER JOIN uni_posts_keywords pk ON p.id = pk.post_id AND pk.keyword_id='34' WHERE parent_post_id IS NULL AND NOT(keyword_id IS NOT NULL AND p.poster_id!='3') AND blog_id='3' ORDER BY creation_date DESC LIMIT 0, 4&quot;</span><span class="sy0">;</span>
<span class="re0">$select</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;SELECT EXTRACT(DAY FROM creation_date) AS day, COUNT(*) AS day_count FROM uni_posts WHERE parent_post_id IS NULL AND blog_id='3' AND EXTRACT(YEAR FROM creation_date)=2008 AND EXTRACT(MONTH FROM creation_date)=6 GROUP BY day&quot;</span><span class="sy0">;</span>
<span class="re0">$select</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;SELECT uni_posts.id, nick, poster_id, creation_date, post_title, post_text, post_text_wiki, active_text FROM uni_posts LEFT OUTER JOIN uni_users ON uni_posts.poster_id = uni_users.id WHERE uni_posts.id='123' AND blog_id='3'&quot;</span><span class="sy0">;</span>
<span class="re0">$select</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;SELECT uni_posts.id, rgt, nick, creation_date, post_title, post_text FROM uni_posts LEFT OUTER JOIN uni_users ON uni_posts.poster_id = uni_users.id LEFT OUTER JOIN uni_posts_keywords pk ON uni_posts.id = pk.post_id AND pk.keyword_id='34' WHERE NOT(keyword_id IS NOT NULL AND poster_id!='3') AND uni_posts.id='123' AND blog_id='3'&quot;</span><span class="sy0">;</span>
<span class="re0">$select</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;SELECT p1.id, nick, p1.creation_date, p1.modification_date, p1.post_title, p1.post_text, p1.parent_post_id, p2.post_title AS parent_post_title, p3.post_title AS answer_parent_post_title, p3.id AS answer_parent_post_id FROM uni_posts p1 LEFT OUTER JOIN uni_users ON p1.poster_id=uni_users.id LEFT JOIN uni_posts p2 ON p1.parent_post_id=p2.id LEFT JOIN uni_posts p3 ON p2.root_post_id=p3.id LEFT OUTER JOIN uni_posts_keywords pk ON p1.id = pk.post_id AND pk.keyword_id='34' WHERE NOT(keyword_id IS NOT NULL AND p1.poster_id!='3') AND p1.blog_id='3' ORDER BY modification_date DESC LIMIT 0, 10&quot;</span><span class="sy0">;</span>
<span class="re0">$select</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;select productid,productname,unitsinstock,unitprice from products&quot;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$update</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$update</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;UPDATE uni_users SET login_date=NOW() WHERE id='1'&quot;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$count</span> <span class="sy0">=</span> <span class="nu0">2</span><span class="sy0">;</span></pre></div><h3>Итоги</h3><p>Сравним скорость без APC и с APC. В обоих случаях выполнялось 2 цикла.</p>
<script type="text/javascript">
swfobject.embedSWF("/img/open-flash-chart.swf", "chart4", "500", "500", "9.0.0", "/img/expressInstall.swf", {"data-file": "/data/DBAL_test4.txt"});
</script>
<div style="text-align:center">
      <object id="chart4" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="500" height="500">
        <param name="movie" value="/img/open-flash-chart.swf" />
        <param name="flashvars" value="data-file=/data/DBAL_test4.txt" />
        <!--[if !IE]>-->
        <object type="application/x-shockwave-flash" data="/img/open-flash-chart.swf" width="500" height="500">
          <param name="flashvars" value="data-file=/data/DBAL_test4.txt" />
        <!--<![endif]-->
          <p>Установите <a href="http://www.adobe.com/go/getflashplayer">Adobe Flash player</a>, чтобы увидеть график.</p>
        <!--[if !IE]>-->
        </object>
        <!--<![endif]-->
      </object>
</div>
<p>Табличка для дигиталов:</p><table class="center td-right"><tr><th align="center">Класс</th><th>2 цикла</th><th>1 цикл</th><th>2 цикла + APC</th><th>1 цикл + APC</th></tr><tr><th>PDO</th><td>262,6</td><td>355,0</td><td>272,9</td><td>370,1</td></tr><tr><th>ADOdb</th><td>47,7</td><td>49,0</td><td>155,9</td><td>205,65</td></tr><tr><th>mdb2</th><td>39,0</td><td>47,9</td><td>88</td><td>134,8</td></tr><tr><th>Zend_Db</th><td>57,0</td><td>62,5</td><td>101,8</td><td>127,75</td></tr><tr><th>ADOdb Lite</th><td>124,2</td><td>153,4</td><td>166,9</td><td>231,9</td></tr><tr><th>DbSimple</th><td>80,8</td><td>101,1</td><td>102,3</td><td>164,6</td></tr></table><p>Одной из целей этого тестирования было опробовать новый класс Zend_Db и посмотреть его в сравнении. Тесты показали, что Zend_Db достойный конкурент, и этот класс вполне можно использовать в любых комбинациях. Одним из сюрпризов тестирования оказалась большая скорость ADOdb Lite при немаленьком объёме кода. Если ему простить некоторые интерфейсные огрехи, то можно рекомендовать там, где не нужен полный DBAL. В некотором роде аутсайдером оказался MDB2, он работает медленнее остальных. Отчасти это можно объяснить тем, что он построен на основе абстракции от диалектов SQL <a href="http://www.phpclasses.org/browse/package/20.html">Metabase</a>.</p><h3>Субъективные итоги</h3><p>Я ожидал большего от Zend_Db, но в то же время по результатам теста я не буду отказываться от этого класса, он показал себя вполне достойно. Я приятно удивлён скоростью ADOdb, и шокирован скоростью ADOdb Lite, почему-то слово ADOdb у меня ассоциировалось с чем-то большим и неповоротливым. Да, у него есть недостатки в интерфейсе, но скорость на уровне, это радует. DbSimple по непонятной причине оказался медленнее ADOdb Lite, хотя в нём всего 2 файла. Видимо разработчик не ставил целью скорость.</p><p>Для себя я сделал вывод: в новых проектах я буду использовать Zend_Db и APOdb Lite (если устроит его интерфейс). Когда понадобится что-то совсем простое, то либо свой класс, либо DbSimple. MDB2 стоит использовать, когда на самом деле нужна независимость от диалектов SQL, что на практике достаточно редко.</p><h3>Ссылки</h3><ol><li>В одном из запросов использовалась БД из <a href="http://phplens.com/lens/adodb/">давнего теста ADBL</a>. </li><li>Более популярно про <a href="http://devzone.zend.com/article/1367-Zend-Framework-Hidden-Gems-Zend_Db">Zend_Db с примерами</a>. </li><li>В Zend_Db есть ещё <a href="http://framework.zend.com/issues/browse/ZF-1398">небольшой нюанс</a>, который замедляет исполнение SELECT запросов в MySQL. После его исправления надо будет повторить тесты.</li></ol>]]></description>
            <author> v@ua.fm (Vanav)</author>
            <comments>http://codeshit.com/post397</comments>
            <pubDate>Mon, 23 Jun 2008 20:07:08 GMT</pubDate>
            <guid isPermaLink="false">http://codeshit.com/post397</guid>
        </item>
        <item>
            <title>MS SQL и ограничение длины строк</title>
            <link>http://codeshit.com/post96</link>
            <description><![CDATA[<p>На собственном опыте обнаружил странное поведение своего MS SQL сервера. Если в таблицу добавляется значение типа <code>(N)VARCHAR</code> или <code>(N)CHAR</code>, и это значение длиннее ограничения, то текст не обрезался, транзакция не проходила, и IIS вываливался с ошибкой 500, чем вгонял в недоумение админов.</p><div dir="ltr" class="source_code"><pre>Server: Msg 8152, Level 16, State 9, Line 1
String or binary data would be truncated.
The statement has been terminated.</pre></div><p>Выяснилось, что виной этому была опция <code>ANSI_WARNINGS</code>. В документации говорят, она по-умолчанию выключена, но это зависит от настроек конкретного сервера.</p><p>Если её выключить, то строки будут сами обрезаться, и запросы будут успешно исполняться.</p><p>Есть два способа отключения: </p><ol><li>Выполнить в начале запрос: <code>SET ANSI_WARNINGS OFF</code> </li><li>Для всех соединений одновременно, используя хранимую процедуру: <code>sp_dboption 'DatabaseName', 'ANSI warnings', 'FALSE'</code>.</li></ol>]]></description>
            <author> v@ua.fm (Vanav)</author>
            <comments>http://codeshit.com/post96</comments>
            <pubDate>Tue, 25 Jul 2006 09:45:41 GMT</pubDate>
            <guid isPermaLink="false">http://codeshit.com/post96</guid>
        </item>
        <item>
            <title>Простейший AJAX с большой совместимостью</title>
            <link>http://codeshit.com/post95</link>
            <description><![CDATA[<p>Итак, мы хотим послать небольшую строчку на сервер, получить от сервера ответ и показать пользователю. Что-то похожее на AJAX, но как можно проще, без килобайтных библиотек, вопросов совместимости и изучения XML-парсеров разных браузеров. Более того, хотелось бы чтобы способ был совместим с как можно большим числом браузеров, и занял не больше 10 минут на написание с нуля. Наше решение будет совместимо с IE5.0+, Mozilla 1.7+, FireFox 1.0+ и Opera 7.3+.</p>

<p>Например, понадобилось сделать быструю проверку введённого почтового адреса пользователя в базе на сервере, и если такой адрес существует&nbsp;&mdash; сообщить об ошибке. Но открывать для этого каждый раз новую страницу очень не хотелось бы: во-первых, адресов может быть много, и это долго, во-вторых, это прервёт заполнение формы, и придётся вернуть форму пользователю целиком.</p>

<p>Итак, начнём с кода.</p>

<p>Страница, с которой происходит запрос. Здесь расположена форма, которую заполняет пользователь:</p>


<pre class="javascript"><code class="javascript">default.asp:

&lt;script type=&quot;text/javascript&quot;&gt;
  function test(email) {
    href = 'tester.asp?email=' + encodeURI(document.getElementById('in_email').value);
    span = document.body.appendChild(document.createElement(&quot;SPAN&quot;));
    span.style.display = 'none';
    span.innerHTML = 'text&lt;s'+'cript&gt;&lt;/'+'script&gt;';
    setTimeout(function() {
      var s = span.getElementsByTagName(&quot;script&quot;)[0];
      s.language = &quot;JavaScript&quot;;
      if (s.setAttribute)
        s.setAttribute('src', href); else
        s.src = href;
    }, 10);
  }
&lt;/script&gt;

&lt;input type=&quot;text&quot; id=&quot;in_email&quot; value=&quot;test@example.com&quot; /&gt;

&lt;a href=&quot;javascript:test()&quot;&gt;Тест&lt;/a&gt;
&lt;div id=&quot;result&quot;&gt;&lt;/div&gt;</code></pre>

<p>Скрипт, который получает своим аргументом проверяемый адрес, и отвечает посредством создания JavaScript кода:</p>


<pre class="javascript"><code class="javascript">tester.asp:

&lt;%@LANGUAGE = JScript %&gt;
&lt;%
  var email = decodeURI(Request.QueryString(&quot;email&quot;));
  // Здесь можно добавить проверку на уникальность адреса в базе пользователей
  var filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
  if (filter.test(email))
    msg = &quot;Правильный адрес.&quot;;
  else
    msg = &quot;Неправильный адрес!&quot;;
  Response.Write('document.getElementById(&quot;result&quot;).innerHTML = &quot;' + msg + '&quot;');
%&gt;</code></pre>

<p>Обратим внимание, что первый скрипт может быть простым HTML, без ASP или PHP. Второй же скрипт может быть на любом серверном языке ASP или PHP, но возвращать в браузер должен синтаксически правильный JavaScript. Собственно, послеполучения ответа браузер этот JavaScript и будет исполнять.</p>

<p>В нашем примере после нажатия кнопки создаётся новый невидимый элемент span в конце документа, в него добавляется script и источником JS-скрипта ставится ASP или PHP скрипт на сервере. Этому скрипту на сервере передаётся интересующий нас аргумент, он его проверяет, и выдаёт ответ в виде правильного JavaScript. Браузер не замечает, что работал скрипт на сервере, и получает сразу правильный JavaScript, который тут же и исполняет.</p>

<p>Итак, применять это можно в тех случаях, когда нужен простой и быстрый AJAX, в то же время максимально совместимый. Собственно, простота и совместимость&nbsp;&mdash; его главное преимущество перед XMLRequest.</p>

<p>Начальная идея&nbsp;&mdash; dklab, за что огромное спасибо.</p>

]]></description>
            <author> v@ua.fm (Vanav)</author>
            <comments>http://codeshit.com/post95</comments>
            <pubDate>Tue, 25 Jul 2006 06:44:49 GMT</pubDate>
            <guid isPermaLink="false">http://codeshit.com/post95</guid>
        </item>
        <item>
            <title>Подтверждение действий</title>
            <link>http://codeshit.com/post74</link>
            <description><![CDATA[<p>Пользователь любит кликать. Если сделать кнопку с большой надписью "<strong>Не нажимать!</strong>", она будет нажата первой.</p>

<p>Поэтому на критические действия вроде удаления файлов нужно ставить подтверждения. Рассмотрим, как это можно проще всего сделать на JavaScript:</p>


<pre class="html"><code class="html">
&lt;a href=&quot;/post63/delete&quot;
  onclick=&quot;return confirm('Сообщение &amp;laquo;HelloWorld&amp;raquo; будет удалено. Удалить?')&quot;&gt;
  Удалить&lt;/a&gt;
</code></pre>

<p><em>confirm()</em> выведет стандартное диалоговое окно Windows, на котором на будет две кнопки: OK и Отмена (язык надписей соответствует языку Windows).</p>

<p>В сообщении чётко указывается два момента:</p>

<ol>
    <li>что именно будет удалено. Пользователь должен проверить, то ли он на самом деле удаляет;</li>
    <li>чёткий короткий вопрос, который подразумевает однозначный ответ.</li>
</ol>

<p>Если JavaScript будет отключён у пользователя, ссылка всё-равно будет работать, правда без подтверждения. Но поскольку процент таких пользователей мал, это допустимо.</p>

]]></description>
            <author> v@ua.fm (Vanav)</author>
            <comments>http://codeshit.com/post74</comments>
            <pubDate>Wed, 26 Apr 2006 06:20:35 GMT</pubDate>
            <guid isPermaLink="false">http://codeshit.com/post74</guid>
        </item>
        <item>
            <title>Windows Mobile Edition 2005 и борьба с ней</title>
            <link>http://codeshit.com/post72</link>
            <description><![CDATA[<p>С такой же радостью откатился обратно.</p>

<p>Во-первых, заметно увеличелись тормоза и (по отзывам) уменьшилось время работы от аккумулятора, а во-вторых оно умудрялось спонтанно зависать и не включаться без софт-ресета. Возможно не подружилось с единственной установленной программой (Pocket Player), но даже одного из этих условий достаточно, чтобы считать эксперимент удавшимся, но не более чем экспериментом.</p>

<p>Вывод: ждём исправления ошибок и улучшения совместимости, а также, возможно, более мощной машинки. Хотя какой бы мощность не была, уменьшать скорость одной только новой версией ОС &mdash; это занятие на мазохиста апгрейдинговых технологий. Так что пока явных плюсов WM5 не показала. Вот когда появится софт только под WM5, придётся задуматься...</p>

]]></description>
            <author> v@ua.fm (Vanav)</author>
            <comments>http://codeshit.com/post72</comments>
            <pubDate>Fri, 21 Apr 2006 21:28:02 GMT</pubDate>
            <guid isPermaLink="false">http://codeshit.com/post72</guid>
        </item>
        <item>
            <title>Windows Mobile Edition 2005 и борьба с ней</title>
            <link>http://codeshit.com/post66</link>
            <description><![CDATA[<p>Наконец-то созрел для обновления своей машинки. Всё прошло за 10 минут, гладко и безболезненно. Но были некоторые проблемы:</p>

<ol>
    <li>ActiveSync вообще отказался видеть устройство. Решение было в том, что отныне PocketPC использует DHCP для получения своего IP, и поэтому нужно проверить, получило устройство IP или нет (ipconfig /all), и если нет &mdash; включить сервис DHCP (который был у меня отключён как до сих пор неиспользуемый).</li>
    <li>ActiveSync настолькой системы магическим образом переключился в настоящий режим 4.1 (до этого он использовал устаревший код 3.8), при этом у него исчезли настройки периода синхронизации, и он начал синхронизировать устройства каждые 5 минут.</li>
</ol>

<p>Пока приличного решения не существует, но есть удобные сторонние программки для отключения ActiveSync. В частности рекомендую <a href="http://micrologics.co.uk/library/ast/">ActiveSyncToggle</a>, которая умеет работать с коммандной строкой и грамотно отключает все ипостасии ActiveSync.</p>

<p>В общем впечатления от ОС пока положительные: давно мечтал управлять всем при помощи кнопок, а две совтовые клавиши, отображаемые в WM5, этому очень способствуют.</p>

<p>PS. Да, это первый пост, вы ничего не пропустили: здравствуйте.</p>

]]></description>
            <author> v@ua.fm (Vanav)</author>
            <comments>http://codeshit.com/post66</comments>
            <pubDate>Sun, 16 Apr 2006 21:03:20 GMT</pubDate>
            <guid isPermaLink="false">http://codeshit.com/post66</guid>
        </item>
    </channel>
</rss>
