<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Антон Шевчук &#187; PHP</title>
	<atom:link href="http://anton.shevchuk.name/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://anton.shevchuk.name</link>
	<description>Web-разработчик</description>
	<lastBuildDate>Wed, 01 Feb 2012 12:54:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Поговорим о PHP</title>
		<link>http://anton.shevchuk.name/php/about-php/</link>
		<comments>http://anton.shevchuk.name/php/about-php/#comments</comments>
		<pubDate>Thu, 17 Nov 2011 10:59:50 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1987</guid>
		<description><![CDATA[Хотел было назвать статью &#171;профессиональное PHP программирование&#187;, но по факту &#8212; это лишь &#171;заметки бывалого&#187;, которые будут полезны начинающим разработчикам, хотя, возможно, привлеку внимание и &#171;старшего&#187; поколения ;) Инструментарий Хороший мастер работает лишь со своим инструментом, и сильно расстраивается когда оного нет под рукой, так и я &#8212; всё своё ношу с собой: IDE &#8212; [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/11/tools.jpg" alt="" title="tools" width="420" height="351" class="aligncenter size-full wp-image-2006" /><br />
Хотел было назвать статью &laquo;профессиональное PHP программирование&raquo;, но по факту &mdash; это лишь &laquo;заметки бывалого&raquo;, которые будут полезны начинающим разработчикам, хотя, возможно, привлеку внимание и &laquo;старшего&raquo; поколения ;)<br />
<span id="more-1987"></span></p>
<h2>Инструментарий</h2>
<p>Хороший мастер работает лишь со своим инструментом, и сильно расстраивается когда оного нет под рукой, так и я &mdash; всё своё ношу с собой:</p>
<ul>
<li>IDE &mdash; PHP Storm</li>
<li>Менеджер БД &mdash; SQLYog</li>
<li>Всяко-разно &mdash; notepad++, putty, winscp&#8230;</li>
</ul>
<h3>PHP Storm</h3>
<p>IDE решает многие задачи и проблемы, которые постоянно возникают перед разработчиком. Мой выбор остановился на <a href="http://www.jetbrains.com/phpstorm/">PHP Storm</a>, а до того я успел попробовать Zend Studio, Eclipse, Aptana, NetBeans и еще несколько о которых и не вспомню. Что же хорошего в данной IDE:</p>
<ul>
<li>java &mdash; т.е. нам практически любая платформа по плечу</li>
<li>автодополнение &mdash; очень адекватное, и приучает к документированию кода</li>
<li>автосохранение &mdash; забудьте про ctrl+s</li>
<li>автозаливка (FTP/SFTP)</li>
<li>автозаливка после комита</li>
<li>исправление ошибок</li>
<li>поддержка VCS</li>
<li>рефакторинг</li>
<li>поиск и замена по файлам &#8211; работает просто отлично, и частенько меня выручала</li>
<li>отличная поддержка HTML/CSS/JS</li>
<li><a href="http://code.google.com/p/zen-coding/">Zen coding</a> из коробки (советую таки узнать что это ;)</li>
</ul>
<p>Но без недостатков не бывает:</p>
<ul>
<li>java &mdash; т.е. любит память, и IDE приходится время от времени перезапускать &mdash; где-то раз в неделю</li>
<li>цена &mdash; сейчас, со скидкой, это <a href="http://www.jetbrains.com/phpstorm/buy/">$50 (до 27-го ноября)</a></li>
</ul>
<p>Отдельно оговорюсь насчет цены &mdash; у меня купленная версия, но так же есть Open Source лицензия, ее тоже можно получить, но только надо запастись терпением (как можно догадаться &mdash; у меня терпения не хватило). Насчет же NetBeans &mdash; задолбал он своей нестабильной работой и такими же обновлениями.</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/11/phpstorm.jpg" alt="" title="phpstorm" width="400" height="300" class="aligncenter size-full wp-image-2000" /></p>
<p>В качестве заключения: мои слова ничего не значат, верьте своим глазам &mdash; попробуйте триал, благо целого месяца хватит на распробовать ;)</p>
<p>Кстати, если кому нравится <a href="http://dl.dropbox.com/u/27453635/PHPStorm/screen-php.png">моя</a> <a href="http://dl.dropbox.com/u/27453635/PHPStorm/screen-js.png">тёмная</a> <a href="http://dl.dropbox.com/u/27453635/PHPStorm/screen-php.png">схема</a> для PHP Storm, то её можно забрать с <a href="http://dl.dropbox.com/u/27453635/PHPStorm/config.zip">dropbox&#8217;a</a></p>
<h2>SQLYog</h2>
<p>Для работы с MySQL использую <a href="http://www.webyog.com/en/downloads.php">SQLYog</a>, что же в нём такого хорошего:</p>
<ul>
<li>автодополнение</li>
<li>профайлер</li>
<li>автоформатирование запросов</li>
<li>синхронизация структуры БД и данных при необходимости</li>
<li>SSH туннель &mdash; если у вас есть SSH, то SQLYog в игре</li>
<li>HTTP туннель &mdash; хотя хватит доступа и к FTP</li>
<li>отлично работает под wine (по правде говоря есть огрехи, но совсем мелкие)</li>
</ul>
<p>Ну не без ложки дёгтя:</p>
<ul>
<li>цена &mdash; от <a href="http://www.webyog.com/en/buy.php">$69</a>, но есть триальная и урезанная фришная версия</li>
</ul>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/11/sqlyog.png" alt="" title="sqlyog" width="113" height="90" class="alignleft size-full wp-image-2001" /></p>
<p>Опять же, есть триал &mdash; пробуйте, и забудьте про phpMyAdmin как про страшный сон.</p>
<h2>Ещё чуть-чуть</h2>
<p>Ой, ну дальше дело вкуса:</p>
<ul>
<li><a href="http://notepad-plus-plus.org/">Notepad++</a> &mdash; всегда на подхвате, открывает большие файлы, понимает все кодировки, с плагином hex-редактор</li>
<li><a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html">PuTTY</a> &mdash; без него никак, (<a href="http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter8.html#pubkey-puttygen">puttygen</a> рулит кстати)</li>
<li><a href="http://www.ghisler.com/">TotalCmd</a> &#8211; файловый менеджер + FTP клиентб недавно вышла 8-ая версия (beta), не купил, жмакаю раз в неделю кнопку с циферкой</li>
<li><a href="http://winscp.net/eng/index.php">WinScp</a> &#8211; SFTP клиент (файловый менеджер over SSH)</li>
</ul>
<p>Да, я работаю на винде, набор инструментов для Linux и Мас отличается лишь набором дополнительных тулзов&#8230;</p>
<h2>Фишки PHP и 5.3 в частности</h2>
<h3>phpDocumentor</h3>
<p>Я уже упоминал о крутом автодополнение в IDE, тук вот, чтобы этого добиться необходимо описывать классы, методы и функции используя doc comment&#8217;ы:</p>
<pre class="brush: php; title: ; notranslate">
/**
 * @property integer $id
 * @property string $login
 * ...
 */
class Row
{

}
</pre>
<p>В результате IDE будет подсказывать нам всё что нужно:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/11/phpDocumentor-1.png" alt="" title="phpDocumentor" width="348" height="110" class="aligncenter size-full wp-image-1988" /></p>
<p>Еще примерчик:</p>
<pre class="brush: php; title: ; notranslate">
/**
 * @return Users\Row
 */
function getUser()
{
     return Registry::get(&quot;user&quot;);
}

/* @var Users\Row $user */
$user = Registry::get(&quot;user&quot;);
</pre>
<p>В IDE похожий результат:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/11/phpDocumentor-2.png" alt="" title="phpDocumentor" width="348" height="88" class="aligncenter size-full wp-image-1989" /></p>
<p>Кроме бенефитов для IDE есть еще возможность сгенерировать техническую документацию используя PHPDocumentor:</p>
<pre class="brush: php; title: ; notranslate">
/**
 * &lt;code&gt;
 * $this-&gt;getByLoginOrPasswordAndEmail(
 *      'vasya', md5(123456), 'vasya@mail.ru'
 *  )
 * &lt;/code&gt;
 */
function getByColumnsFinder()
{
    /*...*/
}
</pre>
<p>Обернётся в:<br />
<img src="http://anton.shevchuk.name/wp-content/uploads/2011/11/phpDocumentor-3.png" alt="" title="phpDocumentor-3" width="526" height="186" class="aligncenter size-full wp-image-1990" /></p>
<h3>Tips &#038; Tricks: Fixme Comment</h3>
<p><a href="http://c2.com/cgi/wiki?FixmeComment">Заметки</a> на будущее прям в коде:</p>
<pre class="brush: php; title: ; notranslate">
// TODO: do something with some function
// FIXME: hands
// XXX: may be broken
</pre>
<p>IDE нынче умные пошли, и все такие заметки соберут до кучи:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/11/TODO.png" alt="" title="TODO" width="526" height="186" class="aligncenter size-full wp-image-1993" /></p>
<h3>Tips &#038; Tricks: о include и require</h3>
<p>А вы смотрели что возвращают функции include и require? А если в подключаемом файле используется return?</p>
<pre class="brush: php; title: ; notranslate">
$config = require 'config.php';

&lt;? // config.php
    return array(
        'DB' =&gt; array(...)
    );
</pre>
<p>Достаточно универсальный способ для подключения конфигурационных файлов.</p>
<h3>Tips &#038; Tricks: о фильтрации данных</h3>
<p>На дворе уже закат PHP 5.2, а фильтры не используем:</p>
<pre class="brush: php; title: ; notranslate">
// return string or false
filter_var('bob@example', FILTER_VALIDATE_EMAIL);
filter_var('bob@example.com', FILTER_VALIDATE_EMAIL);

// return string or false
filter_var('http://example.com', FILTER_VALIDATE_URL);
</pre>
<p>По теме:</p>
<ul>
<li><a href="http://www.php.net/manual/en/function.filter-var.php">http://www.php.net/manual/en/function.filter-var.php</a></li>
<li><a href="http://www.php.net/manual/en/filter.filters.validate.php">http://www.php.net/manual/en/filter.filters.validate.php</a></li>
<li><a href="http://www.php.net/manual/en/function.filter-var-array.php">http://www.php.net/manual/en/function.filter-var-array.php</a></li>
</ul>
<p>Иногда обработку массива превращают в что-то невообразимое, можно же проще:</p>
<pre class="brush: php; title: ; notranslate">
  // array of ids
$ids = $Request-&gt;getParam(&quot;ids&quot;);

  // to integer
$ids = array_map(&quot;intval&quot;, $ids);

  // without zero
$ids = array_filter($ids);

  // unique
$ids = array_unique($ids);

  // prepare for SQL
$idsStr = join(&quot;,&quot;, $ids);
</pre>
<p>А бывают ситуации, когда в форме есть несколько submit кнопок, и надо понимать что же было нажато (о да, это плохие формы)</p>
<pre class="brush: xml; title: ; notranslate">
&lt;form&gt;
    ...
    &lt;input type=&quot;submit&quot; name=&quot;disable&quot; value=&quot;Disable selected&quot; /&gt;
    &lt;input type=&quot;submit&quot; name=&quot;enable&quot; value=&quot;Enable selected&quot; /&gt;
&lt;/form&gt;
</pre>
<pre class="brush: php; title: ; notranslate">
&lt;?php  // gets current action
$action = current(array_intersect(array_keys(
    $_REQUEST), array('disable', 'enable')
));
</pre>
<h3>Tips &#038; Tricks: glob</h3>
<p>Уже везде упоминали функцию <a href="http://www.php.net/manual/en/function.glob.php">glob</a>, но мы всё так же её игнорируем, так что я решил её опять вспомнить:</p>
<pre class="brush: php; title: ; notranslate">
$controllers = glob('/modules/*/controllers/*.php');

$modules = glob('/modules/*', GLOB_ONLYDIR);
</pre>
<h3>Tips &#038; Tricks: mysqlnd</h3>
<p>Если у вас высокопроизводительное приложение, если вам знакомо понятие master и slave, если разруливаете это ручками, то вот оно &mdash; прозрение:</p>
<pre class="brush: php; title: ; notranslate">
// in config
[myapp]
master[]=localhost:/tmp/mysql.sock
slave[]=192.168.2.27:3306

// in sources
$mysqli = new mysqli(&quot;myapp&quot;, &quot;username&quot;, &quot;password&quot;, &quot;database&quot;);
$pdo = new PDO(&quot;mysql:host=myapp;dbname=database&quot;, &quot;username&quot;, &quot;password&quot;);
</pre>
<p>По теме:</p>
<ul>
<li><a href="http://www.php.net/manual/en/book.mysqlnd.php">http://www.php.net/manual/en/book.mysqlnd.php</a></li>
<li><a href="http://blog.ulf-wendel.de/?p=307">Replication and load balancing mysqlnd plugin for all PHP MySQL extensions released</a></li>
</ul>
<p>P.S. О, это уже было в &#8230; <a href="https://twitter.com/AntonShevchuk">твитере</a> ;)</p>
<h3>Reflection</h3>
<p>Посмотри на себя в <a href="http://php.net/manual/en/book.reflection.php">зеркало</a>, в нём ты увидишь и имена <a href="http://www.php.net/manual/en/reflectionclass.getmethods.php">методов</a>, и приватные <a href="http://www.php.net/manual/en/reflectionclass.getproperties.php">данные</a>, и даже <a href="http://www.php.net/manual/en/reflectionclass.getdoccomment.php">комментарии</a>. Знай и используй с умом!</p>
<h2>PHP 5.3</h2>
<p>Приведу краткие тезисы по теме новшеств в PHP 5.3 (да, да, жевано-пережевано, но повторить то стоит)</p>
<h3>Анонимные функции</h3>
<p>Используете jQuery, значит ничего нового тут не увидите:</p>
<pre class="brush: php; title: ; notranslate">
$ids = array_map(function($el){
    return intval($el);
}, $ids);
  // or
$toInt = function($el){
    return intval($el);
};
$ids = array_map($toInt, $ids);
  // or
$int = $toInt($el);
</pre>
<h3>Замыкания</h3>
<p>Смотреть в код до наступления понимания происходящего:</p>
<pre class="brush: php; title: ; notranslate">
function getTimeout() {
    $el = &quot;15 minutes&quot;;

    return function() use ($el){
        return intval($el);
    };
}

$timeOut = getTimeout();
echo $timeOut(); // &gt;&gt; 15
</pre>
<h3>Магический метод __invoke()</h3>
<p>Вместо слов, лишь код:</p>
<pre class="brush: php; title: ; notranslate">
class Row
{
    function __invoke()
    {
        return $this-&gt;login;
    }
}

$user = new Users\Row();
$var = $user(); // &gt;&gt; return user login
</pre>
<h3>Namespaces</h3>
<p>Про пространство имён не писал лишь ленивый, но я предлагаю простую ассоциацию c файловой системой (спасибо за бэкслеш, ассоциация будет с Windows):</p>
<pre class="brush: plain; title: ; notranslate">
\              &lt;&lt; Default namespace - root
\Exception
\StdClass
\Application   &lt;&lt; Our namespace - Application
            \Model
                  \User
                  \Group
            \Controller
                  \User
</pre>
<ul>
<li>объявляя namespace &mdash; мы добавляем новый класс в иерархию &laquo;папок&raquo; &mdash; cd &#038;&#038; mkdir</li>
<li>говоря use &mdash; используем директорию из иерархии (1 или более) &mdash; как команда cd</li>
<li>говоря use as &mdash; используем под определенным именем &mdash; ln</li>
<li>всегда можно обращаться идя от корня (абсолютный путь: \Application\Model\Group)</li>
</ul>
<h3>Позднее статическое связывание</h3>
<p>Эта непонятная фигня означает, что когда вы наследуете класс от статического, то у вас возникнут проблемы с доступом к статическим методам наследуемого класса, приведу наглядный (я надеюсь) пример:</p>
<pre class="brush: php; title: ; notranslate">
class Latin
{
     const CHARS = 'abc...';
     static function getChars() {
         return self::CHARS;
     }
}
class French extends Latin
{
    const CHARS = 'aàábc...';
}

echo French::getChars(); // &gt;&gt; abc...
</pre>
<p>В PHP 5.3 решается просто:</p>
<pre class="brush: php; title: ; notranslate">
class Latin
{
     const CHARS = 'abc...';
     static function getChars() {
         return static::CHARS; // &lt;&lt; static
     }
}
class French extends Latin
{
    const CHARS = 'aàábc...';
}

echo French::getChars(); // &gt;&gt; aàábc...
</pre>
<h3>Почти фреймворк</h3>
<p>Ой, велосипед, да на &laquo;новых&raquo; технологиях, нямочка, зацените-ка контроллер:</p>
<pre class="brush: php; title: ; notranslate">
namespace Bluz;
return
/**
 * @acl View User Profile
 * @cache 5 minutes
 * @param integer $id
 * @return closure
 */
function($id) use ($bootstrap, $app, $view) {
    /**
     * @var closure $bootstrap
     * @var Application $app
     * @var View $view
     */
     $view-&gt;user = $app-&gt;getDb()-&gt;getRow(&quot;...&quot;, array($id));
};
</pre>
<ul>
<li>Контроллер &mdash; анонимная функция возвращаемая при подключении файла</li>
<li>Может вернуть что угодно, лишь бы было callable</li>
<li>Принимает параметры с реквеста, уже отфильтрованнные (описали же в doccomment&#8217;e правила)</li>
<li>Кеширует результат выполнения на 5 минут (опять reflection)</li>
<li>Acl тут тоже не просто так ;)</li>
</ul>
<p>Возможно выложим на общее обозрение сие творение, ну когда отшлифуем его ;)</p>
<h3>Если что-то позабыл</h3>
<p>Позабыл я о небольшом изменении в поведении тернарного оператора &laquo;(statement)?(then):(else)&raquo;, у него появилось мини-сокращение:</p>
<pre class="brush: php; title: ; notranslate">
echo $var?$var:'nil';
// ? : improvements
echo $var?:'nil';
</pre>
<p>Что-то оно не очень, можно было сделать лучше (эти примеры не работают!):</p>
<pre class="brush: php; title: ; notranslate">
echo $var?;
// or
echo isset($var['elem'])?$var['elem'];
</pre>
<p>Еще &laquo;незаслужено&raquo; обошёл стороной метки:</p>
<pre class="brush: php; title: ; notranslate">
// labels
start: echo &quot;start&quot;; goto finish;

echo &quot;never&quot;;

finish: echo &quot;finish&quot;;
</pre>
<p>Но наверное вы в курсе:<br />
<a href="http://xkcd.com/292/"><img src="http://anton.shevchuk.name/wp-content/uploads/2011/11/xkcd-goto.png" alt="" title="xkcd-goto" width="620" class="aligncenter size-full wp-image-1994" /></a></p>
<p>P.S. Всё это и еще немного больше я обычно рассказываю на лекциях в <a href="http://anton.shevchuk.name/php/team-life/" title="Жизнь в PHP отделе">своём отделе</a> ;)</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li>No Related Post</li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/about-php/feed/</wfw:commentRss>
		<slash:comments>37</slash:comments>
		</item>
		<item>
		<title>Zend Framework Day</title>
		<link>http://anton.shevchuk.name/php/zend-framework-day/</link>
		<comments>http://anton.shevchuk.name/php/zend-framework-day/#comments</comments>
		<pubDate>Mon, 31 Oct 2011 15:20:11 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[announcement]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1982</guid>
		<description><![CDATA[12 ноября, в Киеве, пройдет конференция &#8220;Zend Framework Day&#8221;, посвященная популярному PHP фреймворку Zend Framework. Среди докладчиков &#8211; непосредственные разработчики компонент Zend Framework. Событие соберет лучших PHP и Zend Framework специалистов из Украины, России, Белоруссии и других стран СНГ. С текущим списком тем и докладчиков можно ознакомиться на странице. Событие будет интересно не только тем, [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://frameworksdays.com/uploads/events/zend-framework-day.png" alt="Zend Framework Day" align="left" style="margin-right: 20px;" />12 ноября, в Киеве, пройдет конференция &#8220;Zend Framework Day&#8221;, посвященная популярному PHP фреймворку Zend Framework. <br/><br />
Среди докладчиков &#8211; непосредственные разработчики компонент Zend Framework. Событие соберет лучших PHP и Zend Framework специалистов из Украины, России, Белоруссии и других стран СНГ.</p>
<p><span id="more-1982"></span></p>
<p>С текущим списком тем и докладчиков можно ознакомиться на <a href="http://frameworksdays.com/event/zend-framework-day-2011/speakers">странице</a>.</p>
<p>Событие будет интересно не только тем, кто использует Zend Framework, но и всем веб-разработчикам и PHP программистам в частности.</p>
<p>Место проведения — отель &#8220;Казацкий&#8221;, г. Киев, ул. Михайловская 1/3 (Площадь Независимости).</p>
<p>Стоимость участия и другая необходимая информация доступна на <a href="http://frameworksdays.com/event/zend-framework-day-2011">странице</a>.</p>
<p>Для участия в конференции необходимо пройти регистрацию на <a href="http://frameworksdays.com/register">странице</a>, а затем произвести оплату из личного кабинета.</p>
<p>
Сайт конференции &#8211; <a href="http://frameworksdays.com/event/zend-framework-day-2011">http://frameworksdays.com/event/zend-framework-day-2011</a><br />
Наши новости в twitter &#8211; <a href="http://twitter.com/fwdays">http://twitter.com/fwdays</a><br />
Наша страница в facebook – <a href="http://facebook.com/fwdays">http://facebook.com/fwdays</a></p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/html-and-css/master-klass-yuriy-akella-artyukh/" title="Мастер-класс Юры Артюха">Мастер-класс Юры Артюха</a></li><li><a href="http://anton.shevchuk.name/internet/sphinx-master-class/" title="Мастер-класс по Sphinx">Мастер-класс по Sphinx</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-2011-piter/" title="Конференция ZFConf 2011">Конференция ZFConf 2011</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-ukraine/" title="ZFConf Ukraine">ZFConf Ukraine</a></li><li><a href="http://anton.shevchuk.name/javascript/javascript-master-class-feedback/" title="Анонс мастер-классов по JavaScript&#8217;у">Анонс мастер-классов по JavaScript&#8217;у</a></li><li><a href="http://anton.shevchuk.name/javascript/javascript-master-class/" title="Мастер-классы по JavaScript&#8217;у">Мастер-классы по JavaScript&#8217;у</a></li><li><a href="http://anton.shevchuk.name/internet/pdfeed-rss-to-pdf-generator/" title="PDFEED &#8211; или RSS2PDF в новом обличье">PDFEED &#8211; или RSS2PDF в новом обличье</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/zend-framework-day/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Интеграция форума Vanilla</title>
		<link>http://anton.shevchuk.name/php/embed-migration-integration-vanilla/</link>
		<comments>http://anton.shevchuk.name/php/embed-migration-integration-vanilla/#comments</comments>
		<pubDate>Thu, 30 Jun 2011 06:57:18 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Vanilla]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1932</guid>
		<description><![CDATA[Если перед вами стоит задача подключения форума к готовой системе, то советую вам обратить внимание на Vanilla Forum. А дальше &#8212; скучноватый пошаговый мануал =\ Для начала нам потребуется скачать последнюю версию форума с официального сайта, а поскольку я тут успел наступить на одни грабли &#8212; то качаем последнюю бету &#8212; vanilla-core-2.0.18b2. Встраиваем По умолчанию, [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/06/vanilla_sticker-460x207.png" alt="" title="Vanilla Forum" width="460" height="207" class="aligncenter size-medium wp-image-1933" /><br />
Если перед вами стоит задача подключения форума к готовой системе, то советую вам обратить внимание на <a href="http://vanillaforums.org/">Vanilla Forum</a>. А дальше &mdash; скучноватый пошаговый мануал =\<br />
<span id="more-1932"></span></p>
<blockquote><p>Для начала нам потребуется скачать последнюю версию форума с официального сайта, а поскольку я тут успел наступить на одни грабли &mdash; то качаем последнюю бету &mdash; <a href="http://vanillaforums.org/addon/vanilla-core-2.0.18b2">vanilla-core-2.0.18b2</a>.</p></blockquote>
<h3>Встраиваем</h3>
<p>По умолчанию, Vanilla форум имеет прекрасную возможность &mdash; встраиваться в любую HTML страничку посредством JavaScript&#8217;a, для этого нам понадобится активировать плагин &lt;Embed&gt; Vanilla (идет в стандартной поставке) и получить код скрипта:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;http://forum.your-domain.com/plugins/embedvanilla/remote.js&quot;&gt;&lt;/script&gt;
</pre>
<p>Так же советую использовать дефолтную тему &laquo;Embed-Friendly&raquo; для встроенного форума &mdash; она достаточно простая и удобная для кастомизации. </p>
<p>Единственная проблема которая у меня возникла при подобном подключении форума &mdash; это ошибки JavaScript&#8217;a &mdash; разработчики форума не в курсе о методе <code>jQuery().delegate()</code> и используют <code>jQuery('a').live('click')</code>, чем вызывают не очень адекватное поведение ссылок на странице без атрибута href. </p>
<h3>Интеграция с использованием ProxyConnect</h3>
<p>Как понятно из подзаголовка, нам потребуется скачать плагин <a href="http://vanillaforums.org/addon/proxyconnect-plugin">Vanilla ProxyConnect</a>. Устанавливаем и идем настраивать (Users &gt; Authentication):</p>
<p><a href="http://anton.shevchuk.name/wp-content/uploads/2011/06/ProxyConnect.png"><img src="http://anton.shevchuk.name/wp-content/uploads/2011/06/ProxyConnect-460x300.png" alt="" title="ProxyConnect" width="460" height="300" class="aligncenter size-medium wp-image-1936" /></a></p>
<p>Тут всё просто, по пунктам (пути в примере полные, могут быть относительные):</p>
<ol>
<li>Ваш сайт</li>
<li>Страничка для получения данных о вашем пользователе (о ней чуть далее)</li>
<li>Страница регистрации</li>
<li>Страница входа</li>
<li>Страница выхода</li>
</ol>
<p>Теперь займемся пунктом 2 &mdash; Authenticate URL. Это страничка, где у вас должны выводиться данные вашего текущего пользователя в простом формате:</p>
<blockquote><pre>
UniqueID=1279
Name=Bob
Email=bob@gmail.com
TransientKey=qwerty
DateOfBirth=1970-01-01
Gender=Male
Roles=Member,Admin
</pre>
</blockquote>
<ul>
<li><strong>UniqueID</strong> &mdash; обязательное поле, UID пользователя в вашей системе</li>
<li><strong>Name </strong> &mdash; обязательное поле, username, или как он у вас обзывается</li>
<li><strong>Email</strong> &mdash; обязательное поле, email вашего пользователя</li>
<li><strong>TransientKey</strong> &mdash; ключ, для предотвращения CSRF атак (вам скорей всего не понадобится, для wordpress обязательно)</li>
<li><strong>DateOfBirth</strong> &mdash; дата рождения</li>
<li><strong>Gender</strong> &mdash; мальчик/девочка, Male/Female</li>
<li><strong>Roles</strong> &mdash; роли пользователя на Vanilla форуме, через запятую, можно IDшниками</li>
</ul>
<p>Если у вас Zend Framework, то action будет выглядеть как-то так:</p>
<pre class="brush: php; title: ; notranslate">
// disable View
$this-&gt;_helper-&gt;layout()-&gt;disableLayout();
$this-&gt;_helper-&gt;viewRenderer-&gt;setNoRender(true);

// get current User
$user = Zend_Auth::getInstance()-&gt;getIdentity();
if ($user) {
echo &quot;UniqueID={$user-&gt;id}\n&quot;
     . &quot;Name={$user-&gt;username}\n&quot;
     . &quot;Email={$user-&gt;email}\n&quot;
     . &quot;Roles={$user-&gt;role}\n&quot;;
} else {
echo &quot;UniqueID=\n&quot;
     . &quot;Name=\n&quot;
     . &quot;Email=\n&quot;;
}
</pre>
<p>Как это работает &mdash; когда пользователь заходит на форум, то Vanilla незаметно так стучится на нашу страничку с текущей сессией пользователя, и пытается получить о нем информацию. Если это удалось, то движок пытается найти соответсвующую запись у себя в БД &mdash; если нашел, то мы авторизированы, если не нашел соответсвия &mdash; регистриует нового пользователя с указанными данными и так же авторизирует. Если наша страничка не вернула данных о пользователе, то он либо будет послан на страницу Sign In указанную в настройках ProxyConnect, либо отобразит страницу авторизации форума (зависит, от того, что разрешено пользователю guest).</p>
<p>Из замеченного: для разлогинивания на форуме следует чистить куки:</p>
<pre class="brush: php; title: ; notranslate">
// vanilla forum logout
setcookie('Vanilla', ' ', time() - 3600, '/', '.domain.com');
unset($_COOKIE['Vanilla']);
</pre>
<p>Если пользователь авторизирован в вашей системе, а при входе на форум требуется логинится (банально клацнуть на кнопку, для запуска процесса авторизации), то это тоже поправимо &mdash; следует в вашем методе логина добавить следующую очистку куков (я их еще и в логаут добавил, так на всяк случай):</p>
<pre class="brush: php; title: ; notranslate">
setcookie(&quot;Vanilla&quot;, &quot;deleted&quot;, time() - 1,'/', &quot;.domain.com&quot;, false);
setcookie(&quot;Vanilla-Volatile&quot;, &quot;deleted&quot;, time() - 1, '/', &quot;.domain.com&quot;, false);
</pre>
<p><a href="http://vanillaforums.org/page/ProxyConnect_SSO">Официальная документация по ProxyConnect</a></p>
<h3>Портирование с phpBB, vBulletin и т.д.</h3>
<p>На случай если вы решили сменить движок форума, то это тоже предусмотрено ;) Качаем <a href="http://vanillaforums.org/addon/porter-core">Vanilla Porter</a> и заливаем его в корень вашего текущего форума (phpBB или что там у вас). Далее следуем простой инструкции по переносу данных:</p>
<ol>
<li>Экспорт форума в TXT файл хитрого формата</li>
<li>Импорт этого самого файла</li>
</ol>
<p>Как просто-то :) Ну для наглядности приведу еще скриншот самой тулзы:<br />
<img src="http://anton.shevchuk.name/wp-content/uploads/2011/06/VanillaPorter-356x460.png" alt="" title="VanillaPorter" width="356" height="460" class="aligncenter size-medium wp-image-1934" /></p>
<p>Но без нюансов тоже не обошлось &mdash; первое с чем столкнулся это непонимание формата файла в архиве, пришлось распаковывать и заливать. Второе &mdash; во время импорта все текущие пользователи Vanilla будут удалены, включая вашу учетную запись, так что в определенный момент вам надо будет указать email нового админа из портированных данных, после чего вы получите ошибку авторизации, без паники, логинимся заново под новым админом и продолжаем импорт данных (будет такая кнопочка). Следующий шаг &mdash; вам потребуется назначить права для всех ролей пользователей, которые были перенесены со старого движка. И напоследок, когда пользователи со старой системы попробуют зайти на новый форум им высветится следующий диалог (это верно для Vanilla + ProxyConnect):</p>
<p><a href="http://anton.shevchuk.name/wp-content/uploads/2011/06/linking.png"><img src="http://anton.shevchuk.name/wp-content/uploads/2011/06/linking.png" alt="" title="Vanilla Linking Accounts" width="600" height="287" class="aligncenter size-full wp-image-1938" /></a></p>
<p>Подготовьте пользователей к правильным действиям заранее.</p>
<p>Еще такой момент, умные дядьки часто изменяют алгоритм хэшированя паролей, на этот случай прийдется выполнить следующие действия:</p>
<ol>
<li>Заменить имя метода хэширования на некоторый произвольный</li>
<li>Добавить его поддержку в файле <code>vanilla/library/core/class.passwordhash.php</code></li>
</ol>
<p>С первым всё совсем просто:</p>
<pre class="brush: sql; title: ; notranslate">
UPDATE GDN_User SET HashMethod = 'myHash' WHERE HashMethod = 'phpBB';
</pre>
<p>Второй пункт тоже не сложен &mdash; нам потребуется найти <code>switch</code> в методе <code>CheckPassword()</code> и добавить туда новый <code>case</code>:</p>
<pre class="brush: php; title: ; notranslate">
switch(strtolower($Method)) {
/* ... */
    case 'myhash':
        $Result = myMegaHashFunction($Password, $StoredHash);
        break;
/* ... */
}
</pre>
<p><a href="http://vanillaforums.com/blog/help/importing-data/">Документация по Vanilla Porter</a></p>
<h3>Вместо вывода</h3>
<p>Форум действительно стоит того, чтобы его установить и опробовать: много локализаций (включая русский и арабский) и полезных плагинов; простые для адаптации темы (хоть и Smarty используется); и таки удобная система интеграции.</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li>No Related Post</li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/embed-migration-integration-vanilla/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Конференция ZFConf 2011</title>
		<link>http://anton.shevchuk.name/php/zfconf-2011-piter/</link>
		<comments>http://anton.shevchuk.name/php/zfconf-2011-piter/#comments</comments>
		<pubDate>Tue, 10 May 2011 14:24:38 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[announcement]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1865</guid>
		<description><![CDATA[Если вы не понаслышке знакомы с Zend Framework&#8217;ом, то вам стоит посетить ZFConf. Сейчас уже открыта финальная регистрация на ZFConf 2011 — вторую российскую конференцию по Zend Framework. Основные подготовительные работы по конференции закончены. Доклады сформированы. Среди спикеров представители KnpLabs, Magento, JetBrains, Sphinx и других известных компаний. Если Вы проходили предварительную регистрацию, то для прохождения [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.zfconf.ru/" target="_blank"><img src="http://cs9981.vkontakte.ru/g14951507/a_78058dc0.jpg" width="192" height="139" class="aligncenter"></a><br />
Если вы не понаслышке знакомы с Zend Framework&#8217;ом, то вам стоит посетить ZFConf. Сейчас уже открыта <a href="http://www.zfconf.ru/registration/" target="_blank">финальная регистрация</a> на ZFConf 2011 — вторую российскую конференцию по Zend Framework.</p>
<p>Основные подготовительные работы по конференции закончены. Доклады сформированы. Среди спикеров представители KnpLabs, Magento, JetBrains, Sphinx и других известных компаний.</p>
<p><span id="more-1865"></span></p>
<p>Если Вы проходили предварительную регистрацию, то для прохождения финальной регистрации Вам нужно перейти по специальной ссылке, отправленной на e-mail. Введенную ранее информацию нужно будет только подтвердить. Если ссылка не приходила Вам, ее можно запросить через <a href="http://www.zfconf.ru/get-registration-path/" target="_blank">специальную форму</a>.</p>
<p>Пожалуйста, обратите внимание на то, что <strong>изменилось <a href="http://www.zfconf.ru/2011/location/" target="_blank">место проведения</a></strong>! Мероприятие будет проходить на прошлогодней площадке — в актовом зале ЛГТЦ (Ленгипротранс-Центр), по адресу: Московский пр., 143, ст. м. «Электросила». Кофе-брейки и обсуждения на флипчартах будут проходить в фойе.</p>
<h2>Cписок докладов</h2>
<ul>
<li><strong><a href="http://www.zfconf.ru/2011/topics/flexible-architecture-using-dependency-injection/" target="_blank">Гибкая архитектура Zend Framework приложений с использованием Dependency Injection</a></strong> &mdash; Алексей Качаев (<a href="http://cogniance.com/" target="_blank">Сogniance</a>, Senior PHP Developer)</li>
<li><strong><a href="http://www.zfconf.ru/2011/topics/performance-increasing-with-phpdaemon-varnish-esi/" target="_blank">Воюем за ресурсы: Повышение производительности Zend Framework приложения с помощью phpDaemon, Varnish и ESI</a></strong>  &mdash; Алексей Качаев (<a href="http://cogniance.com/" target="_blank">Сogniance</a>, Senior PHP Developer)</li>
<li><strong><a href="http://www.zfconf.ru/2011/topics/behavior-driven-development/" target="_blank">Behavior Driven Development в PHP и Zend Framework</a></strong>  &mdash; Константин Кудряшов (<a href="http://www.knplabs.com/" target="_blank">KnpLabs</a>, Senior Web Developer)</li>
<li><strong><a href="http://www.zfconf.ru/2011/topics/multitasking-distributed-system-with-job-queue/" target="_blank">Разделение труда: Организация многозадачной, распределенной системы в Zend Framework с помощью Job Queue</a></strong>  &mdash; Александр Готгельф (<a href="http://www.magentocommerce.com/" target="_blank">Magento</a>, PHP-разработчик) </li>
<li><strong><a href="http://www.zfconf.ru/2011/topics/how-can-help-development-environment/" target="_blank">Как может помочь среда разработки при написании приложения на Zend Framework?</a></strong>  &mdash; Николай Матвеев (<a href="http://www.jetbrains.com/" target="_blank">JetBrains</a>, разработчик) </li>
<li><strong><a href="http://www.zfconf.ru/2011/topics/creating-rest-api-for-third-party-developers-and-mobile-devices-with-oauth/" target="_blank">Создание REST-API для сторонних разработчиков и мобильных устройств с авторизацией по протоколу OAuth 1.0</a></strong>  &mdash; Дмитрий Чижевский (<a href="http://zenmoney.ru/" target="_blank">Дзен-мани</a>, разработчик) </li>
<li><strong><a href="http://www.zfconf.ru/2011/topics/fat-model-through-orm-development-history/" target="_blank">Толстая модель: История разработки собственного ORM</a></strong>  &mdash; Михаил Шамин (<a href="http://geometria.ru/" target="_blank">Геометрия</a>, ведущий разработчик) </li>
<li><strong><a href="http://www.zfconf.ru/2011/topics/what-is-sphinx-and-how-use-it-with-php/" target="_blank">Что такое Sphinx, зачем он вообще нужен и как его использовать с PHP (от простого индекса до поискового кластера)</a></strong>  &mdash; Владимир Федорков (<a href="http://sphinxsearch.com/" target="_blank">Sphinx</a>, Director of Professional Services)</li>
</ul>
<p>Стоимость участия в мероприятии составит 1500 руб. В данную сумму входит: участие в самой конференции, кофе-брейки (от внешней кейтеринг-компании), пакет материалов участника и доступ к профессиональным видеозаписям докладов (после мероприятия). Доступно <a href="http://www.zfconf.ru/payment-types/" target="_blank">множество способов оплаты</a>.</p>
<p>Торопитесь, <strong>количество мест ограничено</strong>!</p>
<p>Обязательно следите за последними новостями:</p>
<ul>
<li><strong>RSS-канал:</strong> <a href="http://feeds.feedburner.com/zfconf" target="_blank">feeds.feedburner.com/zfconf</a></li>
<li><strong>Twitter:</strong> <a href="http://twitter.com/zfconf" target="_blank">@zfconf</a></li>
<li><strong>Группа ВКонтакте:</strong> <a href="http://vkontakte.ru/club14951507" target="_blank">vkontakte.ru/club14951507</a></li>
</ul>
<p><strong>Организатор:</strong> <a href="http://www.wizartech.ru/" target="_blank">компания Wizartech</a><br />
<strong>Партнеры:</strong> <a href="http://zendframework.ru/" target="_blank">сообщество zendframework.ru</a> и <a href="http://zfconf.org.ua/" target="_blank">конференция ZFConf Ukraine</a></p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/zend-framework-day/" title="Zend Framework Day">Zend Framework Day</a></li><li><a href="http://anton.shevchuk.name/html-and-css/master-klass-yuriy-akella-artyukh/" title="Мастер-класс Юры Артюха">Мастер-класс Юры Артюха</a></li><li><a href="http://anton.shevchuk.name/internet/sphinx-master-class/" title="Мастер-класс по Sphinx">Мастер-класс по Sphinx</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-ukraine/" title="ZFConf Ukraine">ZFConf Ukraine</a></li><li><a href="http://anton.shevchuk.name/javascript/javascript-master-class-feedback/" title="Анонс мастер-классов по JavaScript&#8217;у">Анонс мастер-классов по JavaScript&#8217;у</a></li><li><a href="http://anton.shevchuk.name/php/facing-refucktoring/" title="Facing refucktoring">Facing refucktoring</a></li><li><a href="http://anton.shevchuk.name/javascript/javascript-master-class/" title="Мастер-классы по JavaScript&#8217;у">Мастер-классы по JavaScript&#8217;у</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-feedback/" title="Под впечатлением от ZFConf">Под впечатлением от ZFConf</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/zfconf-2011-piter/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Вакансия PHP разработчика</title>
		<link>http://anton.shevchuk.name/php/vacancy-php-developer/</link>
		<comments>http://anton.shevchuk.name/php/vacancy-php-developer/#comments</comments>
		<pubDate>Thu, 07 Apr 2011 11:44:20 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1846</guid>
		<description><![CDATA[В компанию NIX Solutions требуются PHP разработчики. Текст вакансии перепечатывать не буду, а скажу, что требуется: Опыт работы в коммерческих проектах с каким-нибудь из PHP фреймворков: Zend Framework, Yii, Code Igniter, CakePHP или Symfony Опыт работы с JavaScript фреймворком jQuery, или Dojo Toolkit, MooTools, YUI Ответить на наши вопросы Вопросы элементарные, на них ответит каждый, [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2010/02/logo.png" alt="" title="NIX Solutions Ltd" width="200" height="90" class="aligncenter size-full wp-image-1200" /></p>
<p>В компанию <a href="http://nixsolutions.com">NIX Solutions</a> требуются <a href="http://nixsolutions.com/ru/career/vacancies/middle-php-razrabotchik/">PHP разработчики</a>.<br />
<span id="more-1846"></span></p>
<p>Текст вакансии перепечатывать не буду, а скажу, что требуется:</p>
<ul>
<li>Опыт работы в коммерческих проектах с каким-нибудь из PHP фреймворков: Zend Framework, Yii, Code Igniter, CakePHP или Symfony</li>
<li>Опыт работы с JavaScript фреймворком jQuery, или Dojo Toolkit, MooTools, YUI</li>
<li>Ответить на наши вопросы</li>
</ul>
<p>Вопросы элементарные, на них ответит каждый, кто в теме:</p>
<p> &mdash; Что нового в PHP 5.3?</p>
<p> &mdash; Какие паттерны знаете?</p>
<p> &mdash; Как сохранить в MySQL дерево категорий произвольной вложенности?</p>
<p> &mdash; Напишите функцию для вывода дерева категорий в HTML.</p>
<p> &mdash; Что нового в HTML5?</p>
<p> &mdash; Написать HTML код для следующих CSS селекторов:</p>
<pre class="brush: css; title: ; notranslate">
#my .pm.tt strong, li li a:hover+span {
    color:#f00; // какой это цвет?
}
</pre>
<p> &mdash; Что знаете о CSS3?</p>
<p> &mdash; Что такое прототип в js?</p>
<p> &mdash; Как организовать классическое наследование в js?</p>
<p> &mdash; Что такое bubbling?</p>
<p> &mdash; Используя native js изменить текст следующего тега на &#8220;Hello World!&#8221;:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;div class=&quot;tutu bubu&quot; &gt;
    Example!
&lt;/div&gt;
</pre>
<p>Используя js фреймворк изменить цвет последнего элемента списка на желтый по завершению загрузки DOM&#8217;а:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;#&quot; id=&quot;my&quot;&gt;Tic-tac-toe&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;/li&gt;
    ...
    &lt;li&gt;Yellow string!&lt;/li&gt;
&lt;/ul&gt;
</pre>
<p>С нашей стороны &mdash; обеспечим интересной работой и белую ЗП по ЧП. И, конечно же, дружный коллектив, фото с бани мне, правда, приложить не разрешили, но так оно и есть&#8230;</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li>No Related Post</li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/vacancy-php-developer/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Рефакторинг который мы не делаем</title>
		<link>http://anton.shevchuk.name/php/unimplemented-refactoring/</link>
		<comments>http://anton.shevchuk.name/php/unimplemented-refactoring/#comments</comments>
		<pubDate>Fri, 11 Feb 2011 14:00:33 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1715</guid>
		<description><![CDATA[Не так давно я описывал процесс рефакторинга одного проекта зашедшего в отдел, мы вместе посмеялись да поплакали, но ведать мораль оттуда не вынесли. И в этой истории мы не &#171;белые и пушистые&#187;&#8230; История начиналась банально &#8212; проект загибается, заказчик ищет новую команду разработки и находит нас. Мы такие все из себя, на белом коне въезжаем [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/02/again.jpg" alt="" title="Again" width="300" height="300" class="aligncenter size-full wp-image-1736" /></p>
<p>Не так давно я <a href="http://anton.shevchuk.name/php/facing-refucktoring/">описывал процесс рефакторинга</a> одного проекта зашедшего в отдел, мы вместе посмеялись да поплакали, но ведать мораль оттуда не вынесли. И в этой истории мы не &laquo;белые и пушистые&raquo;&#8230;<br />
<span id="more-1715"></span></p>
<p>История начиналась банально &mdash; проект загибается, заказчик ищет новую команду разработки и находит нас. Мы такие все из себя, на белом коне въезжаем в проект, а там ничего страшного, просто <strike>функциональный</strike> plain PHP код, т.е. классов нет, функций практически тоже (один файл functions.php, а в нем dbconnect(), userlogin() да функция-шаблонизатор, и что-то еще по мелочи). Смотрим &mdash; а проблема то в нагрузке &mdash; до 10 000 онлайн пользователей и в это время на сайт и зайти нельзя. Хорошо, задача ясна, садим в проект разработчика, который знаком с оптимизацией, ну и он начал работать. И да, после двух недель работы (а может и больше, уже не помню) сайт ожил, чуть-чуть индексов, чуть-чуть кеширования, еще немного оптимизации SQL запросов и нагрузка на сервера спала. </p>
<p>Заказчик доволен, и уже у него появляются &laquo;хотелки&raquo; на новый фичи, да немного еще старых багов подкидывает на фикс. В этот момент эффективность нашего разработчика сильно падает &mdash; он то любит заниматься оптимизацией, а вот эти CSS да JavaScript ну ой как туго идут&#8230;</p>
<p>Спустя некоторое время, мы заменяем разработчика в проекте, новому человеку больше по душе приходятся фичи и ченжи заказчика, и наша эффективность опять идет вверх, заказчик удовлетворен, мы считаем деньги. Все хорошо и ничто не предвещало случившегося.</p>
<p>В проекте всё меньше и меньше работы, задачи скатываются до уровня trivial, и мы принимаем решение посадить в него &laquo;зелёного&raquo; бойца, дабы опробовать его, да и заказчик дал на это добро. Старт был гладким, а потом начались проблемы, то вылезли за сроки, которые сами и оговорили, то на сервер залили не совсем рабочий код. И как итог &mdash; &laquo;Спасибо, что помогли, вы меня действительно выручили, но дальше я обойдусь без вас&raquo;</p>
<p>Начинаем разбор полётов, решаем, что новенького мы зря посадили в проект, но я к тому моменту залез в изучение кода по уши, и поэтому сейчас опишу то, чего я там не увидел.</p>
<h2>Шаблонизатор</h2>
<p>В системе был создан элементарный шаблонизатор с нативным синтаксисом (что скорее плюс), но в шаблонах постоянно встречались обращения к БД, и при изменении структуры приходилось перелапачивать не только код страниц, но и все шаблоны. Таким образом необходимо было <strong>разделить логику от представления</strong> раз и навсегда.</p>
<p>Если говорить о функции-шаблонизаторе, то вот элементарный пример:</p>
<pre class="brush: php; title: ; notranslate">
/**
 * @param string
 * @param array
 */
function template($path, $vars = array()) {
    extract($vars);
    include $path;
}

template(&quot;templates/index.phtml&quot;, array(
    &quot;foo&quot; =&gt; &quot;bar&quot;,
    &quot;user&quot; =&gt; $user
));
</pre>
<p>Остальное остается на совести разработчика, собственно даже в тот же Smarty можно впихнуть логику при должном желании.</p>
<h2>Сущности</h2>
<p>Процесс разработки так же затруднялся тем, что очень много копипаста было в проекте, и вносить изменения сразу и везде было проблематично (поиск и замена по папке это не тру). А необходимо было выделить сущности, и методы для работы с ними свести в один класс (или хотя бы файл). Таким образом мы бы смогли <strong>уменьшить повторение кода</strong> (и повторения ошибок).</p>
<h2>Контроллеры</h2>
<p>Тут просто напрашивался этот пункт, поэтому я его добавил, и хотел сказать следующее &mdash; если у вас в корне проекта лежит пару десятков файлов, то не стоит заморачиваться, и переписывать их все, чтобы у вас получилась папочка controllers и единая точка входа, ведь эти файлы с поставленной задачей вполне справляются. Как вывод &mdash; <strong>не стоит гнаться за идеальным кодом</strong>, такие вложения труда могут не окупиться.</p>
<h2>Работа с БД</h2>
<p>Как я описывал выше, SQL запросы встречались в проекте везде и всюду, и даже процесс экранирования входных данных в них был разный. <strong>Привести к единому стилю</strong>, создать нормальный врапер для БД, или хотя бы заменить использование расширения mysql на что-либо более современное.</p>
<p>Можно было бы написать простой врапер для врапера PDO за 20 минут, конечно он не будет универсален, но облегчит труд рефакторинга:</p>
<ul>
<li>легко логировать используя одну точку входа</li>
<li>можно быстро добавить кеширование для всех запросов</li>
</ul>
<h2>Кеширование</h2>
<p>Тут стоит упомянуть, кеширование прикрутили, НО лишь там где указал заказчик, а хотелось бы, чтобы был кеш везде где это имеет смысл, а не исходя из заключения &laquo;вроде тут&raquo;, т.е. надо было в каждый файл заглянуть, логи проанализировать, да и время кеширования можно было потюнить в рейлтайме, но сдвиг часового пояса нам наверное сильно помешал, и никто не захотел ради этого из дома мониторить сервер, или хотя бы попросить об этом наших же админов (а мы такие услуги предоставляем ;).</p>
<h2>Работа над ошибками</h2>
<p>Тут все легко, если вы открываете PHP файл, а вам IDE <strike>подсказывает</strike> кричит о ошибках в коде, то уделите пять минут времени и исправьте их:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/02/notices.png" alt="" title="Notices" width="311" height="145" class="aligncenter size-full wp-image-1746" /></p>
<p>И еще &mdash; в обязательном порядке включите отображение ошибок, и запомните &mdash; &laquo;подавленный&raquo; Notice крадет время:</p>
<pre class="brush: php; title: ; notranslate">
error_reporting(E_ALL);
ini_set('display_errors', '1');
</pre>
<h2>Тестирование</h2>
<p> &mdash; Unit тестирование? О чем речь, у нас тут запара, надо выкатывать на лив!<br />
Ну-ну&#8230;</p>
<h2>Клиентская оптимизация</h2>
<p>Этого пункта не коснулся ни один из разработчиков, участвовавших в проекте, и это действительно огорчает. Конечно, главная страничка проекта не слишком и тяжела &mdash; всего 300кб, но на них приходится 15 JS файлов, 18 картинок в CSS (спрайты спасут сервер), так же стоило потереть старые хуки для браузеров (IE5.5 и IE6), облегчить верстку, ведь 8,7kb это не так уж и мало (слишком много таблиц).</p>
<p>Собственно, сайт по оценке YSlow получает оценку C (70), что я считаю недопустимым, для сравнения мой блог с кучей картинок &mdash; A (90). Как улучшить эти показания очень хорошо рассказывает сайт <a href="http://webo.in/articles/">webo.in</a>, и я думаю ему стоит уделить место в закладках.</p>
<h2>&laquo;Не наш&raquo; проект</h2>
<p>Еще я бы хотел рассказать о том, что очень часто программисты не переживают и не живут проектом, а это очень негативно сказывается на взаимоотношения с заказчиком и на развитие проекта. Проект должен стать чем-то неотъемлемым от вас, если сайт загибается под наплывом пользователей &mdash; вам тоже должно быть не по себе, ведь это ваше творение страдает, и ему нужна ваша помощь. Вы как специалист должны советовать заказчику, как развивать проект, ведь именно вы знаете о всех ноу-хау в web-разработке, и не стоит ждать, что с предложением о улучшении выступит он, сделать первый шаг должны именно вы и именно сейчас.  Не стоит в проекте жить одним днем, надо смотреть вперед и возможно рефакторинг проекта стоило начать еще вчера.</p>
<h2>P.S</h2>
<p>Если в вашем проект все идет гладко, то присмотритесь, возможно под столь сладким обликом притаился рак вашего безразличия к жизни проекта.</p>
<h2>P.P.S</h2>
<p>Если вы уже загорелись начать рефакторинг, то я вас еще чуть-чуть еще отвлеку &mdash; стоит рефакторинг правильно &laquo;продать&raquo; заказчику, который любит и умеет считать деньги, с ним стоит разговаривать именно в этом ключе. Удачи вам.</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li>No Related Post</li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/unimplemented-refactoring/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>PHP jQuery Cookbook</title>
		<link>http://anton.shevchuk.name/php/php-jquery-cookbook/</link>
		<comments>http://anton.shevchuk.name/php/php-jquery-cookbook/#comments</comments>
		<pubDate>Tue, 04 Jan 2011 12:57:43 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1673</guid>
		<description><![CDATA[Давеча, меня попросили написать рецензию на новую книгу от издательства PACKT &#171;PHP jQuery Cookbook&#187;, и вот моя оценка готовым блюдам&#8230; Надеюсь я никого не обижу тем, что выложу оглавление книги, и буду идти по нему: Управление событиями в jQuery Первая глава посвящена полностью jQuery &#8212; основной акцент конечно сделан на события: события мышки и клавиатуры, [...]]]></description>
			<content:encoded><![CDATA[<p>Давеча, меня попросили написать рецензию на новую книгу от издательства <a href="https://www.packtpub.com/">PACKT</a> &laquo;<a href="https://www.packtpub.com/php-jquery-cookbook-to-create-interactive-web-applications/book">PHP jQuery Cookbook</a>&raquo;, и вот моя оценка готовым блюдам&#8230;<br />
<span id="more-1673"></span><br />
Надеюсь я никого не обижу тем, что выложу <a href="https://www.packtpub.com/toc/php-jquery-cookbook-table-contents">оглавление книги</a>, и буду идти по нему:</p>
<h3>Управление событиями в jQuery</h3>
<p>Первая глава посвящена полностью jQuery &mdash; основной акцент конечно сделан на события: события мышки и клавиатуры, отправка формы и манипуляции с ее элементами, затронуты методы live и die, но как-то мало о селекторах &mdash; лишь ссылка на официальную документацию, я бы привел больше примеров с <a href="http://api.jquery.com/category/selectors/form-selectors/">селекторами форм</a>. В целом, примеры достаточно простые и будут понятны любому новичку.</p>
<h3>Связка PHP и jQuery</h3>
<p>Данная глава посвящена AJAX&#8217;у, тема раскрыта практически полностью, хорошо описана обработка ошибок, за что автор получает плюс в карму ;)</p>
<h3>Работа с XML документами и JSON</h3>
<p>Каждому формату по главе, и да работа с XML и JSON описано очень хорошо, особенно то что касается PHP части . Порадовало, что автор ссылается на PHP 5.3, за что получает еще один плюс к своей карме.</p>
<h3>Работа с формами</h3>
<p>Работа с формами &mdash; этот момент вызывает ступор у многих web-разработчиков, особенно если требуется динамическая форма, с валидацией, и другими плюшками, а тут всё просто и на примерах &mdash; must have (<a href="https://www.packtpub.com/sites/default/files/2749OS-Chapter-5-Working-with-Forms.pdf">скачайте и посмотрите</a>). За использование <a href="http://ua.php.net/manual/en/function.filter-var.php">filter_var</a> еще один плюс.</p>
<h3>Добавление визуальных эффектов для форм</h3>
<p>Конечно, первый пример с игрой крестики-нолики меня немного озадачил, но остальные действительно будут полезны, особенно порадовал удачный пример с корзиной покупок.</p>
<h3>Создание прикольных навигационных меню</h3>
<p>Созданию различных меню посвящена целая глава, примеры конечно тривиальны, но в этом то и соль подобных книг, я ведь тоже <a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-2/">не смог обойти стороной менюшки</a>.</p>
<h3>Работа с данными используя PHP и jQuery</h3>
<p>Тривиальные задачи (автодополнение, облако тегов) и простые примеры работы с БД (хотя для кого-то они окажутся в новинку), но почему-то без использования PDO.</p>
<h3>Улучшаем сайт</h3>
<p>Это правильная глава, тут о кросс-доменных запросах пишут и почему-то сюда попала статья о создании jQuery плагинов, но от этого не стало хуже.</p>
<h3>Firebug</h3>
<p>В конце книги нас решили познакомить с полезным инструментом &mdash; Firebug, но как-то скромно.</p>
<h3>Оформление книги</h3>
<p>Тут чуть-чуть покритикую так как мне не понравилось сразу несколько вещей:</p>
<ul>
<li>Неужели сложно для примеров кода сделать адекватное форматирование?</li>
<li>Подсветка синтаксиса, даже элементарная, очень положительно сказывается на читаемости кода</li>
<li>Скриншоты с браузера &mdash; лучше убрать адресную строку и подобную лабуду, или по крайней мере делать их в одном браузере</li>
</ul>
<h3>Итого</h3>
<p>И что мы имеем? <a href="https://www.packtpub.com/php-jquery-cookbook-to-create-interactive-web-applications/book">Хорошая книга</a>, которая содержит много &laquo;правильныx&raquo; примеров &mdash; простых, доступных, охватывающих большинство задач, которые ставятся web-разработчикам в 90% проектов. Книга рассчитана на начинающих разработчиков, но пригодиться в качестве шпаргалки и для более продвинутой аудитории. </p>
<p>Немного огорчило, что автор забыл про все jQuery плагины, да и про jQuery UI стоило замолвить слово, хотя возможно не тот формат.</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/" title="jQuery для начинающих. Часть 9. Пишем плагины анимации">jQuery для начинающих. Часть 9. Пишем плагины анимации</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/" title="jQuery для начинающих. Часть 8. Расширяем фильтры">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/" title="jQuery для начинающих. Часть 7. Пишем плагины">jQuery для начинающих. Часть 7. Пишем плагины</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/" title="jQuery для начинающих. Часть 6. События">jQuery для начинающих. Часть 6. События</a></li><li><a href="http://anton.shevchuk.name/javascript/a-sexy-images-jquery-plugin/" title="(a) Sexy Images jQuery плагин">(a) Sexy Images jQuery плагин</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/" title="jQuery для начинающих. Часть 5. Эффекты">jQuery для начинающих. Часть 5. Эффекты</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-iphone-ui/" title="jQuery iPhone UI">jQuery iPhone UI</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-datagrid-plugins/" title="10 jQuery Datagrid плагинов">10 jQuery Datagrid плагинов</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/php-jquery-cookbook/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>ZFConf Ukraine</title>
		<link>http://anton.shevchuk.name/php/zfconf-ukraine/</link>
		<comments>http://anton.shevchuk.name/php/zfconf-ukraine/#comments</comments>
		<pubDate>Tue, 16 Nov 2010 14:09:34 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[announcement]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1620</guid>
		<description><![CDATA[27го ноября в Киеве пройдет бесплатная, международная совместная конференция ZFConf Ukraine &#038; MageConf. Цель конференции &#8211; объединить лучших специалистов PHP и Zend Framework Украины, России и других стран СНГ. ZFConf Ukraine – первая в Украине конференция по Zend Framework, проводится в партнерстве с ZFConf Russia и сообществом zendframework.ru. ZFConf Russia была первой конференцией по Zend [...]]]></description>
			<content:encoded><![CDATA[<p><img alt="" src="http://habreffect.ru/files/a10/cbbfacb76/zfconfua_and_mageconf.jpg" title="ZFConf Ukraine" class="aligncenter" width="236" height="137" /></p>
<p>27го ноября в Киеве пройдет бесплатная, международная совместная конференция <a href="http://zfconf.org.ua">ZFConf Ukraine</a> &#038; <a href="http://mageconf.com">MageConf</a>. Цель конференции &#8211; объединить лучших специалистов PHP и Zend Framework Украины, России и других стран СНГ.<br />
<span id="more-1620"></span><br />
ZFConf Ukraine – первая в Украине конференция по Zend Framework, проводится в партнерстве с <a href="http://www.zfconf.ru">ZFConf Russia</a> и сообществом <a href="http://zendframework.ru">zendframework.ru</a>. ZFConf Russia была первой конференцией по Zend Framework и прошла в марте этого года в Санкт-Петербурге, Россия, собрав более 200 участников из России, Беларуси, Украины, Литвы и других стран СНГ</p>
<p>Конференция MageConf впервые прошла в мае 2010 года в Киеве. Это была первая конференция из серии PHP and Zend Framework в Украине. Ее посетили более 150 специалистов.</p>
<p>Конференция пройдет <strong>27 ноября</strong>, в Киеве</p>
<ul>
<li>Место: КиевЭкспоПлаза, ул. Салютная 2б, 3 павильон</li>
<li>Время: 27 ноября 2010 г. (с 11:00 до 19:00)</li>
<li>Длительность: 1 полный день</li>
<li>Формат: доклады в три потока и обсуждения на флипчарте (в перерывах)</li>
<li>Язык докладов: русский</li>
<li>Стоимость участия: <b>бесплатно</b></li>
<li>Потоки: Zend Framework, PHP, Magento.</li>
<li>Аудитория: Разработчики ПО и специалисты по Project Management с Украины, России и близлежащих стран СНГ. Ожидаемое количество участников – 500 человек.</li>
</ul>
<h3><a href="http://zfconf.org.ua/conf-2010/category/topics/">Доклады</a></h3>
<ol>
<li>Встречайте Zend Framework 2.0 &mdash; Александр Веремьев / Zend Framework Core команда (<a href="http://www.zend.com/">Zend Technologies</a>)</li>
<li>Эволюция ZF: архитектура, шаблоны, рефакторинг &mdash; Виктор Фараздаги / Component Maintainer и Contributor ZF 2.0 (4C)</li>
<li>Юнит тестирование в Zend Framework 2.0 &mdash;  Ростислав Михайлив / Chief Developer / Software Architect (Gadu-Gadu)</li>
<li>Хранение, обработка и отдача статики с использованием \Zend \File. Опыт социальной сети starlook.ru &mdash; Кирилл Мокевнин / Senior developer (undev)</li>
<li>Чуть сложнее чем Singleton: аннотации, IOC, АОП &mdash; Кирилл Чебунин / Senior PHP Developer (Альпари)</li>
<li>Doctrine 2 &mdash; Валерий Рабиевский / Team leader (<a href="http://stfalcon.com">stfalcon.com</a>)</li>
</ol>
<p>Список еще будет обновляться, вы также можете <a href="http://zfconf.org.ua/contacts/">подать заявку на доклад</a>, если вам есть чем поделиться поделиться с общественностью. Также были бы рады услышать ваши комментарии по текущим темам.</p>
<p><a href="http://zfconf.org.ua/registration/">Регистрируясь</a> на одну конференцию, вы автоматически получаете доступ и на другую.</p>
<blockquote><p>По независящим от меня причинам, организовать ZFConf Kharkov у меня так и не получилось, но могу посоветовать всем моим читателям посетить данную конференцию &mdash; будет действительно интересно</p></blockquote>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/zend-framework-day/" title="Zend Framework Day">Zend Framework Day</a></li><li><a href="http://anton.shevchuk.name/html-and-css/master-klass-yuriy-akella-artyukh/" title="Мастер-класс Юры Артюха">Мастер-класс Юры Артюха</a></li><li><a href="http://anton.shevchuk.name/internet/sphinx-master-class/" title="Мастер-класс по Sphinx">Мастер-класс по Sphinx</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-2011-piter/" title="Конференция ZFConf 2011">Конференция ZFConf 2011</a></li><li><a href="http://anton.shevchuk.name/javascript/javascript-master-class-feedback/" title="Анонс мастер-классов по JavaScript&#8217;у">Анонс мастер-классов по JavaScript&#8217;у</a></li><li><a href="http://anton.shevchuk.name/javascript/javascript-master-class/" title="Мастер-классы по JavaScript&#8217;у">Мастер-классы по JavaScript&#8217;у</a></li><li><a href="http://anton.shevchuk.name/internet/pdfeed-rss-to-pdf-generator/" title="PDFEED &#8211; или RSS2PDF в новом обличье">PDFEED &#8211; или RSS2PDF в новом обличье</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/zfconf-ukraine/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>CouchDB для начинающих</title>
		<link>http://anton.shevchuk.name/php/couchdb-for-developers/</link>
		<comments>http://anton.shevchuk.name/php/couchdb-for-developers/#comments</comments>
		<pubDate>Mon, 13 Sep 2010 12:26:19 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1456</guid>
		<description><![CDATA[Решил на досуге в рабочее время поковырять CouchDB, если NoSQL вам в новинку, то эта статейка для вас окажется полезной. Установку CouchDB описывать не буду, собственно его сборкой я и не занимался. Перейдем сразу к Futon&#8217;у Futon После сборки и запуска CouchDB по адресу http://localhost:5984/_utils/ вам будет доступен web-интерфейс для управления БД &#8212; это и [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2010/09/couchdb-logo.png" alt="" title="CouchDB" width="220" height="200" class="aligncenter size-full wp-image-1475" /></p>
<p>Решил <strike>на досуге</strike> в рабочее время поковырять CouchDB, если NoSQL вам в новинку, то эта статейка для вас окажется полезной.<br />
<span id="more-1456"></span></p>
<blockquote><p>Установку CouchDB описывать не буду, собственно его сборкой я и не занимался. Перейдем сразу к Futon&#8217;у</p></blockquote>
<h3>Futon</h3>
<p>После сборки и запуска CouchDB по адресу <em>http://localhost:5984/_utils/</em> вам будет доступен web-интерфейс для управления БД &mdash; это и есть <a href="http://couchdb.apache.org/screenshots.html" title="Screenshots of Futon">Futon</a>. При помощи данного инструмента мы и будет проводить все описанные далее манипуляции.</p>
<h3>Создание базы данных</h3>
<p>Тут вроде все просто, кликаем Create Database:<br />
<img src="http://anton.shevchuk.name/wp-content/uploads/2010/09/create-db.png" alt="" title="Couch Db: Create Db" width="465" height="200" class="aligncenter size-full wp-image-1459" /></p>
<blockquote><p>Наблюдал странный баг, когда попытался создать БД &#8220;example&#8221; и &#8220;example/users&#8221;, Futon ругался, я не понял&#8230; Возможно он плохо дружит с БД в названии которых присутствует слэш.</p></blockquote>
<h3>Создание документов</h3>
<p>Теперь пора создавать документы по которым будем осуществлять дальнейший поиск. Возьмем тривиальную задачу &mdash; будем &laquo;забивать&raquo; библиотеку:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2010/09/document-1.png" alt="" title="Create Document" width="654" height="178" class="aligncenter size-full wp-image-1460" /></p>
<blockquote><p>Id документа должен быть строкой, по этой причине Id=1 заключен в кавычки, конечно можно использовать и автоматически генерируемое значение, и это даже правильно (иначе вам придется следить за ними на уровне приложения), но уж больно они громоздкие, по этой причине я буду сочинять свои.</p></blockquote>
<p>Немного погодя понимаешь, что необходимо сохранять не просто текстовое поле, а набор данных, нам понадобится массив или даже объект (после и перед скобочками необходимо ставить пробел или перенос каретки):</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2010/09/document-array.png" alt="" title="Document Array Property" width="654" height="53" class="aligncenter size-full wp-image-1462" /></p>
<p>Теперь можем чуть-чуть потренироваться опрашивая сервер:</p>
<p>Информация о БД &mdash; <strong>http://localhost:5984/simple/</strong>:</p>
<pre class="brush: jscript; title: ; notranslate">
{
&quot;db_name&quot;:&quot;simple&quot;,
&quot;doc_count&quot;:4,
&quot;doc_del_count&quot;:1,
&quot;update_seq&quot;:9,
&quot;purge_seq&quot;:0,
&quot;compact_running&quot;:false,
&quot;disk_size&quot;:36953,
&quot;instance_start_time&quot;:&quot;1284023758033358&quot;,
&quot;disk_format_version&quot;:5,
&quot;committed_update_seq&quot;:9
}
</pre>
<p>Вывод записи по Id &mdash; <strong>http://localhost:5984/simple/1/</strong>:</p>
<pre class="brush: jscript; title: ; notranslate">
{
&quot;_id&quot;:&quot;1&quot;,
&quot;_rev&quot;:&quot;3-eef52c024c2b27dadda90e0a510015b8&quot;,
&quot;title&quot;:&quot;Улитка на склоне&quot;,
&quot;ISBN&quot;:[&quot;978-5-17-057930-3&quot;,&quot;978-5-403-00677-4&quot;]
}
</pre>
<p>Вывод всех записей &mdash; <strong>http://localhost:5984/simple/_all_docs</strong>:</p>
<pre class="brush: jscript; title: ; notranslate">
{&quot;total_rows&quot;:4,&quot;offset&quot;:0,&quot;rows&quot;:[
{&quot;id&quot;:&quot;1&quot;,&quot;key&quot;:&quot;1&quot;,&quot;value&quot;:{&quot;rev&quot;:&quot;3-eef52c024c2b27dadda90e0a510015b8&quot;}},
{&quot;id&quot;:&quot;2&quot;,&quot;key&quot;:&quot;2&quot;,&quot;value&quot;:{&quot;rev&quot;:&quot;2-ccf196a429e2f118a7b558eb049e5773&quot;}},
{&quot;id&quot;:&quot;3&quot;,&quot;key&quot;:&quot;3&quot;,&quot;value&quot;:{&quot;rev&quot;:&quot;1-d49358e63c151f2f3ea7066146f24a66&quot;}},
{&quot;id&quot;:&quot;44&quot;,&quot;key&quot;:&quot;44&quot;,&quot;value&quot;:{&quot;rev&quot;:&quot;1-c0d28980139f7f4c372ca86306147b55&quot;}}
]}
</pre>
<p>Так же есть несколько <a href="http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options">дополнительных опции</a>, но на них остановимся чуть позже.</p>
<h3>REST API</h3>
<p>Но не Fucon&#8217;ом единым, CouchDB предоставляет REST интерфейс для работы с базой (собственно, Fucon через него и работает, можете посмотреть в консоли FireBug&#8217;a &mdash; очень наглядно), а это значит приблизительно следующее:</p>
<ul>
<li>Если мы хотим получить информацию &mdash; отправляем GET запрос (как в примерах выше)</li>
<li>Если надо создать документ &mdash; POST</li>
<li>Необходимо что-то изменить &mdash; PUT</li>
<li>COPY для копирования</li>
<li>DELETE для удаления</li>
</ul>
<p>Что-бы не быть голословным приведу пример создания и изменения документа:</p>
<pre class="brush: bash; title: ; notranslate">
# создаем документ, UUID генерируется автоматически
curl -X POST http://localhost:5984/simple -d '{&quot;title&quot;:&quot;Hello World!!!&quot;}' -H &quot;Content-Type:application/json&quot;
&gt;&gt; {&quot;ok&quot;:true,&quot;id&quot;:&quot;dbc21298bfb3f78ebe815cdb7000ae98&quot;,&quot;rev&quot;:&quot;1-f33e3c9b23e594593e93181763f0fb1b&quot;}

# создаем документ, UUID задаем явно
curl -X PUT http://localhost:5984/simple/hello -d '{&quot;title&quot;:&quot;Hello World!&quot;}' -H &quot;Content-Type:application/json&quot;
&gt;&gt; {&quot;ok&quot;:true,&quot;id&quot;:&quot;hello&quot;,&quot;rev&quot;:&quot;1-f33e3c9b23e594593e93181763f0fb1b&quot;}

# получение документа
curl -X GET http://localhost:5984/simple/hello
&gt;&gt; {&quot;_id&quot;:&quot;hello&quot;,&quot;_rev&quot;:&quot;1-f33e3c9b23e594593e93181763f0fb1b&quot;,&quot;title&quot;:&quot;Hello World!!!&quot;}

# редактирование документа, необходимо указывать ревизию редактируемого документа
curl -X PUT http://localhost:5984/simple/hello -d '{&quot;_rev&quot;:&quot;1-f33e3c9b23e594593e93181763f0fb1b&quot;,&quot;body&quot;:&quot;My world&quot;}' -H &quot;Content-Type:application/json&quot;
&gt;&gt; {&quot;ok&quot;:true,&quot;id&quot;:&quot;hello&quot;,&quot;rev&quot;:&quot;2-493961fb199ea564f532de44d8be2650&quot;}

# копирование документа
curl -X COPY http://localhost:5984/simple/hello -H &quot;Destination:hello2&quot;
&gt;&gt; {&quot;id&quot;:&quot;hello2&quot;,&quot;rev&quot;:&quot;1-1053fa7d0d20242aeeb1ba24ffd94977&quot;}

# получения всех документов
curl -X GET http://localhost:5984/simple/_all_docs
{&quot;total_rows&quot;:2,&quot;offset&quot;:0,&quot;rows&quot;:[
  {&quot;id&quot;:&quot;hello&quot;,&quot;key&quot;:&quot;hello&quot;,&quot;value&quot;:{&quot;rev&quot;:&quot;2-493961fb199ea564f532de44d8be2650&quot;}},
  {&quot;id&quot;:&quot;hello2&quot;,&quot;key&quot;:&quot;hello2&quot;,&quot;value&quot;:{&quot;rev&quot;:&quot;1-1053fa7d0d20242aeeb1ba24ffd94977&quot;}}
]}

# удаление документа
curl -X DELETE http://localhost:5984/simple/hello2?rev=1-1053fa7d0d20242aeeb1ba24ffd94977
&gt;&gt; {&quot;ok&quot;:true,&quot;id&quot;:&quot;hello2&quot;,&quot;rev&quot;:&quot;2-72d1c6aa20574c9444eae3d90715a862&quot;}

# получение UUID
curl -X GET http://localhost:5984/_uuids?count=1
&gt;&gt; {&quot;uuids&quot;:[&quot;dbc21298bfb3f78ebe815cdb70014788&quot;]}
</pre>
<h3>View</h3>
<p>Но вернемся к Fucon&#8217;у &mdash; настал черед создать <em>view</em>, идем в пункт &#8220;Temporary View&#8230;&#8221;:<br />
<img src="http://anton.shevchuk.name/wp-content/uploads/2010/09/create-view.png" alt="" title="Create Temporary View" width="247" height="80" class="aligncenter size-full wp-image-1464" /></p>
<p>Затем создаем <em>map</em> функцию:</p>
<pre class="brush: jscript; title: ; notranslate">
function(doc) {
  emit(doc.title, doc.genre);
}
</pre>
<blockquote><p>Функция <strong>emit</strong> принимает в качестве параметров ключ и значение, данный параметры могут быть простыми, как в примере выше, или составными &mdash; массив или объект. Историю почему функция носит такое имя я рассказать не могу, зваться бы ей push</p></blockquote>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2010/09/create-view-books.png" alt="" title="Сreate View" width="701" height="494" class="aligncenter size-full wp-image-1469" /></p>
<p>Cохраняем <em>view</em> как документ <em>_design/books</em> с именем <em>genre</em> &mdash; таки <em>view</em> это обычный документ в нашей БД, можем его даже пощупать:</p>
<pre class="brush: jscript; title: ; notranslate">
{
   &quot;_id&quot;: &quot;_design/books&quot;,
   &quot;_rev&quot;: &quot;1-532d13f2b2ef9dea495df8230f31b0bf&quot;,
   &quot;language&quot;: &quot;javascript&quot;,
   &quot;views&quot;: {
       &quot;genre&quot;: {
           &quot;map&quot;: &quot;function(doc) {\n  emit(doc.title, doc.genre);\n}&quot;
       }
   }
}
</pre>
<p>Результатом нашей работы будет следующий набор данных:</p>
<pre class="brush: jscript; title: ; notranslate">
// URL: http://localhost:5984/simple/_design/books/_view/genre
// UTF я привел к русскому

{&quot;total_rows&quot;:5,&quot;offset&quot;:0,&quot;rows&quot;:[
  {&quot;id&quot;:&quot;44&quot;,&quot;key&quot;:&quot;Будущее, ХХ век. Исследователи&quot;,&quot;value&quot;:&quot;fantastic&quot;},
  {&quot;id&quot;:&quot;55&quot;,&quot;key&quot;:&quot;Винни-Пух&quot;,&quot;value&quot;:&quot;child&quot;},
  {&quot;id&quot;:&quot;2&quot;,&quot;key&quot;:&quot;Пикник на обочине&quot;,&quot;value&quot;:&quot;fantastic&quot;},
  {&quot;id&quot;:&quot;3&quot;,&quot;key&quot;:&quot;Трудно быть богом&quot;,&quot;value&quot;:&quot;fantastic&quot;},
  {&quot;id&quot;:&quot;1&quot;,&quot;key&quot;:&quot;Улитка на склоне&quot;,&quot;value&quot;:&quot;fantastic&quot;}
]}
</pre>
<h3>Тем кто с SQL знаком</h3>
<h4>ORDER BY</h4>
<p>CouchDB сортирует выборку по ключу передаваемому в функцию <em>emit</em> первым параметром. Следовательно, если нам надо отсортировать по названию книги, то <em>map</em> функцию придется изменить:</p>
<pre class="brush: jscript; title: ; notranslate">
function(doc) {
  emit(doc.title, {title:doc.title, isbn:doc.ISBN});
}
</pre>
<p>Так же можно задать составной ключ:</p>
<pre class="brush: jscript; title: ; notranslate">
function(doc) {
  emit([doc.genre, doc.title], {title:doc.title, isbn:doc.ISBN});
}
</pre>
<p>Для обратного порядка есть параметр <strong>?descending=true</strong></p>
<h4>WHERE</h4>
<p>Большинство логики ложится непосредственно на функцию <em>map</em> &mdash; именно она в ответе за условия поиска:</p>
<pre class="brush: jscript; title: ; notranslate">
function(doc) {
    if (doc.authors.length &gt; 1)
        emit(doc._id, {title:doc.title, isbn:doc.ISBN});
}
</pre>
<p>Но так же есть возможность простой фильтрации по ключу:</p>
<pre class="brush: jscript; title: ; notranslate">
function(doc) {
    // ключом будет цена книги
    emit(doc.price, {title:doc.title, isbn:doc.ISBN});
}
</pre>
<p>Получить книги с определенной ценой: <strong>?key=235</strong><br />
Получить все книги из диапазона цен: <strong>?startkey=200&#038;endkey=300</strong></p>
<p>Если у нас составной ключ, то startkey и endkey должны быть составными: <strong>?startkey=[100]&#038;endkey=[300]</strong> (фильтр накладывается лишь на первый элемент составного ключа)</p>
<p>Если мы имеем дело с текстовым ключом, то можно получить конструкцию аналогичную LIKE &#8220;Ul%&#8221; используя следующий запрос: <strong>?startkey=&#8221;ul&#8221;&#038;endkey=&#8221;UL\ufff0&#8243;</strong>.</p>
<p>Порядок сортировки ключей следующий:</p>
<pre>
  ` ^ _ - , ; : ! ? . ' " ( ) [ ] { } @ * / \ &#038; # % + < = > | ~ $ 0 1 2 3 4 5 6 7 8 9
a A b B c C d D e E f F g G h H i I j J k K l L m M n N o O p P q Q r R s S t T u U v V w W x X y Y z Z
</pre>
<p>Больше информации о сортировке найдете в wiki: <a href="http://wiki.apache.org/couchdb/View_collation">View сollation</a> </p>
<h4>LIMIT и OFFSET</h4>
<p>Существуют следующие параметры <strong>?limit=5&#038;skip=10</strong>, вроде то что нужно, но, не ведитесь на эту провокацию, CouchDb все равно будет считывать пропущенные документы, но лишь отображать их не будет. Правильным является использование параметра startkey + limit. Алгоритм следующий:</p>
<ul>
<li>Выбираем необходимое количество элементов <em>rows_per_page</em> + еще один</li>
<li>Отображаем <em>rows_per_page</em> элементов пользователю</li>
<li>Запасной элемент (его ключ) используем для получения <em>next_startkey</em> (т.е. ссылки на следующую страницу)</li>
<li>Ключ первого элемента используем для получения ссылки на предыдущую страницу</li>
</ul>
<h4>SUM, COUNT</h4>
<p>Для подобных вычислений нам понадобится создать <em>reduce</em> функцию, именно она может обработать результаты работы <em>map</em> функции. Давайте вычислим количество книг:</p>
<pre class="brush: jscript; title: ; notranslate">
// map
function(doc) {
  // ключом будет жанр книги
  emit(doc._id, doc.title);
}
// reduce
function(keys,values) {
    return values.length;
}
// результат
{&quot;rows&quot;:[
  {&quot;key&quot;:null,&quot;value&quot;:4}
]}
// можно еще упростить данный пример
</pre>
<p>Или лучше среднюю стоимость книг:</p>
<pre class="brush: jscript; title: ; notranslate">
// map
function(doc) {
  emit(doc._id, doc.price);
}
// reduce
function(keys,values) {
    return Math.floor(sum(values)/values.length);
}
// результат
{&quot;rows&quot;:[
{&quot;key&quot;:null,&quot;value&quot;:233}
]}
</pre>
<p>Если надобности в <em>reduce</em> нет, то можно отключить, используя параметр <strong>?reduce=false</strong></p>
<blockquote><p>У функции <em>reduce</em> есть еще третий параметр &mdash; rereduce. Насколько я понял, в том случае, когда у нас очень большая база, документы в <em>reduce</em> функцию будут поступать пачками, и пока rereduce=false у нас все хорошо, и функция работает как обычно, но когда rereduce=true то к нам на вход попадут в качестве values промежуточные данные вычислений.</p></blockquote>
<h4>GROUP BY</h4>
<p>Для организации группировки нам понадобится <em>reduce</em> функция и параметр <strong>?group=true</strong>. Приведу пример вычисления средней стоимости книг, с группировкой по жанру:</p>
<pre class="brush: jscript; title: ; notranslate">
// map
function(doc) {
  // ключом будет жанр книги
  emit(doc.genre, doc.price);
}

// reduce
function(keys,values) {
    return Math.floor(sum(values)/values.length);
}

// результат
{&quot;rows&quot;:[
{&quot;key&quot;:&quot;child&quot;,&quot;value&quot;:145},
{&quot;key&quot;:&quot;fantastic&quot;,&quot;value&quot;:263}
]}
</pre>
<p>Так же возможна группировка по составному ключу, тут пригодится параметр <strong>?group_level=1</strong>, с его помощью можно указывать уровень группировки, приведу пример из руководства:</p>
<pre class="brush: jscript; title: ; notranslate">
// у нас есть следующие ключи
[&quot;a&quot;,1,1]
[&quot;a&quot;,3,4]
[&quot;a&quot;,3,8]
[&quot;b&quot;,2,6]
[&quot;b&quot;,2,6]
[&quot;c&quot;,1,5]
[&quot;c&quot;,4,2]
</pre>
<p>При <strong>?group_level=1</strong> функция <em>reduce</em> будет запущена 3 раза, по разу для каждого из следующих сетов:</p>
<pre class="brush: jscript; title: ; notranslate">
// set &quot;a&quot;
[&quot;a&quot;,1,1]
[&quot;a&quot;,3,4]
[&quot;a&quot;,3,8]
// set &quot;b&quot;
[&quot;b&quot;,2,6]
[&quot;b&quot;,2,6]
// set &quot;c&quot;
[&quot;c&quot;,1,5]
[&quot;c&quot;,4,2]
</pre>
<p>При <strong>?group_level=2</strong> функция <em>reduce</em> будет запущена 5 раз, по разу для каждого из следующих сетов:</p>
<pre class="brush: jscript; title: ; notranslate">
// set &quot;a&quot;, 1
[&quot;a&quot;,1,1]
// set &quot;a&quot;, 3
[&quot;a&quot;,3,4]
[&quot;a&quot;,3,8]
// set &quot;b&quot;, 2
[&quot;b&quot;,2,6]
[&quot;b&quot;,2,6]
// set &quot;c&quot;,1
[&quot;c&quot;,1,5]
// set &quot;c&quot;,4
[&quot;c&quot;,4,2]
</pre>
<p>Возвращаясь к нашим книгам, то можно сделать следующий финт ушами (<strong>?group_level=2</strong>):</p>
<pre class="brush: jscript; title: ; notranslate">
// map
function(doc) {
  // первый параметр ключа - жанр, второй - цена в сотнях
  emit([doc.genre, Math.floor(doc.price/100)], doc.price);
}
// reduce
function(keys,values) {
    return Math.floor(sum(values)/values.length);
}
// результат
{&quot;rows&quot;:[
{&quot;key&quot;:[&quot;child&quot;,1],&quot;value&quot;:145},
{&quot;key&quot;:[&quot;fantastic&quot;,2],&quot;value&quot;:244},
{&quot;key&quot;:[&quot;fantastic&quot;,3],&quot;value&quot;:302}
]}
</pre>
<h3>Связанные документы</h3>
<p>Теперь надо бы добавить авторов книг, но как-то не очень хочется добавлять к каждому документу объект со всеми свойствами, так и хочется создать новую таблицу &laquo;пользователи&raquo;, но у нас не та БД. По этой причине создаем новые документы с атрибутом <em>type=author</em>:</p>
<pre class="brush: jscript; title: ; notranslate">
{
   &quot;_id&quot;: &quot;author_1&quot;,
   &quot;name&quot;: &quot;Alan Alexander Milne&quot;,
   &quot;type&quot;: &quot;author&quot;
}
</pre>
<p>В книгах добавляем <em>type=book</em> и ссылку на автора:</p>
<pre class="brush: jscript; title: ; notranslate">
{
   /*...*/
   &quot;authors&quot;: [
       &quot;author_1&quot;
   ],
   &quot;type&quot;: &quot;book&quot;
}
</pre>
<p>Теперь надо создать <em>view</em>, чтобы возвращались лишь книги:</p>
<pre class="brush: jscript; title: ; notranslate">
function(doc) {
    if (doc.type != &quot;book&quot;) return;
    emit(doc._id, {title:doc.title, isbn:doc.ISBN});
}
</pre>
<p>Чтобы получить связанные документы (авторов из нашего примера) в функцию <em>emit</em> необходимо передать в качестве параметра объект следующего вида:</p>
<pre class="brush: jscript; title: ; notranslate">
{
    _id:&quot;author_2&quot;
}
</pre>
<p>А <em>map</em> функция приобретет следующий вид:</p>
<pre class="brush: jscript; title: ; notranslate">
function(doc) {
  if (doc.type != &quot;book&quot;) return;

  emit([doc._id, 'book', 0], {title:doc.title, isbn:doc.ISBN});
  if (doc.authors) {
    for (var i in doc.authors) {
      emit([doc._id, 'author', Number(i)+1], {_id:doc.authors[i]})
    }
  }
}
</pre>
<p>Но и этого мало, запрос при этом должен иметь вид <em>http://localhost:5984/simple/_design/books/_view/all<strong>?include_docs=true</strong></em>:</p>
<pre class="brush: jscript; title: ; notranslate">
{&quot;total_rows&quot;:14,&quot;offset&quot;:0,&quot;rows&quot;:[
    {
        &quot;id&quot;:&quot;1&quot;,
        &quot;key&quot;:[&quot;1&quot;,&quot;author&quot;,1],
        &quot;value&quot;:{&quot;_id&quot;:&quot;author_2&quot;},
        &quot;doc&quot;:{&quot;_id&quot;:&quot;author_2&quot;,&quot;_rev&quot;:&quot;1-d39dd295bf1b91f27e329362a727eaf8&quot;,&quot;name&quot;:&quot;Arcadiy Strugackiy&quot;,&quot;type&quot;:&quot;author&quot;}
    },
    {
        &quot;id&quot;:&quot;1&quot;,
        &quot;key&quot;:[&quot;1&quot;,&quot;author&quot;,2],
        &quot;value&quot;:{&quot;_id&quot;:&quot;author_3&quot;},
        &quot;doc&quot;:{&quot;_id&quot;:&quot;author_3&quot;,&quot;_rev&quot;:&quot;1-eeaadba334acb0afdd5d4d7a6eb3a11b&quot;,&quot;name&quot;:&quot;Boris Strugackiy&quot;,&quot;type&quot;:&quot;author&quot;}
    },
    {
        &quot;id&quot;:&quot;1&quot;,
        &quot;key&quot;:[&quot;1&quot;,&quot;doc&quot;,0],
        &quot;value&quot;:{&quot;title&quot;:&quot;Улитка на склоне&quot;,&quot;isbn&quot;:[&quot;978-5-17-057930-3&quot;,&quot;978-5-403-00677-4&quot;]},
        &quot;doc&quot;:{
            &quot;_id&quot;:&quot;1&quot;,
            &quot;_rev&quot;:&quot;8-1777bfc0f098b2382543ec5e48bc3b44&quot;,
            &quot;title&quot;:&quot;Улитка на склоне&quot;,&quot;ISBN&quot;:[&quot;978-5-17-057930-3&quot;,&quot;978-5-403-00677-4&quot;],
            &quot;genre&quot;:&quot;fantastic&quot;,&quot;price&quot;:235,&quot;type&quot;:&quot;book&quot;,
            &quot;authors&quot;:[&quot;author_2&quot;,&quot;author_3&quot;]
        }
    },
    /* ... skip ... */
    {
        &quot;id&quot;:&quot;55&quot;,
        &quot;key&quot;:[&quot;55&quot;,&quot;author&quot;,1],
        &quot;value&quot;:{&quot;_id&quot;:&quot;author_1&quot;},
        &quot;doc&quot;:{&quot;_id&quot;:&quot;author_1&quot;,&quot;_rev&quot;:&quot;3-b3bce00a63620dbf4dd14b1337802e78&quot;,&quot;name&quot;:&quot;Alan Alexander Milne&quot;,&quot;type&quot;:&quot;author&quot;}
    },
    {
        &quot;id&quot;:&quot;55&quot;,
        &quot;key&quot;:[&quot;55&quot;,&quot;doc&quot;,0],
        &quot;value&quot;:{&quot;title&quot;:&quot;Винни-Пух&quot;,&quot;isbn&quot;:[&quot;978-5-17-066600-3&quot;,&quot;978-5-271-27610-1&quot;,&quot;978-985-16-8371-6&quot;]},
        &quot;doc&quot;:{
            &quot;_id&quot;:&quot;55&quot;,
            &quot;_rev&quot;:&quot;4-ea5acfb3800e0414bf20fb2a5150a586&quot;,
            &quot;title&quot;:&quot;Винни-Пух&quot;,&quot;ISBN&quot;:[&quot;978-5-17-066600-3&quot;,&quot;978-5-271-27610-1&quot;,&quot;978-985-16-8371-6&quot;],
            &quot;genre&quot;:&quot;child&quot;,&quot;authors&quot;:[&quot;author_1&quot;],&quot;type&quot;:&quot;book&quot;
        }
    }
]}
</pre>
<p>Теперь остается собрать этот view в человеческий вид, и так хочется использовать <em>reduce</em> функцию, но вот беда, с параметром <strong>?include_docs=true</strong> это невозможно.</p>
<blockquote><p>Я не думаю, что это правильно и соответствует идеологии CouchDB, скорей всего стоит забыть о JOIN-подобных конструкциях</p></blockquote>
<h3>Немного о правах доступа</h3>
<p>Если вы уже успели немного изучить интерфейс Fucon&#8217;а, то уже обратили внимание на кнопочку &#8220;Security&#8221;, нажимаем &mdash; в появившемся окошке можно установить имена/роли админов и пользователей (первые могут редактировать документы, вторые &mdash; не должен):<br />
<img src="http://anton.shevchuk.name/wp-content/uploads/2010/09/security.png" alt="" title="security" width="461" height="391" class="aligncenter size-full wp-image-1488" /></p>
<p>Если список reader&#8217;ов пуст, то БД считается публичной, и для чтения данных пароль не потребуется, иначе потребуется авторизация:</p>
<pre class="brush: bash; title: ; notranslate">
# получение документа + авторизация
curl -X GET http://username:password@localhost:5984/simple/hello
&gt;&gt; {&quot;_id&quot;:&quot;hello&quot;,&quot;_rev&quot;:&quot;1-f33e3c9b23e594593e93181763f0fb1b&quot;,&quot;title&quot;:&quot;Hello World!!!&quot;}
</pre>
<blockquote><p>Собственно, мы редактируем документ <strong>db_name/_security</strong>, единственное отличие, он не имеет ревизии, дабы увеличить скорость работы авторизации</p></blockquote>
<p>Поддерживаются следующие обработчики для авторизации:</p>
<ul>
<li>OAuth</li>
<li>cookie</li>
<li>default (используется для HTTP авторизации, описанной в RFC 2617)</li>
</ul>
<p>Для каждого HTTP запроса запускаются все обработчики по очереди, как только кто-то опознает пользователя дальнейшее выполнение прерывается (очередность определяется конфигом).</p>
<p>Теперь можно попробовать создать несколько пользователей с разными ролями, Logout &raquo; Signup:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2010/09/create-user.png" alt="" title="Create User" width="461" height="216" class="aligncenter size-full wp-image-1489" /></p>
<p>Результатом станет появление документа в БД _users:</p>
<pre class="brush: jscript; title: ; notranslate">
{
   &quot;_id&quot;: &quot;org.couchdb.user:guest&quot;,
   &quot;_rev&quot;: &quot;1-9eeb0dcc95d472849590885bcc5f242c&quot;,
   &quot;name&quot;: &quot;guest&quot;,
   &quot;salt&quot;: &quot;dbc21298bfb3f78ebe815cdb7000e180&quot;,
   &quot;password_sha&quot;: &quot;3d447e52765d76064223f501f16a9213d43a5ee5&quot;,
   &quot;type&quot;: &quot;user&quot;,
   &quot;roles&quot;: []
}
</pre>
<p>Побуем:</p>
<pre class="brush: bash; title: ; notranslate">
curl -X GET http://guest:123456@localhost:5984/simple/_all_docs
&gt;&gt; {&quot;error&quot;:&quot;unauthorized&quot;,&quot;reason&quot;:&quot;You are not authorized to access this db.&quot;}
</pre>
<p>Добавим ему роль <em>reader</em> и пробуем:</p>
<pre class="brush: bash; title: ; notranslate">
 curl -X GET http://guest:123456@localhost:5984/simple/_all_docs
&gt;&gt; {&quot;total_rows&quot;:1,&quot;offset&quot;:0,&quot;rows&quot;:[{&quot;id&quot;:&quot;hello&quot;,&quot;key&quot;:&quot;hello&quot;,&quot;value&quot;:{&quot;rev&quot;:&quot;4-2edebeac5a6e202b7d468c2d2fcd434c&quot;}}]}
</pre>
<p>Теперь пробуем редактировать:</p>
<pre class="brush: bash; title: ; notranslate">
curl -X POST http://guest:123456@localhost:5984/simple -d '{&quot;title&quot;:&quot;Hello World!!!&quot;}' -H &quot;Content-Type:application/json&quot;
&gt;&gt; {&quot;ok&quot;:true,&quot;id&quot;:&quot;dbc21298bfb3f78ebe815cdb7000eb31&quot;,&quot;rev&quot;:&quot;1-f33e3c9b23e594593e93181763f0fb1b&quot;}
</pre>
<p>Результат немного неожиданный, документ создали, хоть guest=reader. В действительности для разграничений прав доступа необходимо создать функцию валидации данных, и в ней организовывать логику разделения прав. Для этой цели создайте любой <em>design</em> документ с атрибутом <em>validate_doc_update</em>, в котором и будет описана наша функция:</p>
<pre class="brush: jscript; title: ; notranslate">
{
   &quot;_id&quot;: &quot;_design/validate&quot;,
   &quot;_rev&quot;: &quot;4-e53c54b69a5916ddc06a4fd16f4b299d&quot;,
   &quot;validate_doc_update&quot;: &quot;function(new_doc, old_doc, userCtx) { /* code here */  }&quot;
}
</pre>
<p>К примеру, функция которая разрешает доступ лишь для редакторов будет иметь следующий вид:</p>
<pre class="brush: jscript; title: ; notranslate">
function(new_doc, old_doc, userCtx) {
    if(userCtx.roles.indexOf(&quot;editor&quot;) === -1) {
      throw({unauthorized: &quot;You are not an editor.&quot;});
    }
}
</pre>
<h3>Валидация</h3>
<p>Прочитав предыдущий параграф возникает резонная мысль &mdash; значит CouchDB позволяет проверять данные перед занесением их в БД. О, да, и вот тому наглядный такой пример:</p>
<pre class="brush: jscript; title: ; notranslate">
function(newDoc, oldDoc, userCtx) {
  function require(field, message) {
    message = message || &quot;Document must have a &quot; + field;
    if (!newDoc[field]) throw({forbidden : message});
  };

  if (newDoc.type == &quot;post&quot;) {
    require(&quot;title&quot;);
    require(&quot;created_at&quot;);
    require(&quot;body&quot;);
    require(&quot;author&quot;);
  }
  if (newDoc.type == &quot;comment&quot;) {
    require(&quot;name&quot;);
    require(&quot;created_at&quot;);
    require(&quot;comment&quot;, &quot;You may not leave an empty comment&quot;);
  }
}
</pre>
<h3>Хранение файлов</h3>
<p>Отдельно стоит отметить, что файлы стоит хранить в самой CouchDB, так как хранятся они как есть на файловой системе, без всяких извращений, как то свойственно SQL. Из Fucon&#8217;а вы сможете легко добавить несколько аттачей к документу:</p>
<pre class="brush: jscript; title: ; notranslate">
{
   &quot;_id&quot;: &quot;1&quot;,
   &quot;_rev&quot;: &quot;10-6754a5ad1330a6b8ebb544d052f2b03c&quot;,
   &quot;title&quot;: &quot;Ulitka na sklone&quot;,
   &quot;_attachments&quot;: {
       &quot;ulitka.jpeg&quot;: { // данный файл будет доступен по адресу http://localhost:5984/simple/1/ulitka.jpeg
           &quot;content_type&quot;: &quot;image/jpeg&quot;,
           &quot;revpos&quot;: 10,
           &quot;length&quot;: 25227,
           &quot;stub&quot;: true
       }
   }
}
</pre>
<h3>Документация</h3>
<ul>
<li><a href="http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views">Introduction to CouchDB views</a></li>
<li><a href="http://wiki.apache.org/couchdb/HTTP_Document_API">HTTP Document API</a></li>
<li><a href="http://wiki.apache.org/couchdb/HTTP_view_API">HTTP view API</a></li>
<li><a href="http://wiki.apache.org/couchdb/View_Snippets">View Snippets</a> (полезные примеры)</li>
<li><a href="http://labs.mudynamics.com/wp-content/uploads/2009/04/icouch.html">Interactive CouchDB</a> (можно попробовать в живую)</li>
<li><a href="http://guide.couchdb.org/draft/recipes.html">Recipes</a></li>
<li><a href="http://guide.couchdb.org/draft/security.html">Security</a></li>
<li><a href="http://guide.couchdb.org/draft/validation.html">Validation</a></li>
<li><a href="http://sitr.us/2009/06/30/database-queries-the-couchdb-way.html">Database Queries the CouchDB Way</a></li>
</ul>
<p>Обзор CouchDB 1.0 и сравнение с версией 0.11, полезно почитать:</p>
<ul>
<li><a href="http://blog.couchone.com/post/443028592/whats-new-in-apache-couchdb-0-11-part-one-nice-urls">Nice URLs with Rewrite Rules and Virtual Hosts</a></li>
<li><a href="http://blog.couchone.com/post/446015664/whats-new-in-apache-couchdb-0-11-part-two-views">Views; JOINs Redux, Raw Collation for Speed</a></li>
<li><a href="http://blog.couchone.com/post/468392274/whats-new-in-apache-couchdb-0-11-part-three-new">New Features in Replication</a></li>
<li><a href="http://blog.couchone.com/post/1027100082/whats-new-in-couchdb-1-0-part-4-securityn-stuff">Users, Authentication, Authorisation and Permissions</a></li>
</ul>
<p>P.S. Есть чем дополнить/исправить &mdash; пишите комментарии.</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li>No Related Post</li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/couchdb-for-developers/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>Facing refucktoring</title>
		<link>http://anton.shevchuk.name/php/facing-refucktoring/</link>
		<comments>http://anton.shevchuk.name/php/facing-refucktoring/#comments</comments>
		<pubDate>Thu, 19 Aug 2010 08:48:30 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1364</guid>
		<description><![CDATA[История одного рефакторинга, или о сказ о том как не надо разрабатывать на PHP&#8230; Началось все как всегда, откуда-то появляется заказчик весь в слезах и трансе, орет не своим голосом: &#171;Спасите, помогите, мой проект тормозит, от меня пользователи бегут. У вас неделя&#8230;&#187; Развернули проект на нашем сервере &#8212; н-да уж&#8230; Дальше был анализ сего творения [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://anton.shevchuk.name/wp-content/uploads/2010/08/Mission-Impossible.jpg"><img src="http://anton.shevchuk.name/wp-content/uploads/2010/08/Mission-Impossible.jpg" alt="" title="Mission Impossible" width="451" height="323" class="aligncenter size-full wp-image-1375" /></a></p>
<p>История одного рефакторинга, или о сказ о том как не надо разрабатывать на PHP&#8230;<br />
<span id="more-1364"></span><br />
Началось все как всегда, откуда-то появляется заказчик весь в слезах и трансе, орет не своим голосом: &laquo;Спасите, помогите, мой проект тормозит, от меня пользователи бегут. У вас неделя&#8230;&raquo;</p>
<p>Развернули проект на нашем сервере &mdash; н-да уж&#8230; Дальше был анализ сего творения фирмы &laquo;Q&raquo; (кто <a href="http://twitter.com/AntonShevchuk">следит за мной</a>, знает о какой украинской фирме речь), смех сквозь слезы, и истерика в течении недели. Ну для начала был взят XDEBUG и посмотрели профайлером что там и как (кликабельно):<br />
<a href="http://anton.shevchuk.name/wp-content/uploads/2010/08/splash-rev-0.png"><img src="http://anton.shevchuk.name/wp-content/uploads/2010/08/splash-rev-0-720x375.png" alt="" title="Splash Screen" width="720" height="375" class="aligncenter size-medium wp-image-1427" /></a></p>
<blockquote><p>Такую клевую штуку рисует <a href="http://code.google.com/p/webgrind/">Webgrind</a>, удобно и наглядно</p></blockquote>
<p>Т.е. у нас страница, которая выводит приветствие + выбор языка + выбор категории для просмотра (захардкоденных кстати) генерировалась за 4,6 секунды на сервере с Core2 Quad CPU Q6600@2.40GHz/16Gb (истерика и лучи ненависти конторе &laquo;Q&raquo;).</p>
<p>Куда более жутка картина ожидала нас на главной странице:<br />
<a href="http://anton.shevchuk.name/wp-content/uploads/2010/08/main-rev-0.png"><img src="http://anton.shevchuk.name/wp-content/uploads/2010/08/main-rev-0-720x155.png" alt="" title="Main Page" width="720" height="155" class="aligncenter size-medium wp-image-1426" /></a></p>
<p>До применения Zend Framework&#8217;a нас отделяло еще несколько недель оптимизации г..кода&#8230;</p>
<h3>FastTemplate такой &laquo;Fast&raquo;</h3>
<p>Если присмотреться, то на предыдущем скрине видно, что очень дорого нам обходится некая функция <em>parse_body</em>:</p>
<p><a href="http://anton.shevchuk.name/wp-content/uploads/2010/08/main-parse-rev-0.png"><img src="http://anton.shevchuk.name/wp-content/uploads/2010/08/main-parse-rev-0-720x458.png" alt="" title="Main Page and FastTemplate" width="720" height="458" class="aligncenter size-medium wp-image-1425" /></a></p>
<p>Внутри нас ждет:</p>
<ul>
<li>вызов strtoupper &mdash; 56560 раз</li>
<li>вызов str_replace &mdash; 56560 раз</li>
</ul>
<p>Смотрим на код:</p>
<pre class="brush: php; title: ; notranslate">
// $content - шаблон
// $rec - переменные
$content = preg_replace(&quot;/\\$([A-Z][A-Z0-9_]+)/&quot;, &quot;@\\1@&quot;, $content);
if (is_array($rec)) {
    foreach ($rec as $key =&gt; $value) {
        $name = strtoupper($key);
        $content = str_replace(&quot;@$name@&quot;, &quot;$value&quot;, &quot;$content&quot;);
    }
}
return $content;
</pre>
<p>Т.е. в самом начале мы ищем в шаблонах что-то вроде $HTTP_PATH, заменяем это на @HTTP_PATH@, потом перебором пытаемся заменить все известные переменные. Есть одна проблема &mdash; шаблонов у нас много, в них используется совсем чуть-чуть из пары сотен переменных. Простая замена str_replace на preg_replace_callback дала прирост в пару секунд (т.е. около 10%).</p>
<h3>Примеры кода</h3>
<p>Слабонервным лучше пропустить данный абзац.</p>
<p>Классика плохого PHP кода, встречается у индусов и наших студентов:</p>
<pre class="brush: php; title: ; notranslate">
function n() {
   global $db, $CONSTANTS, $user, ...; // много одним словом

   echo $CONSTANTS[HOMEPAGE_BANNER1_ID];  // думаете это константа?
   echo $CONSTANTS[HOMEPAGE_BANNER2_ID];  // а знаете, что внутри?
   echo $CONSTANTS[HOMEPAGE_BANNER3_ID];  // 1, 2, 3, которые меняются совсем не там где объявляются О_о
}
</pre>
<p>Использование файловой системы вместо системы контроля версий:</p>
<pre>
tpl
|-- index.tpl
|-- index2.tpl
|-- index3.tpl
|-- index__.tpl
`-- index.44.tpl
</pre>
<p>С базой данных тот же номер &mdash; таблицы categories_old, items_1 и т.д.</p>
<blockquote><p>Если мальчик любит труд<br />
тычет в книжку пальчик,<br />
про такого пишут тут:<br />
он хороший мальчик.</p></blockquote>
<p>Это, как вы понимаете, не про наших &laquo;мальчиков&raquo;, у наших 9 000 notices на главной. Да и manual&#8217;ами пользуются только слабаки:</p>
<pre class="brush: php; title: ; notranslate">
// мы не читаем мануалов
while ($row = mysql_fetch_array($res)) {
      if ($row) {
            foreach ($row AS $key =&gt; $field) {
                if (ereg(&quot;^[0-9]+&quot;, $key)) {
                    unset($row[$key]);
                }
            }
      }
      $rows[] = $row;
}
// если чуть-чуть допилить
// мелочь, конечно, но прирост ~0,1 сек т.к. имеет место 23162 вызовов
while ($row = mysql_fetch_array($res, MYSQL_ASSOC)) {
      $rows[] = $row;
}
</pre>
<p>Далее просто гениальное решение, тайный смысл этого творения я не осилил:</p>
<pre class="brush: php; title: ; notranslate">
$sqls[] = $sql;
if (is_array($sqls)) {
    foreach ($sqls AS $ssql) {
        if ($ssql) {
            $res = mysql_db_query($db['name'], $ssql, $db['id']);
            if (!$res) {
                return 0;
            }
        }
    }
} else {
    return 0;
}
</pre>
<p>Подсчет результатов поиска, что может быть проще:</p>
<pre class="brush: php; title: ; notranslate">
// перед выполнением запроса можно подсчитать кол-во результатов
// воспользовавшись функцией db_count (вызывается в 96 разных местах)
function db_count($sql) {
    global $db;
    $res = mysql_db_query($db['name'], $sql, $db['id']);
    $result = mysql_num_rows($res);
    return $result;
}
</pre>
<p>Постраничная навигация, и это тоже можем:</p>
<pre class="brush: php; title: ; notranslate">
// пошел запрос к БД
$res = mysql_db_query($db['name'], $sql, $db['id']);
// подсчитали итого (хотя до этого уже был вызван db_count)
$row_count = mysql_num_rows($res);
// подсчитали сколько у нас страниц получается
$page_count = floor($row_count / $pager['per_page'] + 1);

// это offset
$bi = ($pos - 1) * $pager['per_page'];

// теперь выбираем только нужные записи
for ($i = $bi; $i &lt; $bi + $pager[&quot;per_page&quot;]; $i++) {
    if ($i &gt;= $row_count) {
        break;
    }

    if (!mysql_data_seek($res, $i)) {
        break;
    }

    if (!($row = mysql_fetch_assoc($res))) {
        break;
    }
    // складируем результат
    $new_rows[] = $row;
}
</pre>
<p>Повторение строк &mdash; мы не ищем легких путей (str_repeat):</p>
<pre class="brush: php; title: ; notranslate">
// в файле categories.sql.php
// функция которая строит select для HTML
$offset_string  = '';
for ($i = 1; $i &lt; $rec['level']; $i++) {
    $offset_string .= '&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;';
}
</pre>
<p>Если нам надо обрезать строку на 100 символов, и при этом не кромсать слова то вот оно решение:</p>
<pre class="brush: php; title: ; notranslate">
$data['row']['description'] = substr($description, 0, 100);

$i = 100;
while (!($description[$i] == &quot; &quot; || $description[$i] == &quot;_&quot;) &amp;&amp; $i &lt; strlen($description)):
    $data['row']['description'] .= $description[$i];
    $i++;
endwhile;

if ($i &lt; strlen($description)) {
    $data['row']['description'] .= &quot;...&quot;;
}
</pre>
<p>У нас так много глобальных переменных, там есть конечно $db, и она же передается во все функции которые работают с БД:</p>
<pre class="brush: php; title: ; notranslate">
function db_query($db, $sql) {}
function db_sql_query($db, $sql) {}
function db_count($db, $sql) {}
// и т.д.
// но почему не так, ведь у нас одна БД
function db_query($sql) {
    global $db;
}
</pre>
<p>Пусть на море качка, но мы всегда прибережем обходные пути:</p>
<pre class="brush: php; title: ; notranslate">
$sql = &quot;SELECT id, name, pasw from users where name = '$_POST[username]'&quot;;
</pre>
<p>Про агрегирование в SQL мы не знаем:</p>
<pre class="brush: php; title: ; notranslate">
// $rows - записи из БД
foreach ($rows as $value) {
      $total += $value[&quot;price&quot;];
}
</pre>
<p>Необходимо SEO URL? Не проблема:</p>
<pre class="brush: php; title: ; notranslate">
switch ($params[1]):
       case &quot;usageagreement&quot;:
            $page_id = 13;
       break;
       case &quot;privacypolicy&quot;:
            $page_id = 14;
       break;
       case &quot;termsandconditions&quot;:
            $page_id = 15;
       break;
       case &quot;affiliates&quot;:
            $page_id = 22;
       break;
       case &quot;aboutus&quot;:
            $page_id = 19;
       break;
endswitch;
</pre>
<p>Ладно с PHP, но HTML то можно было подучить:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!-- id такой id --&gt;
&lt;div id=&quot;banner&quot;&gt;...&lt;/div&gt;
&lt;div id=&quot;banner&quot;&gt;...&lt;/div&gt;
&lt;div id=&quot;banner&quot;&gt;...&lt;/div&gt;
&lt;div id=&quot;banner&quot;&gt;...&lt;/div&gt;

&lt;!-- class это почти style --&gt;
&lt;li class=&quot;padding-left:15px;&quot;&gt;...&lt;/li&gt;

&lt;!-- табличная верстка --&gt;
&lt;!-- хотя не стоит 10 вложенных таблиц расписывать --&gt;

&lt;!-- margin, что такое margin? --&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
</pre>
<h3>Применение Zend_Cache</h3>
<p>Кеш спасет мир, подумали мы и прикрутили его для всех SQL запросов (благо кто-то догадался написать единую функцию <em>db_sql_query</em>) и всех вызовов <em>parse_body</em>. Для начала попробовали кешировать в файлы, на тестовом сервере это помогло, на живом &mdash; нет. Причина &mdash; у нас так много мелких шаблонов (~200 для главной), что операции с файловой системой свели на нет прирост кеширования.</p>
<p>Вторая попытка оказалась более удачной, решили применить memcache &mdash; прирост скорости ~180%. Какой клевый показатель, но верен лишь в 100% попадании в кеш, таким образом перед нами вырисовывалась перспектива полного рефакторинга системы.</p>
<h3>Немного клиентской оптимизации</h3>
<p>Ну что тут можно рассказать, простое добавление следующих правил в .htaccess сильно облегчило навигацию пользователям, которые хоть раз заходили на сайт:</p>
<pre class="brush: bash; title: ; notranslate">
FileETag MTime Size

AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/x-javascript

&lt;ifModule mod_expires.c&gt;
  ExpiresActive On
  ExpiresDefault &quot;access plus 1 seconds&quot;
  ExpiresByType text/html &quot;access plus 1 seconds&quot;
  ExpiresByType image/x-icon &quot;access plus 2592000 seconds&quot;
  ExpiresByType image/gif &quot;access plus 2592000 seconds&quot;
  ExpiresByType image/jpeg &quot;access plus 2592000 seconds&quot;
  ExpiresByType image/png &quot;access plus 2592000 seconds&quot;
  ExpiresByType text/css &quot;access plus 604800 seconds&quot;
  ExpiresByType text/javascript &quot;access plus 216000 seconds&quot;
  ExpiresByType application/x-javascript &quot;access plus 216000 seconds&quot;
&lt;/ifModule&gt;
</pre>
<p>А далее все по порядку:</p>
<ul>
<li>Оптимизация изображений для web (есть такой пункт в Photoshop) &mdash; 40% на всех JPEG фалах</li>
<li>Спрайты &mdash; кропотливая работа, десятки обращений к серверу можно свести к единицам</li>
<li>Жмем JavaScript и CSS &mdash; ~50%</li>
<li>И последним пунктом &mdash; nginx для всего этого добра</li>
</ul>
<h3>Zend Framework</h3>
<p>А теперь расскажу о том, как проект медленно переезжает на Zend Framework. Начинается всё с простой проверки в index.php (о да в нашей системе одна точка входа):</p>
<pre class="brush: php; title: ; notranslate">
// список модулей, которые уже отрефакторили
$modules = array (
    '/search/',
    '/about/'
);

$path = $_SERVER['REQUEST_URI'];

// нас устроила такая простая проверка,
// но запрос вида /search/?... уже не будет обрабатываться
if(in_array($path, $modules)) {
    // подключаем ZF (внутри стандартный код из сгенерированного public/index.php)
    require 'loader.php';
    exit();
}

// а эта будет
foreach ($modules as $module) {
    if (strpos($path, $module) === 0) {
        require 'loader.php';
        exit();
    }
}
</pre>
<p>Если у нас не одна точка входа, то в каждом файле, которые были затронуты делаем простую вставку:</p>
<pre class="brush: php; title: ; notranslate">
$_SERVER['REQUEST_URI'] = str_replace($_SERVER['PHP_SELF'], '/admin/', $_SERVER['REQUEST_URI']);
require 'loader.php';
exit();
</pre>
<p>Что-бы забыть &laquo;глобальный&raquo; ужас, жизненно-необходимые переменный были закинуты в Zend_Registry (а в дальнейшем закинуты в конфигурационный файл application.ini, где им самое место).</p>
<p>Так же Zend_Translate была скормлена таблица с переводами (см. адаптер array)</p>
<h3>Результат</h3>
<p>Сложно судить о результате, проект находится в разработке, но вот несколько сравнительных замеров:</p>
<table cellspacing="1" class="inline">
<tr>
<th></th>
<th>Время генерации</th>
<th>Время генерации (re)</th>
<th>Время генерации (ZF)</th>
<th>Объем страницы</th>
<th>Объем страницы (ZF)</th>
</tr>
<tr>
<th>Главная страница</th>
<td align="right">4 663ms</td>
<td align="right">2 759ms</td>
<td align="right">&mdash;</td>
<td align="right">699.5Kb</td>
<td align="right">288.0Kb</td>
</tr>
<tr>
<th>Статические страницы</th>
<td align="right">3 115ms</td>
<td align="right">2 008ms</td>
<td align="right">&nbsp;295ms</td>
<td align="right">263.3Kb</td>
<td align="right">166.2Kb</td>
</tr>
<tr>
<th>Cтраница айтема</th>
<td align="right">3 082ms</td>
<td align="right">1 745ms</td>
<td align="right">&nbsp;180ms</td>
<td align="right">589.1Kb</td>
<td align="right">260.8Kb</td>
</tr>
</table>
<p>Еще наглядный скриншот среднего/максимального времени генерации страниц по датам: <a href="http://screencast.com/t/NDY1NGE5">http://screencast.com/t/NDY1NGE5</a></p>
<p><!--c5246b3f2eff43858fc1dea4d499898d--></p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/zfconf-2011-piter/" title="Конференция ZFConf 2011">Конференция ZFConf 2011</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-feedback/" title="Под впечатлением от ZFConf">Под впечатлением от ZFConf</a></li><li><a href="http://anton.shevchuk.name/php/write-custom-provider-for-zend_tool/" title="Пишем свой провайдер для Zend_Tool">Пишем свой провайдер для Zend_Tool</a></li><li><a href="http://anton.shevchuk.name/php/zfcore-cmf-based-o-zend-framework/" title="ZFCore &#8211; CMF на Zend Frameworkе">ZFCore &#8211; CMF на Zend Frameworkе</a></li><li><a href="http://anton.shevchuk.name/php/zend-framework-conference/" title="Первая в России конференция по Zend Framework">Первая в России конференция по Zend Framework</a></li><li><a href="http://anton.shevchuk.name/php/write-php-framework/" title="Пишем свой PHP Framework">Пишем свой PHP Framework</a></li><li><a href="http://anton.shevchuk.name/php/unit-tests-zend-framework-application/" title="Юнит тестирование приложений на Zend Framework&#8217;е">Юнит тестирование приложений на Zend Framework&#8217;е</a></li><li><a href="http://anton.shevchuk.name/php/zend-framework-hacks/" title="Zend Framework hacks">Zend Framework hacks</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/facing-refucktoring/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
	</channel>
</rss>

