Commits

Anonymous committed f334ce9

Update blog

Comments (0)

Files changed (87)

loutmega_blog.sql

+-- phpMyAdmin SQL Dump
+-- version 3.2.4
+-- http://www.phpmyadmin.net
+--
+-- Host: localhost
+-- Generation Time: Mar 25, 2010 at 12:11 PM
+-- Server version: 5.0.87
+-- PHP Version: 5.2.6
+
+SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
+
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+
+--
+-- Database: `loutmega_blog`
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `admin__config`
+--
+
+CREATE TABLE IF NOT EXISTS `admin__config` (
+  `name` varchar(63) collate utf8_unicode_ci NOT NULL default '',
+  `value` text collate utf8_unicode_ci,
+  PRIMARY KEY  (`name`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+--
+-- Dumping data for table `admin__config`
+--
+
+INSERT INTO `admin__config` (`name`, `value`) VALUES
+('site.description', 'Фриланс, Zend Framework, Doctrine'),
+('site.keywords', 'Фриланс, Zend Framework, Doctrine'),
+('site.name', 'Фриланс разработка'),
+('support.email', 'vladimir.webdev@gmail.com'),
+('support.name', 'Администрация сайта');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `blog__comment`
+--
+
+CREATE TABLE IF NOT EXISTS `blog__comment` (
+  `id` bigint(20) NOT NULL auto_increment,
+  `name` varchar(128) collate utf8_unicode_ci default NULL,
+  `email` varchar(128) collate utf8_unicode_ci default NULL,
+  `site` varchar(128) collate utf8_unicode_ci default NULL,
+  `text` text collate utf8_unicode_ci,
+  `texthtml` text collate utf8_unicode_ci,
+  `createdat` datetime NOT NULL,
+  `updatedat` datetime NOT NULL,
+  `user_id` bigint(20) default NULL,
+  `root_id` bigint(20) default NULL,
+  `lft` int(11) default NULL,
+  `rgt` int(11) default NULL,
+  `level` smallint(6) default NULL,
+  PRIMARY KEY  (`id`),
+  KEY `user_id_idx` (`user_id`),
+  KEY `root_idx` (`root_id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=35 ;
+
+--
+-- Dumping data for table `blog__comment`
+--
+
+INSERT INTO `blog__comment` (`id`, `name`, `email`, `site`, `text`, `texthtml`, `createdat`, `updatedat`, `user_id`, `root_id`, `lft`, `rgt`, `level`) VALUES
+(1, NULL, NULL, '', NULL, '\n', '2010-01-23 06:35:40', '2010-01-23 06:35:40', 1, 14, 1, 4, 0),
+(2, 'Smarty', '', '', 'Smarty для JS: http://ejohn.org/blog/javascript-micro-templating/', '<p>Smarty для JS: http://ejohn.org/blog/javascript-micro-templating/</p>\n', '2010-01-23 06:35:40', '2010-01-23 06:35:40', 1, 14, 2, 3, 1),
+(3, NULL, NULL, NULL, NULL, '\n', '2010-01-28 19:19:11', '2010-01-28 19:19:11', 0, 1, 1, 2, 0),
+(12, NULL, NULL, NULL, NULL, '\n', '2010-02-04 08:26:56', '2010-02-04 08:26:56', 1, 13, 1, 4, 0),
+(13, 'cl1ck', 'cl1ckme@gmail.com', '', 'А где можно посмотреть весь исходный код?', '<p>А где можно посмотреть весь исходный код?</p>\n', '2010-02-04 08:26:56', '2010-02-04 08:26:56', 1, 13, 2, 3, 1),
+(10, 'Владимир Михайленко', 'vladimir.webdev@gmail.com', '', 'Спасибо, не знал о таком методе. Правда создание специального гидратора все-таки имеет смысл, т.к. он работает быстрее, чем HYDRATE_RECORD + Doctrine_Collection::toKeyValueArray().', '<p>Спасибо, не знал о таком методе. Правда создание специального гидратора все-таки имеет смысл, т.к. он работает быстрее, чем HYDRATE_RECORD + Doctrine_Collection::toKeyValueArray().</p>\n', '2010-02-02 08:35:36', '2010-02-02 08:35:36', 1, 11, 3, 4, 2),
+(7, NULL, NULL, NULL, NULL, '\n', '2010-02-01 17:17:06', '2010-02-01 17:17:06', 0, 11, 1, 6, 0),
+(8, 'f!TR', 'facktoreal@gmail.com', 'http://facktoreal.com', 'Сначала думал тоже такой гидратор реализовывать, но потом обнаружил в Доктрине уже готовое решение: \r\nDoctrine_Collection::toKeyValueArray("key_field","value_field");', '<p>Сначала думал тоже такой гидратор реализовывать, но потом обнаружил в Доктрине уже готовое решение: \nDoctrine_Collection::toKeyValueArray(&#8220;key_field&#8221;,&#8221;value_field&#8221;);</p>\n', '2010-02-01 17:17:06', '2010-02-01 17:17:06', 0, 11, 2, 5, 1),
+(15, NULL, NULL, NULL, NULL, '\n', '2010-02-22 20:42:08', '2010-02-22 20:42:08', 0, 18, 1, 2, 0),
+(20, NULL, NULL, NULL, NULL, '\n', '2010-02-27 05:33:55', '2010-02-27 05:33:55', 0, 4, 1, 2, 0),
+(27, NULL, NULL, NULL, NULL, '\n', '2010-03-13 07:46:07', '2010-03-13 07:46:08', 0, 2, 1, 2, 0),
+(30, NULL, NULL, NULL, NULL, '\n', '2010-03-21 15:38:23', '2010-03-21 15:38:23', 0, 17, 1, 2, 0);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `blog__entry`
+--
+
+CREATE TABLE IF NOT EXISTS `blog__entry` (
+  `id` bigint(20) NOT NULL auto_increment,
+  `title` varchar(128) collate utf8_unicode_ci default NULL,
+  `tid` varchar(128) collate utf8_unicode_ci default NULL,
+  `content` text collate utf8_unicode_ci,
+  `contenthtml` text collate utf8_unicode_ci,
+  `comment_number` bigint(20) default '0',
+  `createdat` datetime NOT NULL,
+  `updatedat` datetime NOT NULL,
+  `user_id` bigint(20) default NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `tid` (`tid`),
+  KEY `user_id_idx` (`user_id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=21 ;
+
+--
+-- Dumping data for table `blog__entry`
+--
+
+INSERT INTO `blog__entry` (`id`, `title`, `tid`, `content`, `contenthtml`, `comment_number`, `createdat`, `updatedat`, `user_id`) VALUES
+(1, 'Service Locator на основе Zend_Registry', 'Service_Locator_na_osnove_Zend_Registry', '*Задача*. Прозрачно и с сохранением обратной совместимости расширить `Zend_Registry` для реализации паттерна Service Locator. Показать гибкость Zend Framework.\r\n\r\n<!-- cut -->\r\n\r\nДля начала рекомендую ознакомиться со статьей [Управление зависимостями в PHP-коде](http://wiki.agiledev.ru/doku.php?id=ooad:manage_dependencies_in_php_code), т.к. приведенная в этой статье реализация по-возможности основана на принципах Limb Toolkit, а следовательно наследует ее плюсы и минусы.\r\n\r\nПлюсы:\r\n\r\n*   Легко расширяется;\r\n*   Избавляет клиентов от знаний, кто именно реализует нужные им методы;\r\n*   Позволяет иметь в инструментах (tools) любые методы, в том числе фабричные и сервисные;\r\n*   Отлично обеспечивает изоляцию в тестах;\r\n*   Позволяет заменять инструменты в тестах только частично.\r\n\r\nМинусы:\r\n\r\n*   Неочевидно, в каком инструменте находится нужный метод, и от какого инструмента этот метод был вызван;\r\n*   Названия методов могут пересекаться.\r\n\r\nПервым делом добавляем в `Zend_Registry` методы для добавления инструментов (tools). В моей реализации из инструментов строится цепочка, т.е. работать мы будем с однонаправленным списком.\r\n\r\n    <?php\r\n    class ZendY_Registry_Toolkit extends Zend_Registry\r\n    {\r\n        protected $_tool = null;\r\n\r\n        public function setTool(ZendY_Registry_Tool $tool)\r\n        {\r\n            $this->_tool = $tool;\r\n            return $this;\r\n        }\r\n\r\n        public function getTool()\r\n        {\r\n            return $this->_tool;\r\n        }\r\n\r\n        public function addTool(ZendY_Registry_Tool $tool)\r\n        {\r\n            if ($this->_tool) {\r\n                $tool->setPrevious($this->_tool);\r\n            }\r\n            $this->setTool($tool);\r\n            return $this;\r\n        }\r\n\r\n        public function emptyTool()\r\n        {\r\n            $this->_tool = null;\r\n            return $this;\r\n        }\r\n\r\n        public function offsetExists($index)\r\n        {\r\n            if (parent::offsetExists($index)) {\r\n                return true;\r\n            } else {\r\n                try {\r\n                    $this->offsetSet($index, $this->_tool->get($index));\r\n                    return true;\r\n                } catch (ZendY_Registry_Tool_Exception $e) {\r\n                    return false;\r\n                }\r\n            }\r\n        }\r\n    }\r\n    ?>\r\n\r\nТеперь реализуем сам инструмент. Поиск необходимого сервиса происходит таким образом, что при отсутствии нужного сервиса в данном инструменте (срабатывание метода `__call()`) производится поиск в предыдущем инструменте (фактически, это вызов метода, накладные расходы очень малы).\r\n\r\n    <?php\r\n    abstract class ZendY_Registry_Tool\r\n    {\r\n        protected $_previous = null;\r\n\r\n        public function setPrevious(ZendY_Registry_Tool $previous)\r\n        {\r\n            if ($this->_previous) {\r\n                $this->_previous->setPrevious($previous);\r\n            } else {\r\n                $this->_previous = $previous;\r\n            }\r\n            return $this;\r\n        }\r\n\r\n        public function getPrevious()\r\n        {\r\n            return $this->_previous;\r\n        }\r\n\r\n        public function __construct(ZendY_Registry_Tool $previous = null)\r\n        {\r\n            if (null !== $previous) {\r\n                $this->setPrevious($previous);\r\n            }\r\n        }\r\n\r\n        public function __call($method, $args)\r\n        {\r\n            if (null !== $this->_previous) {\r\n                return $this->_previous->$method();\r\n            } else {\r\n                throw new ZendY_Registry_Tool_Exception(\r\n                    ''Undefined tool "'' . $method . ''"''\r\n                );\r\n            }\r\n        }\r\n\r\n        public function get($index)\r\n        {\r\n            $method = $index . ''Tool'';\r\n            return $this->$method();\r\n        }\r\n    }\r\n    ?>\r\n\r\n### Использование\r\n\r\nТеперь надо вместо `Zend_Registry` использовать `ZendY_Registry_Toolkit`:\r\n\r\n    <?php\r\n    // можно опустить, если Zend_Registry пуст\r\n    $oldRegistry = Zend_Registry::getInstance();\r\n    Zend_Registry::_unsetInstance();\r\n\r\n    // устанавливаем ZendY_Registry_Toolkit\r\n    Zend_Registry::setInstance(new ZendY_Registry_Toolkit());\r\n    $toolkit = Zend_Registry::getInstance();\r\n    $toolkit->addTool(new SystemTool());\r\n    ?>\r\n\r\nКласс `SystemTool`, содержащий необходимые нам сервисы, может выглядеть следующим образом:\r\n\r\n    <?php\r\n    class SystemTool extends ZendY_Registry_Tool\r\n    {\r\n        public function zendLogTool()\r\n        {\r\n            $logger = new Zend_Log();\r\n            $writer = new Zend_Log_Writer_Stream(DIR_LOGS);\r\n            $writer->setFormatter(new Zend_Log_Formatter_Xml());\r\n            $logger->addWriter($writer);\r\n            $logger->setEventItem(''requestUri'', $_SERVER[''REQUEST_URI'']);\r\n\r\n            return $logger;\r\n        }\r\n    }\r\n    ?>\r\n\r\nТеперь, если где-то в программе нам нужен `Zend_Log`, мы как и раньше работаем с `Zend_Registry` (ничего не изменилось, *клиентский код остался прежним*):\r\n\r\n    <?php\r\n    $logger = Zend_Registry::get(''zendLog'');\r\n    $logger->emerg(''System init failed'');\r\n    ?>\r\n\r\nСобственно, все. Для меня главная ценность этого метода в *ленивой загрузке*, т.е. `Zend_Log` в приведенном примере будет создан только в тот момент, когда он нам реально понадобится.\r\n\r\n### Исходный код\r\n\r\nИсходный код и примеры: [ZendY_Registry_Toolkit](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/).', '<p><em>Задача</em>. Прозрачно и с сохранением обратной совместимости расширить <code>Zend_Registry</code> для реализации паттерна Service Locator. Показать гибкость Zend Framework.</p>\n\n<!-- cut -->\n\n<p>Для начала рекомендую ознакомиться со статьей <a href="http://wiki.agiledev.ru/doku.php?id=ooad:manage_dependencies_in_php_code">Управление зависимостями в PHP-коде</a>, т.к. приведенная в этой статье реализация по-возможности основана на принципах Limb Toolkit, а следовательно наследует ее плюсы и минусы.</p>\n\n<p>Плюсы:</p>\n\n<ul>\n<li>Легко расширяется;</li>\n<li>Избавляет клиентов от знаний, кто именно реализует нужные им методы;</li>\n<li>Позволяет иметь в инструментах (tools) любые методы, в том числе фабричные и сервисные;</li>\n<li>Отлично обеспечивает изоляцию в тестах;</li>\n<li>Позволяет заменять инструменты в тестах только частично.</li>\n</ul>\n\n<p>Минусы:</p>\n\n<ul>\n<li>Неочевидно, в каком инструменте находится нужный метод, и от какого инструмента этот метод был вызван;</li>\n<li>Названия методов могут пересекаться.</li>\n</ul>\n\n<p>Первым делом добавляем в <code>Zend_Registry</code> методы для добавления инструментов (tools). В моей реализации из инструментов строится цепочка, т.е. работать мы будем с однонаправленным списком.</p>\n\n<pre><code>&lt;?php\nclass ZendY_Registry_Toolkit extends Zend_Registry\n{\n    protected $_tool = null;\n\n    public function setTool(ZendY_Registry_Tool $tool)\n    {\n        $this-&gt;_tool = $tool;\n        return $this;\n    }\n\n    public function getTool()\n    {\n        return $this-&gt;_tool;\n    }\n\n    public function addTool(ZendY_Registry_Tool $tool)\n    {\n        if ($this-&gt;_tool) {\n            $tool-&gt;setPrevious($this-&gt;_tool);\n        }\n        $this-&gt;setTool($tool);\n        return $this;\n    }\n\n    public function emptyTool()\n    {\n        $this-&gt;_tool = null;\n        return $this;\n    }\n\n    public function offsetExists($index)\n    {\n        if (parent::offsetExists($index)) {\n            return true;\n        } else {\n            try {\n                $this-&gt;offsetSet($index, $this-&gt;_tool-&gt;get($index));\n                return true;\n            } catch (ZendY_Registry_Tool_Exception $e) {\n                return false;\n            }\n        }\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Теперь реализуем сам инструмент. Поиск необходимого сервиса происходит таким образом, что при отсутствии нужного сервиса в данном инструменте (срабатывание метода <code>__call()</code>) производится поиск в предыдущем инструменте (фактически, это вызов метода, накладные расходы очень малы).</p>\n\n<pre><code>&lt;?php\nabstract class ZendY_Registry_Tool\n{\n    protected $_previous = null;\n\n    public function setPrevious(ZendY_Registry_Tool $previous)\n    {\n        if ($this-&gt;_previous) {\n            $this-&gt;_previous-&gt;setPrevious($previous);\n        } else {\n            $this-&gt;_previous = $previous;\n        }\n        return $this;\n    }\n\n    public function getPrevious()\n    {\n        return $this-&gt;_previous;\n    }\n\n    public function __construct(ZendY_Registry_Tool $previous = null)\n    {\n        if (null !== $previous) {\n            $this-&gt;setPrevious($previous);\n        }\n    }\n\n    public function __call($method, $args)\n    {\n        if (null !== $this-&gt;_previous) {\n            return $this-&gt;_previous-&gt;$method();\n        } else {\n            throw new ZendY_Registry_Tool_Exception(\n                ''Undefined tool "'' . $method . ''"''\n            );\n        }\n    }\n\n    public function get($index)\n    {\n        $method = $index . ''Tool'';\n        return $this-&gt;$method();\n    }\n}\n?&gt;\n</code></pre>\n\n<h3>Использование</h3>\n\n<p>Теперь надо вместо <code>Zend_Registry</code> использовать <code>ZendY_Registry_Toolkit</code>:</p>\n\n<pre><code>&lt;?php\n// можно опустить, если Zend_Registry пуст\n$oldRegistry = Zend_Registry::getInstance();\nZend_Registry::_unsetInstance();\n\n// устанавливаем ZendY_Registry_Toolkit\nZend_Registry::setInstance(new ZendY_Registry_Toolkit());\n$toolkit = Zend_Registry::getInstance();\n$toolkit-&gt;addTool(new SystemTool());\n?&gt;\n</code></pre>\n\n<p>Класс <code>SystemTool</code>, содержащий необходимые нам сервисы, может выглядеть следующим образом:</p>\n\n<pre><code>&lt;?php\nclass SystemTool extends ZendY_Registry_Tool\n{\n    public function zendLogTool()\n    {\n        $logger = new Zend_Log();\n        $writer = new Zend_Log_Writer_Stream(DIR_LOGS);\n        $writer-&gt;setFormatter(new Zend_Log_Formatter_Xml());\n        $logger-&gt;addWriter($writer);\n        $logger-&gt;setEventItem(''requestUri'', $_SERVER[''REQUEST_URI'']);\n\n        return $logger;\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Теперь, если где-то в программе нам нужен <code>Zend_Log</code>, мы как и раньше работаем с <code>Zend_Registry</code> (ничего не изменилось, <em>клиентский код остался прежним</em>):</p>\n\n<pre><code>&lt;?php\n$logger = Zend_Registry::get(''zendLog'');\n$logger-&gt;emerg(''System init failed'');\n?&gt;\n</code></pre>\n\n<p>Собственно, все. Для меня главная ценность этого метода в <em>ленивой загрузке</em>, т.е. <code>Zend_Log</code> в приведенном примере будет создан только в тот момент, когда он нам реально понадобится.</p>\n\n<h3>Исходный код</h3>\n\n<p>Исходный код и примеры: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/">ZendY_Registry_Toolkit</a>.</p>', 0, '2009-10-14 05:34:55', '2009-12-05 00:11:53', 1),
+(2, 'Параметризация запросов Doctrine_Query', 'Parametrizaciya_zaprosov_Doctrine_Query', '*Задача*. Минимальное изменение `Doctrine_Record` для поддержки параметризация запросов `Doctrine_Query`.\r\n\r\n<!-- cut -->\r\n\r\nБлагодаря тому, что `Doctrine` проверяет запрос `Doctrine_Query` только перед исполнением, у нас есть возможность создавать запрос, опуская неизвестные нам пока части:\r\n\r\n    <?php\r\n    $query = Doctrine_Query::create();\r\n    // в этот момент мы еще ничего не знаем о компоненте Post\r\n    $query->where(''Post.id = ?'', 1);\r\n    $query->from(''Post'');\r\n    $result = $query->execute();\r\n    ?>\r\n\r\nБлагодаря этой особенности Doctrine, в методе `fetchMyPosts()` мы расширяем логику запроса `fetchPosts()`, не имея к нему непосредственного доступа (он еще даже не создан).\r\n\r\n    <?php\r\n    class DoctrineX_Record extends Doctrine_Record\r\n    {\r\n        public function getBaseQuery(Doctrine_Query $query = null, $alias = null)\r\n        {\r\n            if (null === $query) {\r\n                $query = $this->getTable()->createQuery($alias);\r\n            }\r\n            return $query;\r\n        }\r\n    }\r\n\r\n    class Post extends DoctrineX_Record\r\n    {\r\n        public function fetchPosts(Doctrine_Query $query = null)\r\n        {\r\n            $posts = $this->getBaseQuery($query)\r\n                    ->from(''Post'')\r\n                    ->execute(array(), Doctrine_Core::HYDRATE_ARRAY);\r\n            foreach ($posts as &$post) {\r\n                // ...\r\n            }\r\n            return $posts;\r\n        }\r\n\r\n        public function fetchMyPosts(Doctrine_Query $query = null)\r\n        {\r\n            return $this->fetchPosts(\r\n                $this->getBaseQuery($query, ''Post'')->addWhere(\r\n                    ''Post.user_id = ?'',\r\n                    Zend_Registry::get(''user'')->id\r\n                )\r\n            );\r\n        }\r\n    }\r\n    ?>\r\n\r\nЕще пример:\r\n\r\n    <?php\r\n    class PostsController extends Zend_Controller_Action\r\n    {\r\n        public function indexAction()\r\n        {\r\n            $post = new Post();\r\n            $this->view->posts = $post->fetchMyPosts(\r\n                $post->getBaseQuery(null, ''Post'')->orderBy(''Post.createdAt DESC'')\r\n            );\r\n        }\r\n    }\r\n    ?>\r\n\r\nМожно пойти еще дальше и вместо результата выполнения запроса возвращать сам запрос (`Doctrine_Query`). Пример:\r\n\r\n    <?php\r\n    class NewPost extends DoctrineX_Record\r\n    {\r\n        public function fetchPosts(Doctrine_Query $query = null)\r\n        {\r\n            return $this->getBaseQuery($query)\r\n                    ->from(''Post'');\r\n        }\r\n\r\n        public function fetchMyPosts(Doctrine_Query $query = null)\r\n        {\r\n            $query = $this->fetchPosts();\r\n            $query->addWhere(\r\n                ''Post.user_id = ?'',\r\n                Zend_Registry::get(''user'')->id\r\n            );\r\n\r\n            return $query;\r\n        }\r\n    }\r\n    ?>\r\n\r\nКонтролер выглядит теперь так:\r\n\r\n    <?php\r\n    class PostsController extends Zend_Controller_Action\r\n    {\r\n        public function indexAction()\r\n        {\r\n            $post = new NewPost();\r\n            $this->view->posts = $post->fetchMyPosts()\r\n                    ->orderBy(''Post.createdAt DESC'')\r\n                    ->fetchArray();\r\n        }\r\n    }\r\n    ?>\r\n\r\nЭтот подход обладает двумя минусами:\r\n\r\n*   в контролере явно указывается, что используется Doctrine, т.е. переписать потом модель с помощью `Zend_Db_Table`, не изменяя контролер, не получится (проблема носит скорее теоритический характер и может быть решена ценой небольшой потери в производительности);\r\n*   т.к. результат запроса известен только в котролере, то никак обработать его в моделе нельзя. В большинстве случаев, эту проблему можно обойти, [перехватывая события](http://doctrine-project.org/documentation/manual/1_2/en/event-listeners) и [устанавливая ацесоры/мутаторы](http://doctrine-project.org/documentation/manual/1_1/en/introduction-to-models#custom-accessors/mutators).', '<p><em>Задача</em>. Минимальное изменение <code>Doctrine_Record</code> для поддержки параметризация запросов <code>Doctrine_Query</code>.</p>\n\n<!-- cut -->\n\n<p>Благодаря тому, что <code>Doctrine</code> проверяет запрос <code>Doctrine_Query</code> только перед исполнением, у нас есть возможность создавать запрос, опуская неизвестные нам пока части:</p>\n\n<pre><code>&lt;?php\n$query = Doctrine_Query::create();\n// в этот момент мы еще ничего не знаем о компоненте Post\n$query-&gt;where(''Post.id = ?'', 1);\n$query-&gt;from(''Post'');\n$result = $query-&gt;execute();\n?&gt;\n</code></pre>\n\n<p>Благодаря этой особенности Doctrine, в методе <code>fetchMyPosts()</code> мы расширяем логику запроса <code>fetchPosts()</code>, не имея к нему непосредственного доступа (он еще даже не создан).</p>\n\n<pre><code>&lt;?php\nclass DoctrineX_Record extends Doctrine_Record\n{\n    public function getBaseQuery(Doctrine_Query $query = null, $alias = null)\n    {\n        if (null === $query) {\n            $query = $this-&gt;getTable()-&gt;createQuery($alias);\n        }\n        return $query;\n    }\n}\n\nclass Post extends DoctrineX_Record\n{\n    public function fetchPosts(Doctrine_Query $query = null)\n    {\n        $posts = $this-&gt;getBaseQuery($query)\n                -&gt;from(''Post'')\n                -&gt;execute(array(), Doctrine_Core::HYDRATE_ARRAY);\n        foreach ($posts as &amp;$post) {\n            // ...\n        }\n        return $posts;\n    }\n\n    public function fetchMyPosts(Doctrine_Query $query = null)\n    {\n        return $this-&gt;fetchPosts(\n            $this-&gt;getBaseQuery($query, ''Post'')-&gt;addWhere(\n                ''Post.user_id = ?'',\n                Zend_Registry::get(''user'')-&gt;id\n            )\n        );\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Еще пример:</p>\n\n<pre><code>&lt;?php\nclass PostsController extends Zend_Controller_Action\n{\n    public function indexAction()\n    {\n        $post = new Post();\n        $this-&gt;view-&gt;posts = $post-&gt;fetchMyPosts(\n            $post-&gt;getBaseQuery(null, ''Post'')-&gt;orderBy(''Post.createdAt DESC'')\n        );\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Можно пойти еще дальше и вместо результата выполнения запроса возвращать сам запрос (<code>Doctrine_Query</code>). Пример:</p>\n\n<pre><code>&lt;?php\nclass NewPost extends DoctrineX_Record\n{\n    public function fetchPosts(Doctrine_Query $query = null)\n    {\n        return $this-&gt;getBaseQuery($query)\n                -&gt;from(''Post'');\n    }\n\n    public function fetchMyPosts(Doctrine_Query $query = null)\n    {\n        $query = $this-&gt;fetchPosts();\n        $query-&gt;addWhere(\n            ''Post.user_id = ?'',\n            Zend_Registry::get(''user'')-&gt;id\n        );\n\n        return $query;\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Контролер выглядит теперь так:</p>\n\n<pre><code>&lt;?php\nclass PostsController extends Zend_Controller_Action\n{\n    public function indexAction()\n    {\n        $post = new NewPost();\n        $this-&gt;view-&gt;posts = $post-&gt;fetchMyPosts()\n                -&gt;orderBy(''Post.createdAt DESC'')\n                -&gt;fetchArray();\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Этот подход обладает двумя минусами:</p>\n\n<ul>\n<li>в контролере явно указывается, что используется Doctrine, т.е. переписать потом модель с помощью <code>Zend_Db_Table</code>, не изменяя контролер, не получится (проблема носит скорее теоритический характер и может быть решена ценой небольшой потери в производительности);</li>\n<li>т.к. результат запроса известен только в котролере, то никак обработать его в моделе нельзя. В большинстве случаев, эту проблему можно обойти, <a href="http://doctrine-project.org/documentation/manual/1_2/en/event-listeners">перехватывая события</a> и <a href="http://doctrine-project.org/documentation/manual/1_1/en/introduction-to-models#custom-accessors/mutators">устанавливая ацесоры/мутаторы</a>.</li>\n</ul>', 0, '2009-10-14 12:33:19', '2009-12-17 05:26:28', 1),
+(3, 'Использование Highlight.js в Zend Framework', 'Ispol''zovanie_Highlight.js_v_Zend_Framework', '*Задача*. Интегрировать [Highlight.js](http://softwaremaniacs.org/soft/highlight/) в Zend Framework.\r\n\r\n<!-- cut -->\r\n\r\nИзучив несложный API Highlight.js, получился следующий хелпер - `ZendY_View_Helper_Highlight`:\r\n\r\n    <?php\r\n    class ZendY_View_Helper_Highlight extends Zend_View_Helper_Abstract\r\n    {\r\n        protected $_scriptPath = '''';\r\n        protected $_stylePath = '''';\r\n        protected $_tabReplace = '''';\r\n        protected $_languages = array();\r\n\r\n        public function setBasePath($path)\r\n        {\r\n            $this->_basePath = $path;\r\n            return $this;\r\n        }\r\n\r\n        public function getBasePath()\r\n        {\r\n            return $this->_basePath;\r\n        }\r\n\r\n        public function setScriptPath($path)\r\n        {\r\n            $this->_scriptPath = $this->getBasePath() . $path;\r\n            return $this;\r\n        }\r\n\r\n        public function getScriptPath()\r\n        {\r\n            return $this->_scriptPath;\r\n        }\r\n\r\n        public function setStylePath($path)\r\n        {\r\n            $this->_stylePath = $this->getBasePath() . $path;\r\n            return $this;\r\n        }\r\n\r\n        public function getStylePath()\r\n        {\r\n            return $this->_stylePath;\r\n        }\r\n\r\n        public function setTabReplace($tabReplace)\r\n        {\r\n            $this->_tabReplace = $tabReplace;\r\n            return $this;\r\n        }\r\n\r\n        public function getTabReplace()\r\n        {\r\n            return $this->_tabReplace;\r\n        }\r\n\r\n        public function setLanguages(array $languages)\r\n        {\r\n            $this->_languages = $languages;\r\n            return $this;\r\n        }\r\n\r\n        public function getLanguages()\r\n        {\r\n            return $this->_languages;\r\n        }\r\n\r\n        public function setOptions(array $options)\r\n        {\r\n            foreach ($options as $key => $value) {\r\n                $methodName = ''set'' . $key;\r\n                if (method_exists($this, $methodName)) {\r\n                    $this->$methodName($value);\r\n                } else {\r\n                    throw new Zend_View_Exception(''Unknown option "'' . $key . ''"'');\r\n                }\r\n            }\r\n            return $this;\r\n        }\r\n\r\n        public function highlight(array $options = array())\r\n        {\r\n            $this->setOptions($options);\r\n            return $this;\r\n        }\r\n\r\n        protected function _getScript()\r\n        {\r\n            $script = '''';\r\n\r\n            $tabReplace = $this->getTabReplace();\r\n            if ('''' !== $tabReplace) {\r\n                $tabReplace = str_replace(''"'', ''\\"'', $tabReplace);\r\n                $script .= ''hljs.tabReplace = "'' . $tabReplace . ''";'' . PHP_EOL;\r\n            }\r\n\r\n            $languages = $this->getLanguages();\r\n            if (array() !== $languages) {\r\n                $languages = ''"'' . implode(''", "'', $languages) . ''"'';\r\n            } else {\r\n                $languages = '''';\r\n            }\r\n            $script .= ''hljs.initHighlightingOnLoad('' . $languages . '');'' . PHP_EOL;\r\n\r\n            return $script;\r\n        }\r\n\r\n        public function inject()\r\n        {\r\n            $localPath = $this->getScriptPath();\r\n            if (!empty($localPath)) {\r\n                $this->view->headScript()\r\n                        ->appendFile($localPath)\r\n                        ->appendScript($this->_getScript());\r\n            } else {\r\n                throw new Zend_View_Exception(''Option "scriptPath" must be set'');\r\n            }\r\n\r\n            $stylePath = $this->getStylePath();\r\n            if (!empty($stylePath)) {\r\n                $this->view->headLink()\r\n                        ->appendStylesheet($stylePath);\r\n            }\r\n        }\r\n\r\n        public function __toString()\r\n        {\r\n            $this->inject();\r\n            return '''';\r\n        }\r\n    }\r\n    ?>\r\n\r\n## Использование\r\n\r\nВ скрипте пишем следующее:\r\n\r\n    <?php\r\n    $this->highlight()\r\n            ->setBasePath(''/library/highlight'')\r\n            ->setScriptPath(''/highlight.pack.js'')\r\n            ->setStylePath(''/styles/vs.css'')\r\n            ->setTabReplace(''    '')\r\n            ->setLanguages(array(''php'', ''js''))\r\n            ->inject();\r\n    ?>\r\n\r\nВ шаблоне у вас уже должно быть что-то похожее на следующий код:\r\n\r\n    <?= $this->headLink(); ?>\r\n    <?= $this->headStyle(); ?>\r\n    <?= $this->headScript(); ?>\r\n    <?= $this->inlineScript(); ?>\r\n\r\nВ результате вывод будет примерно таким:\r\n\r\n    <link href="/library/highlight/styles/vs.css" media="screen" rel="stylesheet" type="text/css" />\r\n    <script type="text/javascript" src="/library/highlight/highlight.pack.js"></script>\r\n    <script type="text/javascript">\r\n    //<![CDATA[\r\n    hljs.tabReplace = "    ";\r\n    hljs.initHighlightingOnLoad("php", "js");\r\n    //]]>\r\n    </script>\r\n\r\n### Исходный код\r\n\r\nИсходный код: [ZendY_View_Helper_Highlight](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/ZendY/View/Helper/Highlight.php).', '<p><em>Задача</em>. Интегрировать <a href="http://softwaremaniacs.org/soft/highlight/">Highlight.js</a> в Zend Framework.</p>\n\n<!-- cut -->\n\n<p>Изучив несложный API Highlight.js, получился следующий хелпер - <code>ZendY_View_Helper_Highlight</code>:</p>\n\n<pre><code>&lt;?php\nclass ZendY_View_Helper_Highlight extends Zend_View_Helper_Abstract\n{\n    protected $_scriptPath = '''';\n    protected $_stylePath = '''';\n    protected $_tabReplace = '''';\n    protected $_languages = array();\n\n    public function setBasePath($path)\n    {\n        $this-&gt;_basePath = $path;\n        return $this;\n    }\n\n    public function getBasePath()\n    {\n        return $this-&gt;_basePath;\n    }\n\n    public function setScriptPath($path)\n    {\n        $this-&gt;_scriptPath = $this-&gt;getBasePath() . $path;\n        return $this;\n    }\n\n    public function getScriptPath()\n    {\n        return $this-&gt;_scriptPath;\n    }\n\n    public function setStylePath($path)\n    {\n        $this-&gt;_stylePath = $this-&gt;getBasePath() . $path;\n        return $this;\n    }\n\n    public function getStylePath()\n    {\n        return $this-&gt;_stylePath;\n    }\n\n    public function setTabReplace($tabReplace)\n    {\n        $this-&gt;_tabReplace = $tabReplace;\n        return $this;\n    }\n\n    public function getTabReplace()\n    {\n        return $this-&gt;_tabReplace;\n    }\n\n    public function setLanguages(array $languages)\n    {\n        $this-&gt;_languages = $languages;\n        return $this;\n    }\n\n    public function getLanguages()\n    {\n        return $this-&gt;_languages;\n    }\n\n    public function setOptions(array $options)\n    {\n        foreach ($options as $key =&gt; $value) {\n            $methodName = ''set'' . $key;\n            if (method_exists($this, $methodName)) {\n                $this-&gt;$methodName($value);\n            } else {\n                throw new Zend_View_Exception(''Unknown option "'' . $key . ''"'');\n            }\n        }\n        return $this;\n    }\n\n    public function highlight(array $options = array())\n    {\n        $this-&gt;setOptions($options);\n        return $this;\n    }\n\n    protected function _getScript()\n    {\n        $script = '''';\n\n        $tabReplace = $this-&gt;getTabReplace();\n        if ('''' !== $tabReplace) {\n            $tabReplace = str_replace(''"'', ''\\"'', $tabReplace);\n            $script .= ''hljs.tabReplace = "'' . $tabReplace . ''";'' . PHP_EOL;\n        }\n\n        $languages = $this-&gt;getLanguages();\n        if (array() !== $languages) {\n            $languages = ''"'' . implode(''", "'', $languages) . ''"'';\n        } else {\n            $languages = '''';\n        }\n        $script .= ''hljs.initHighlightingOnLoad('' . $languages . '');'' . PHP_EOL;\n\n        return $script;\n    }\n\n    public function inject()\n    {\n        $localPath = $this-&gt;getScriptPath();\n        if (!empty($localPath)) {\n            $this-&gt;view-&gt;headScript()\n                    -&gt;appendFile($localPath)\n                    -&gt;appendScript($this-&gt;_getScript());\n        } else {\n            throw new Zend_View_Exception(''Option "scriptPath" must be set'');\n        }\n\n        $stylePath = $this-&gt;getStylePath();\n        if (!empty($stylePath)) {\n            $this-&gt;view-&gt;headLink()\n                    -&gt;appendStylesheet($stylePath);\n        }\n    }\n\n    public function __toString()\n    {\n        $this-&gt;inject();\n        return '''';\n    }\n}\n?&gt;\n</code></pre>\n\n<h2>Использование</h2>\n\n<p>В скрипте пишем следующее:</p>\n\n<pre><code>&lt;?php\n$this-&gt;highlight()\n        -&gt;setBasePath(''/library/highlight'')\n        -&gt;setScriptPath(''/highlight.pack.js'')\n        -&gt;setStylePath(''/styles/vs.css'')\n        -&gt;setTabReplace(''    '')\n        -&gt;setLanguages(array(''php'', ''js''))\n        -&gt;inject();\n?&gt;\n</code></pre>\n\n<p>В шаблоне у вас уже должно быть что-то похожее на следующий код:</p>\n\n<pre><code>&lt;?= $this-&gt;headLink(); ?&gt;\n&lt;?= $this-&gt;headStyle(); ?&gt;\n&lt;?= $this-&gt;headScript(); ?&gt;\n&lt;?= $this-&gt;inlineScript(); ?&gt;\n</code></pre>\n\n<p>В результате вывод будет примерно таким:</p>\n\n<pre><code>&lt;link href="/library/highlight/styles/vs.css" media="screen" rel="stylesheet" type="text/css" /&gt;\n&lt;script type="text/javascript" src="/library/highlight/highlight.pack.js"&gt;&lt;/script&gt;\n&lt;script type="text/javascript"&gt;\n//&lt;![CDATA[\nhljs.tabReplace = "    ";\nhljs.initHighlightingOnLoad("php", "js");\n//]]&gt;\n&lt;/script&gt;\n</code></pre>\n\n<h3>Исходный код</h3>\n\n<p>Исходный код: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/ZendY/View/Helper/Highlight.php">ZendY_View_Helper_Highlight</a>.</p>', 0, '2009-10-14 12:37:21', '2009-12-05 00:16:00', 1),
+(4, 'Плагин к Doctrine для работы с Zend_Search_Lucene', 'Plagin_k_Doctrine_dlya_raboty_s_Zend_Search_Lucene', '*Задача*. Автоматизировать добавление, обновление и удаление документа в индексе Lucene при использовании Doctrine.\r\n\r\n<!-- cut -->\r\n\r\nDoctrine содержит удобные средства для написания плагинов, известные как шаблоны, а также довольно гибкую событийную архитектуру, позволяющую перехватывать события создания, обновления и удаления записей (нам нужны только эти, но на самом деле их [намного больше](http://doctrine-project.org/documentation/manual/1_1/en/event-listeners)).\r\n\r\nСам плагин состоит из двух частей:\r\n\r\n*   `DoctrineX_Template_Lucene`. Основные операции по работе с индексом: создание индекса, документа, поиск документа в индексе;\r\n*   `DoctrineX_Template_Listener_Lucene`. Перехват событий `postInsert`, `postUpdate`, `postDelete` и управление индексом в них.\r\n\r\n### Использование\r\n\r\nК примеру, у нас есть модель статей `Article` и мы хотим добавить в нее возможность поиска. Нижеприведенный код будет управлять в индексе Lucene-документом, содержащем поля `id` (т.к. это первичный ключ и однозначно идентифицирует документ), `name` и `details` (заданы пользователем при конфигурации, при этом для поля name также задан boost-фактор). Также мы задаем используемую кодировку (UTF-8), соответствующий анализатор и массив стоп-слов.\r\n\r\n    <?php\r\n    class Article extends Doctrine_Record\r\n    {\r\n        public function setTableDefinition()\r\n        {\r\n            $this->hasColumn(''id'', ''integer'', null, array(''primary'' => true,\r\n                    ''autoincrement'' => true));\r\n            $this->hasColumn(''name'', ''string'', 128);\r\n            $this->hasColumn(''details'', ''string'');\r\n\r\n            $this->actAs(new DoctrineX_Template_Lucene(array(\r\n                ''indexesPath'' => ''application/data/indexes'',\r\n                ''fields'' => array(\r\n                    array(''name'' => ''name'', ''type'' => ''text'', ''boost'' => 5.0),\r\n                    array(''name'' => ''details'', ''type'' => ''unStored''),\r\n                ),\r\n                ''encoding'' => ''UTF-8'',\r\n                ''analyzer'' => new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive(),\r\n                ''stopWords'' => array(''и'', ''в'', ''по''),\r\n            )));\r\n        }\r\n    }\r\n    ?>\r\n\r\nПравила использования класса `Article` никак не изменинись. Поиск по индексу может выглядеть следующим образом:\r\n\r\n    <?php\r\n    class Article extends Doctrine_Record\r\n    {\r\n        // ...\r\n\r\n        public function search($q, Doctrine_Query $query = null)\r\n        {\r\n            $hits = $this->getLuceneIndex()->find($q);\r\n            return new Zend_Paginator(new Zend_Paginator_Adapter_Array($hits));\r\n        }\r\n    }\r\n    ?>\r\n\r\nТакже плагин определяет собственные события `preLucene` и `postLucene`, вызываемые соостветственно перед и после любой операции с индексом, происходящей в плагине.\r\n\r\n    <?php\r\n    class Article extends Doctrine_Record\r\n    {\r\n        // ...\r\n\r\n        public function preLucene(Doctrine_Event $event)\r\n        {\r\n            if ($this->isSpam()) {\r\n                $event->skipOperation(); // документ не будет добавлен в индекс\r\n            }\r\n        }\r\n\r\n        public function postLucene(Doctrine_Event $event)\r\n        {\r\n        }\r\n    }\r\n    ?>\r\n\r\n### Исходный код\r\n\r\nИсходный код и примеры: [DoctrineX_Template_Lucene](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/DoctrineX/Template/).', '<p><em>Задача</em>. Автоматизировать добавление, обновление и удаление документа в индексе Lucene при использовании Doctrine.</p>\n\n<!-- cut -->\n\n<p>Doctrine содержит удобные средства для написания плагинов, известные как шаблоны, а также довольно гибкую событийную архитектуру, позволяющую перехватывать события создания, обновления и удаления записей (нам нужны только эти, но на самом деле их <a href="http://doctrine-project.org/documentation/manual/1_1/en/event-listeners">намного больше</a>).</p>\n\n<p>Сам плагин состоит из двух частей:</p>\n\n<ul>\n<li><code>DoctrineX_Template_Lucene</code>. Основные операции по работе с индексом: создание индекса, документа, поиск документа в индексе;</li>\n<li><code>DoctrineX_Template_Listener_Lucene</code>. Перехват событий <code>postInsert</code>, <code>postUpdate</code>, <code>postDelete</code> и управление индексом в них.</li>\n</ul>\n\n<h3>Использование</h3>\n\n<p>К примеру, у нас есть модель статей <code>Article</code> и мы хотим добавить в нее возможность поиска. Нижеприведенный код будет управлять в индексе Lucene-документом, содержащем поля <code>id</code> (т.к. это первичный ключ и однозначно идентифицирует документ), <code>name</code> и <code>details</code> (заданы пользователем при конфигурации, при этом для поля name также задан boost-фактор). Также мы задаем используемую кодировку (UTF-8), соответствующий анализатор и массив стоп-слов.</p>\n\n<pre><code>&lt;?php\nclass Article extends Doctrine_Record\n{\n    public function setTableDefinition()\n    {\n        $this-&gt;hasColumn(''id'', ''integer'', null, array(''primary'' =&gt; true,\n                ''autoincrement'' =&gt; true));\n        $this-&gt;hasColumn(''name'', ''string'', 128);\n        $this-&gt;hasColumn(''details'', ''string'');\n\n        $this-&gt;actAs(new DoctrineX_Template_Lucene(array(\n            ''indexesPath'' =&gt; ''application/data/indexes'',\n            ''fields'' =&gt; array(\n                array(''name'' =&gt; ''name'', ''type'' =&gt; ''text'', ''boost'' =&gt; 5.0),\n                array(''name'' =&gt; ''details'', ''type'' =&gt; ''unStored''),\n            ),\n            ''encoding'' =&gt; ''UTF-8'',\n            ''analyzer'' =&gt; new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive(),\n            ''stopWords'' =&gt; array(''и'', ''в'', ''по''),\n        )));\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Правила использования класса <code>Article</code> никак не изменинись. Поиск по индексу может выглядеть следующим образом:</p>\n\n<pre><code>&lt;?php\nclass Article extends Doctrine_Record\n{\n    // ...\n\n    public function search($q, Doctrine_Query $query = null)\n    {\n        $hits = $this-&gt;getLuceneIndex()-&gt;find($q);\n        return new Zend_Paginator(new Zend_Paginator_Adapter_Array($hits));\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Также плагин определяет собственные события <code>preLucene</code> и <code>postLucene</code>, вызываемые соостветственно перед и после любой операции с индексом, происходящей в плагине.</p>\n\n<pre><code>&lt;?php\nclass Article extends Doctrine_Record\n{\n    // ...\n\n    public function preLucene(Doctrine_Event $event)\n    {\n        if ($this-&gt;isSpam()) {\n            $event-&gt;skipOperation(); // документ не будет добавлен в индекс\n        }\n    }\n\n    public function postLucene(Doctrine_Event $event)\n    {\n    }\n}\n?&gt;\n</code></pre>\n\n<h3>Исходный код</h3>\n\n<p>Исходный код и примеры: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/DoctrineX/Template/">DoctrineX_Template_Lucene</a>.</p>', 0, '2009-10-21 06:34:14', '2009-12-05 00:19:44', 1),
+(5, 'Отложенное копирование в PHP', 'Otlozhennoe_kopirovanie_v_PHP', 'Рассмотрим следующий код, который создает в памяти строку в 1024 символов, копирует эту строку в другую переменную, а затем удаляет оригинальную строку:\r\n\r\n    <?php\r\n    $start = memory_get_usage();\r\n\r\n    $string = str_repeat('' '', 1024);\r\n\r\n    $stop = memory_get_usage();\r\n    echo ''1 string: '', ($stop - $start), PHP_EOL;\r\n\r\n    $string2 = $string;\r\n\r\n    $stop = memory_get_usage();\r\n    echo ''2 strings: '', ($stop - $start), PHP_EOL;\r\n\r\n    unset($string);\r\n\r\n    $stop = memory_get_usage();\r\n    echo ''1 string deleted: '', ($stop - $start), PHP_EOL;\r\n    ?>\r\n\r\nЕсли его запустить, то вывод будет таким:\r\n\r\n    1 string: 1184\r\n    2 strings: 1448\r\n    1 string deleted: 1448\r\n\r\nКак видно, две строки занимают вовсе не в два раза больше памяти, а удаление одной строки на расход памяти никак не влияет.\r\n\r\n"Виновато" во всем, как можно догадаться из заголовка, отложенное копирование. PHP при копировании переменной сначала создает ссылку на исходную переменную и только при изменении переменной создаст копию. Память, как видите, экономится.\r\n\r\nВот только освобождать память из-за этого очень непросто. Так как на созданную строку имеется 2 ссылки (`$string` и `$string2`), то мы должны удалить обе ссылки, чтобы освободить память.\r\n\r\nТо есть, при присваивании, передаче переменной в конструктор, функцию и т.д., создается ссылка. И память под переменную не будет освобождена, пока все ссылки не будут уничтожены. В общем, на практике это все очень весело, но к счастью необходимость в принудительном освобождении памяти при работе скрипта возникает не так уж и часто.', '<p>Рассмотрим следующий код, который создает в памяти строку в 1024 символов, копирует эту строку в другую переменную, а затем удаляет оригинальную строку:</p>\n\n<pre><code>&lt;?php\n$start = memory_get_usage();\n\n$string = str_repeat('' '', 1024);\n\n$stop = memory_get_usage();\necho ''1 string: '', ($stop - $start), PHP_EOL;\n\n$string2 = $string;\n\n$stop = memory_get_usage();\necho ''2 strings: '', ($stop - $start), PHP_EOL;\n\nunset($string);\n\n$stop = memory_get_usage();\necho ''1 string deleted: '', ($stop - $start), PHP_EOL;\n?&gt;\n</code></pre>\n\n<p>Если его запустить, то вывод будет таким:</p>\n\n<pre><code>1 string: 1184\n2 strings: 1448\n1 string deleted: 1448\n</code></pre>\n\n<p>Как видно, две строки занимают вовсе не в два раза больше памяти, а удаление одной строки на расход памяти никак не влияет.</p>\n\n<p>&#8220;Виновато&#8221; во всем, как можно догадаться из заголовка, отложенное копирование. PHP при копировании переменной сначала создает ссылку на исходную переменную и только при изменении переменной создаст копию. Память, как видите, экономится.</p>\n\n<p>Вот только освобождать память из-за этого очень непросто. Так как на созданную строку имеется 2 ссылки (<code>$string</code> и <code>$string2</code>), то мы должны удалить обе ссылки, чтобы освободить память.</p>\n\n<p>То есть, при присваивании, передаче переменной в конструктор, функцию и т.д., создается ссылка. И память под переменную не будет освобождена, пока все ссылки не будут уничтожены. В общем, на практике это все очень весело, но к счастью необходимость в принудительном освобождении памяти при работе скрипта возникает не так уж и часто.</p>', 0, '2009-11-11 12:11:34', '2009-11-11 12:16:18', 1);
+INSERT INTO `blog__entry` (`id`, `title`, `tid`, `content`, `contenthtml`, `comment_number`, `createdat`, `updatedat`, `user_id`) VALUES
+(6, 'Регулярные выражения на больших строках', 'Regulyarnye_vyrazheniya_na_bol''shih_strokah', 'Если регулярное выражение вдруг перестает работать на некоторых строках, то дело может быть вовсе не в ужасной поддержке PHP уникода, не в "неправильных" переводах строк ("\\n\\r", "\\n", "\\r") и даже не в регулярном выражении. Возможно, стоит проверить конфигурацию PCRE и выставить значения побольше:\r\n\r\n    [Pcre]\r\n    ;PCRE library backtracking limit.\r\n    pcre.backtrack_limit=100000\r\n\r\n    ;PCRE library recursion limit. \r\n    ;Please note that if you set this value to a high number you may consume all \r\n    ;the available process stack and eventually crash PHP (due to reaching the \r\n    ;stack size limit imposed by the Operating System).\r\n    pcre.recursion_limit=100000', '<p>Если регулярное выражение вдруг перестает работать на некоторых строках, то дело может быть вовсе не в ужасной поддержке PHP уникода, не в &#8220;неправильных&#8221; переводах строк (&#8220;\\n\\r&#8221;, &#8220;\\n&#8221;, &#8220;\\r&#8221;) и даже не в регулярном выражении. Возможно, стоит проверить конфигурацию PCRE и выставить значения побольше:</p>\n\n<pre><code>[Pcre]\n;PCRE library backtracking limit.\npcre.backtrack_limit=100000\n\n;PCRE library recursion limit. \n;Please note that if you set this value to a high number you may consume all \n;the available process stack and eventually crash PHP (due to reaching the \n;stack size limit imposed by the Operating System).\npcre.recursion_limit=100000\n</code></pre>', 0, '2009-11-12 05:18:48', '2009-12-03 04:04:34', 1),
+(7, 'Многоязычный роутер для Zend Framework', 'Mnogoyazychnyj_router_dlya_Zend_Framework', '*Задача*. Создание специального роутера для Zend Framework, который бы поддерживал многоязычность на высоком уровне.\r\n\r\n<!-- cut -->\r\n\r\nРоутер поддерживает следующие URI:\r\n\r\n*   articles/index - статьи на языке по умолчанию (задается пользователем);\r\n*   ru/articles/index - cтатьи на русском языке (префикс `ru`);\r\n*   english/articles/index - статьи на английском языке (префикс `english`).\r\n\r\nДанный роутер основан на роутере [CyEngine_Controller_Router_Route_Multilingual](http://zendframework.ru/forum/index.php?topic=696.msg9391#msg9391).\r\n\r\n## Использование\r\n\r\nДля работы роутера необходимо задать язык по умолчанию (`defaultLang`, в данном случае "en") и текущий язык (`lang`). Т.к. эти данные могут быть полезны и в других местах, то имеет смысл расшарить их через `Zend_Registry`:\r\n\r\n    <?php\r\n    Zend_Registry::set(''defaultLang'', ''en'');\r\n    Zend_Registry::set(''lang'', Zend_Controller_Front::getInstance()\r\n            ->getRequest()->getParam(''lang''));\r\n    ?>\r\n\r\nТакже непосредственно в роутере необходимо установить список поддерживаемых языков (в данном случае роутером будут распознаны префиксы `en` и `ru`):\r\n\r\n    <?php\r\n    ZendY_Controller_Router_Route_Multilingual::setLanguagePrefixes(array(\r\n        ''en'', ''ru''\r\n    ));\r\n    ?>\r\n\r\nТеперь роутером можно пользоваться так же, как и стандартным `Zend_Controller_Router_Route`:\r\n\r\n    <?php\r\n    Zend_Controller_Front::getInstance()->getRouter()\r\n            ->addRoutes(array(\r\n                ''gallery.index'' => array(\r\n                    ''type'' => ''ZendY_Controller_Router_Route_Multilingual'',\r\n                    ''route'' => ''gallery'',\r\n                    ''defaults'' => array(\r\n                        ''module'' => ''obras'',\r\n                        ''controller'' => ''gallery'',\r\n                        ''action'' => ''index'',\r\n                    ),\r\n                ),\r\n            ));\r\n    ?>\r\n\r\nУзнать текущий язык в контроллере можно как обычно:\r\n\r\n    <?php\r\n    class IndexController extends Zend_Controller_Action\r\n    {\r\n        public function indexAction()\r\n        {\r\n            $lang = $this->_getParam(''lang'');\r\n        }\r\n    }\r\n    ?>\r\n\r\nВ других местах язык может быть получен через `Zend_Registry`:\r\n\r\n    <?php\r\n    $lang = Zend_Registry::get(''lang'');\r\n    ?>\r\n\r\n## Исходный код\r\n\r\nИсходный код роутера: [ZendY_Controller_Router_Route_Multilingual](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/ZendY/Controller/Router/Route/Multilingual.php).', '<p><em>Задача</em>. Создание специального роутера для Zend Framework, который бы поддерживал многоязычность на высоком уровне.</p>\n\n<!-- cut -->\n\n<p>Роутер поддерживает следующие URI:</p>\n\n<ul>\n<li>articles/index - статьи на языке по умолчанию (задается пользователем);</li>\n<li>ru/articles/index - cтатьи на русском языке (префикс <code>ru</code>);</li>\n<li>english/articles/index - статьи на английском языке (префикс <code>english</code>).</li>\n</ul>\n\n<p>Данный роутер основан на роутере <a href="http://zendframework.ru/forum/index.php?topic=696.msg9391#msg9391">CyEngine_Controller_Router_Route_Multilingual</a>.</p>\n\n<h2>Использование</h2>\n\n<p>Для работы роутера необходимо задать язык по умолчанию (<code>defaultLang</code>, в данном случае &#8220;en&#8221;) и текущий язык (<code>lang</code>). Т.к. эти данные могут быть полезны и в других местах, то имеет смысл расшарить их через <code>Zend_Registry</code>:</p>\n\n<pre><code>&lt;?php\nZend_Registry::set(''defaultLang'', ''en'');\nZend_Registry::set(''lang'', Zend_Controller_Front::getInstance()\n        -&gt;getRequest()-&gt;getParam(''lang''));\n?&gt;\n</code></pre>\n\n<p>Также непосредственно в роутере необходимо установить список поддерживаемых языков (в данном случае роутером будут распознаны префиксы <code>en</code> и <code>ru</code>):</p>\n\n<pre><code>&lt;?php\nZendY_Controller_Router_Route_Multilingual::setLanguagePrefixes(array(\n    ''en'', ''ru''\n));\n?&gt;\n</code></pre>\n\n<p>Теперь роутером можно пользоваться так же, как и стандартным <code>Zend_Controller_Router_Route</code>:</p>\n\n<pre><code>&lt;?php\nZend_Controller_Front::getInstance()-&gt;getRouter()\n        -&gt;addRoutes(array(\n            ''gallery.index'' =&gt; array(\n                ''type'' =&gt; ''ZendY_Controller_Router_Route_Multilingual'',\n                ''route'' =&gt; ''gallery'',\n                ''defaults'' =&gt; array(\n                    ''module'' =&gt; ''obras'',\n                    ''controller'' =&gt; ''gallery'',\n                    ''action'' =&gt; ''index'',\n                ),\n            ),\n        ));\n?&gt;\n</code></pre>\n\n<p>Узнать текущий язык в контроллере можно как обычно:</p>\n\n<pre><code>&lt;?php\nclass IndexController extends Zend_Controller_Action\n{\n    public function indexAction()\n    {\n        $lang = $this-&gt;_getParam(''lang'');\n    }\n}\n?&gt;\n</code></pre>\n\n<p>В других местах язык может быть получен через <code>Zend_Registry</code>:</p>\n\n<pre><code>&lt;?php\n$lang = Zend_Registry::get(''lang'');\n?&gt;\n</code></pre>\n\n<h2>Исходный код</h2>\n\n<p>Исходный код роутера: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/ZendY/Controller/Router/Route/Multilingual.php">ZendY_Controller_Router_Route_Multilingual</a>.</p>\n', 0, '2009-11-23 08:15:32', '2010-01-22 04:19:29', 1),
+(8, 'Zend_Paginator адаптер для Doctrine_Query', 'Zend_Paginator_adapter_dlya_Doctrine_Query', '*Задача*. Создание адаптера для `Zend_Paginator`, который работает с экземпляром `Doctrine_Query` и следует рекомендациям [Zend_Paginator_Adapter_Doctrine - Jason Eisenmenger](http://framework.zend.com/wiki/display/ZFPROP/Zend_Paginator_Adapter_Doctrine+-+Jason+Eisenmenger).\r\n\r\n<!-- cut -->\r\n\r\n### Использование\r\n\r\n`ZendY_Doctrine_Paginator_Adapter` используется как обычно вместе с `Zend_Paginator`, отмечу лишь, что `$hydrationMode` задается в запросе:\r\n\r\n    <?php\r\n    $query = Doctrine_Query::create()\r\n            ->from(''Article a'')\r\n            ->setHydrationMode(Doctrine::HYDRATE_ARRAY);\r\n\r\n    $paginator = new Zend_Paginator(\r\n            new ZendY_Doctrine_Paginator_Adapter($query));\r\n\r\n    $paginator\r\n            ->setItemCountPerPage(15)\r\n            ->setPageRange(5)\r\n            ->setCurrentPageNumber($this->_getParam(''page''));\r\n\r\n    $this->view->pages = $paginator;\r\n    ?>\r\n\r\n### Исходный код\r\n\r\nИсходный код: [ZendY_Doctrine_Paginator_Adapter](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/ZendY/Doctrine/Paginator/Adapter.php).', '<p><em>Задача</em>. Создание адаптера для <code>Zend_Paginator</code>, который работает с экземпляром <code>Doctrine_Query</code> и следует рекомендациям <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Paginator_Adapter_Doctrine+-+Jason+Eisenmenger">Zend_Paginator_Adapter_Doctrine - Jason Eisenmenger</a>.</p>\n\n<!-- cut -->\n\n<h3>Использование</h3>\n\n<p><code>ZendY_Doctrine_Paginator_Adapter</code> используется как обычно вместе с <code>Zend_Paginator</code>, отмечу лишь, что <code>$hydrationMode</code> задается в запросе:</p>\n\n<pre><code>&lt;?php\n$query = Doctrine_Query::create()\n        -&gt;from(''Article a'')\n        -&gt;setHydrationMode(Doctrine::HYDRATE_ARRAY);\n\n$paginator = new Zend_Paginator(\n        new ZendY_Doctrine_Paginator_Adapter($query));\n\n$paginator\n        -&gt;setItemCountPerPage(15)\n        -&gt;setPageRange(5)\n        -&gt;setCurrentPageNumber($this-&gt;_getParam(''page''));\n\n$this-&gt;view-&gt;pages = $paginator;\n?&gt;\n</code></pre>\n\n<h3>Исходный код</h3>\n\n<p>Исходный код: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/ZendY/Doctrine/Paginator/Adapter.php">ZendY_Doctrine_Paginator_Adapter</a>.</p>', 0, '2009-12-05 00:09:20', '2009-12-05 00:09:20', 1),
+(9, 'Задание для Phing по оптимизации CSS', 'Zadanie_dlya_Phing_po_optimizacii_CSS', '*Задача*. Заменить все импорты файлов в CSS на содержимое этих файлов, тем самым ускорив загрузку сайта и снизив нагрузку на сервер.\r\n\r\n<!-- cut -->\r\n\r\nК примеру, имеем следующий CSS-файл `screen.css`:\r\n\r\n    @IMPORT url(''/application/css/src/reset.css'');\r\n    @IMPORT url(''/application/css/src/global.css'');\r\n\r\n    @IMPORT url(''/application/css/src/layouts/l-content.css'');\r\n    @IMPORT url(''/application/css/src/layouts/l-footer.css'');\r\n    @IMPORT url(''/application/css/src/layouts/l-header.css'');\r\n\r\n    @IMPORT url(''/application/css/src/blocks/b-menu.css'');\r\n    @IMPORT url(''/application/css/src/blocks/b-copyright.css'');\r\n\r\nТакой файл прекрасно подходит для разработки, но на рабочем сервере это выливается в излишне долгую загрузку сайта и нагрузку на сервер. Поэтому было бы неплохо заменить импорты на содержимое этих файлов.\r\n\r\nДля этой задачи прекрасно подходит [Phing](http://phing.info/), поэтому для него было написано задание `ImportCssTask`, справляющееся с данной задачей.\r\n\r\n### Использование\r\n\r\nВ билд файле `build.xml` необходимо объявить новое задание, используя `taskdef`, а затем вызвать задание, передав путь к `DOCUMENT_ROOT` проекта через `basedir`:\r\n\r\n    <?xml version="1.0" encoding="UTF-8"?>\r\n\r\n    <project default="build" name="blog" description="Simple blog">\r\n\r\n        <property name="sourcedir" value="${project.basedir}/www" override="true" />\r\n        <property name="targetdir" value="${project.basedir}/../${phing.project.name}d/www" override="true" />\r\n\r\n        <taskdef name="importcss" classname="phing.tasks.ext.ImportCssTask" />\r\n\r\n        <!-- ... -->\r\n\r\n        <target name="optimizeCss">\r\n            <importcss basedir="${targetdir}">\r\n                <fileset dir="${targetdir}/application/css">\r\n                    <include name="**.css" />\r\n                </fileset>\r\n            </importcss>\r\n        </target>\r\n\r\n        <!-- ... -->\r\n\r\n    </project>\r\n\r\n### Исходный код\r\n\r\nИсходный код: [ImportCssTask](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/phing/tasks/ext/ImportCssTask.php).', '<p><em>Задача</em>. Заменить все импорты файлов в CSS на содержимое этих файлов, тем самым ускорив загрузку сайта и снизив нагрузку на сервер.</p>\n\n<!-- cut -->\n\n<p>К примеру, имеем следующий CSS-файл <code>screen.css</code>:</p>\n\n<pre><code>@IMPORT url(''/application/css/src/reset.css'');\n@IMPORT url(''/application/css/src/global.css'');\n\n@IMPORT url(''/application/css/src/layouts/l-content.css'');\n@IMPORT url(''/application/css/src/layouts/l-footer.css'');\n@IMPORT url(''/application/css/src/layouts/l-header.css'');\n\n@IMPORT url(''/application/css/src/blocks/b-menu.css'');\n@IMPORT url(''/application/css/src/blocks/b-copyright.css'');\n</code></pre>\n\n<p>Такой файл прекрасно подходит для разработки, но на рабочем сервере это выливается в излишне долгую загрузку сайта и нагрузку на сервер. Поэтому было бы неплохо заменить импорты на содержимое этих файлов.</p>\n\n<p>Для этой задачи прекрасно подходит <a href="http://phing.info/">Phing</a>, поэтому для него было написано задание <code>ImportCssTask</code>, справляющееся с данной задачей.</p>\n\n<h3>Использование</h3>\n\n<p>В билд файле <code>build.xml</code> необходимо объявить новое задание, используя <code>taskdef</code>, а затем вызвать задание, передав путь к <code>DOCUMENT_ROOT</code> проекта через <code>basedir</code>:</p>\n\n<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;\n\n&lt;project default="build" name="blog" description="Simple blog"&gt;\n\n    &lt;property name="sourcedir" value="${project.basedir}/www" override="true" /&gt;\n    &lt;property name="targetdir" value="${project.basedir}/../${phing.project.name}d/www" override="true" /&gt;\n\n    &lt;taskdef name="importcss" classname="phing.tasks.ext.ImportCssTask" /&gt;\n\n    &lt;!-- ... --&gt;\n\n    &lt;target name="optimizeCss"&gt;\n        &lt;importcss basedir="${targetdir}"&gt;\n            &lt;fileset dir="${targetdir}/application/css"&gt;\n                &lt;include name="**.css" /&gt;\n            &lt;/fileset&gt;\n        &lt;/importcss&gt;\n    &lt;/target&gt;\n\n    &lt;!-- ... --&gt;\n\n&lt;/project&gt;\n</code></pre>\n\n<h3>Исходный код</h3>\n\n<p>Исходный код: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/phing/tasks/ext/ImportCssTask.php">ImportCssTask</a>.</p>', 0, '2009-12-13 04:05:08', '2009-12-13 04:07:55', 1),
+(10, 'Ссылки за неделю 1', 'Ssylki_za_nedelyu_1', '*   [Новые возможности Doctrine 1.2](http://doctrine-project.org/blog/what-s-new-in-doctrine-1-2) - довольно много и интересно;\r\n*   [Эскалация привилегий при помощи DDL-триггеров](http://blogs.technet.com/isv_team/archive/2009/12/13/3300225.aspx) - повышений привилегий в MS SQL, на мой взгляд, немного надуманно, но уровень развития SQL показывает хорошо;\r\n*   [Accessing ZF Application Resources In Controller](http://bombdiggity.net/blog/2009/12/09/accessing-zf-application-resources-in-controller/) - получение инициализированных ресурсов в контролере (я до сих пор получал их через `Zend_Registry`);\r\n*   [Select edited file in Project view, Files view or Favourite view](http://blogs.sun.com/netbeansphp/entry/select_in_project_view_files), [Select edited file in Project view, Files view or Favourite view](http://blogs.sun.com/netbeansphp/entry/synchronize_editor_with_views), [CTRL+TAB tip](http://blogs.sun.com/netbeansphp/entry/ctrl_tab_tip) - несколько горячих клавиш для пользователей NetBeans (Ctrl + Shift + 1 использую каждый день);\r\n*   [Синтаксический супер сахар](http://community.livejournal.com/ru_java/884539.html) - программируем на аннотациях.', '<ul>\n<li><a href="http://doctrine-project.org/blog/what-s-new-in-doctrine-1-2">Новые возможности Doctrine 1.2</a> - довольно много и интересно;</li>\n<li><a href="http://blogs.technet.com/isv_team/archive/2009/12/13/3300225.aspx">Эскалация привилегий при помощи DDL-триггеров</a> - повышений привилегий в MS SQL, на мой взгляд, немного надуманно, но уровень развития SQL показывает хорошо;</li>\n<li><a href="http://bombdiggity.net/blog/2009/12/09/accessing-zf-application-resources-in-controller/">Accessing ZF Application Resources In Controller</a> - получение инициализированных ресурсов в контролере (я до сих пор получал их через <code>Zend_Registry</code>);</li>\n<li><a href="http://blogs.sun.com/netbeansphp/entry/select_in_project_view_files">Select edited file in Project view, Files view or Favourite view</a>, <a href="http://blogs.sun.com/netbeansphp/entry/synchronize_editor_with_views">Select edited file in Project view, Files view or Favourite view</a>, <a href="http://blogs.sun.com/netbeansphp/entry/ctrl_tab_tip">CTRL+TAB tip</a> - несколько горячих клавиш для пользователей NetBeans (Ctrl + Shift + 1 использую каждый день);</li>\n<li><a href="http://community.livejournal.com/ru_java/884539.html">Синтаксический супер сахар</a> - программируем на аннотациях.</li>\n</ul>', 0, '2009-12-14 11:52:23', '2009-12-14 11:52:23', 1),
+(11, 'Гидраторы в Doctrine 1.2', 'Gidratory_v_Doctrine_1.2', '*Задача*. Создать гидратор (hydrator) для Doctrine, представляющий результат запроса в виде массива пар ключ-значение.\r\n\r\n<!-- cut -->\r\n\r\nВ Doctrine 1.2 появилась возможность создавать свой гидраторы вдобавок к уже имеющимся. Гидратор определяет вид результата запроса, полученного из БД. Используя разные гидраторы, можно представить результат запроса в виде коллекции объектов (`Doctrine_Collection`), массива, дерева и т.д. Выбрав правильный тип гидратора, вы можете существенно выиграть в скорости и простоте обработки данных.\r\n\r\nВ этой заметке я покажу гидратор Pairs, представляющий результат запроса в виде масива пар ключ-значение. Этот тип гидратора удобно использовать вместе с набором элементов `Zend_Form_Element_Multi` для задания опций выбора.\r\n\r\n## Использование\r\n\r\nДля использования необходимо всего лишь зарегистрировать гидратор:\r\n\r\n    <?php\r\n    $manager->registerHydrator(DoctrineX_Core::HYDRATE_PAIRS, ''DoctrineX_Hydrator_Pairs'');\r\n    ?>\r\n\r\nДалее при выполнении запроса указываем способ гидрирования:\r\n\r\n    <?php\r\n    $options = Doctrine_Query::create()\r\n            ->from(''Street'')\r\n            ->select(''Street.id AS _key, Street.name AS _value'')\r\n            ->orderBy(''Street.name DESC'')\r\n            ->execute(array(), DoctrineX_Core::HYDRATE_PAIRS);\r\n\r\n    $form->street_id->setMultiOptions($options);\r\n    ?>\r\n\r\n## Исходный код\r\n\r\nДля создания своего гидратора необходимо расширить класс `Doctrine_Hydrator_Abstract` и реализовать в нем метод `hydrateResultSet()`, принимающий в качестве единственного параметра экземпляр класса `PDOStatement`:\r\n\r\n    <?php\r\n    class DoctrineX_Hydrator_Pairs extends Doctrine_Hydrator_Abstract\r\n    {\r\n        const KEY = ''_key'';\r\n        const VALUE = ''_value'';\r\n\r\n        public function hydrateResultSet($stmt)\r\n        {\r\n            $data = $stmt->fetchAll(Doctrine_Core::FETCH_ASSOC);\r\n\r\n            if (empty($data)) {\r\n                return $data;\r\n            }\r\n\r\n            foreach ($data[0] as $key => $value) {\r\n                if ($key == ''DOCTRINE_ROWNUM'') {\r\n                    continue;\r\n                }\r\n\r\n                $e = explode(''__'', $key);\r\n                $columnName = strtolower(array_pop($e));\r\n                $dqlAlias = $this->_tableAliases[strtolower(implode(''__'', $e))];\r\n                $table = $this->_queryComponents[$dqlAlias][''table''];\r\n\r\n                if (isset($this->_queryComponents[$dqlAlias][''agg''][$columnName])) {\r\n                    $fieldName = $this->_queryComponents[$dqlAlias][''agg''][$columnName];\r\n                } else {\r\n                    $fieldName = $table->getFieldName($columnName);\r\n                }\r\n\r\n                if ($fieldName == self::KEY) {\r\n                    $keyColumnName = $key;\r\n                } else if ($fieldName == self::VALUE) {\r\n                    $valueColumnName = $key;\r\n                }\r\n            }\r\n\r\n            if (!isset($keyColumnName) || !isset($valueColumnName)) {\r\n                throw new Doctrine_Exception(''Can not find field "'' . self::KEY . ''" or "'' . self::VALUE . ''"'');\r\n            }\r\n\r\n            $result = array();\r\n            foreach ($data as $d) {\r\n                $result[$d[$keyColumnName]] = $d[$valueColumnName];\r\n            }\r\n\r\n            return $result;\r\n        }\r\n    }\r\n    ?>\r\n\r\nИсходный код: [DoctrineX_Hydrator_Pairs](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/DoctrineX/Hydrator/Pairs.php).', '<p><em>Задача</em>. Создать гидратор (hydrator) для Doctrine, представляющий результат запроса в виде массива пар ключ-значение.</p>\n\n<!-- cut -->\n\n<p>В Doctrine 1.2 появилась возможность создавать свой гидраторы вдобавок к уже имеющимся. Гидратор определяет вид результата запроса, полученного из БД. Используя разные гидраторы, можно представить результат запроса в виде коллекции объектов (<code>Doctrine_Collection</code>), массива, дерева и т.д. Выбрав правильный тип гидратора, вы можете существенно выиграть в скорости и простоте обработки данных.</p>\n\n<p>В этой заметке я покажу гидратор Pairs, представляющий результат запроса в виде масива пар ключ-значение. Этот тип гидратора удобно использовать вместе с набором элементов <code>Zend_Form_Element_Multi</code> для задания опций выбора.</p>\n\n<h2>Использование</h2>\n\n<p>Для использования необходимо всего лишь зарегистрировать гидратор:</p>\n\n<pre><code>&lt;?php\n$manager-&gt;registerHydrator(DoctrineX_Core::HYDRATE_PAIRS, ''DoctrineX_Hydrator_Pairs'');\n?&gt;\n</code></pre>\n\n<p>Далее при выполнении запроса указываем способ гидрирования:</p>\n\n<pre><code>&lt;?php\n$options = Doctrine_Query::create()\n        -&gt;from(''Street'')\n        -&gt;select(''Street.id AS _key, Street.name AS _value'')\n        -&gt;orderBy(''Street.name DESC'')\n        -&gt;execute(array(), DoctrineX_Core::HYDRATE_PAIRS);\n\n$form-&gt;street_id-&gt;setMultiOptions($options);\n?&gt;\n</code></pre>\n\n<h2>Исходный код</h2>\n\n<p>Для создания своего гидратора необходимо расширить класс <code>Doctrine_Hydrator_Abstract</code> и реализовать в нем метод <code>hydrateResultSet()</code>, принимающий в качестве единственного параметра экземпляр класса <code>PDOStatement</code>:</p>\n\n<pre><code>&lt;?php\nclass DoctrineX_Hydrator_Pairs extends Doctrine_Hydrator_Abstract\n{\n    const KEY = ''_key'';\n    const VALUE = ''_value'';\n\n    public function hydrateResultSet($stmt)\n    {\n        $data = $stmt-&gt;fetchAll(Doctrine_Core::FETCH_ASSOC);\n\n        if (empty($data)) {\n            return $data;\n        }\n\n        foreach ($data[0] as $key =&gt; $value) {\n            if ($key == ''DOCTRINE_ROWNUM'') {\n                continue;\n            }\n\n            $e = explode(''__'', $key);\n            $columnName = strtolower(array_pop($e));\n            $dqlAlias = $this-&gt;_tableAliases[strtolower(implode(''__'', $e))];\n            $table = $this-&gt;_queryComponents[$dqlAlias][''table''];\n\n            if (isset($this-&gt;_queryComponents[$dqlAlias][''agg''][$columnName])) {\n                $fieldName = $this-&gt;_queryComponents[$dqlAlias][''agg''][$columnName];\n            } else {\n                $fieldName = $table-&gt;getFieldName($columnName);\n            }\n\n            if ($fieldName == self::KEY) {\n                $keyColumnName = $key;\n            } else if ($fieldName == self::VALUE) {\n                $valueColumnName = $key;\n            }\n        }\n\n        if (!isset($keyColumnName) || !isset($valueColumnName)) {\n            throw new Doctrine_Exception(''Can not find field "'' . self::KEY . ''" or "'' . self::VALUE . ''"'');\n        }\n\n        $result = array();\n        foreach ($data as $d) {\n            $result[$d[$keyColumnName]] = $d[$valueColumnName];\n        }\n\n        return $result;\n    }\n}\n?&gt;\n</code></pre>\n\n<p>Исходный код: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/library/DoctrineX/Hydrator/Pairs.php">DoctrineX_Hydrator_Pairs</a>.</p>\n', 2, '2009-12-28 13:21:06', '2010-01-22 04:20:19', 1),
+(12, 'Конференция по Zend Framework в России', 'Konferenciya_po_Zend_Framework_v_Rossii', 'Активисты сайта [zendframework.ru](http://zendframework.ru) планируют провести в Санкт-Петербурге [конференцию по Zend Framework](http://zendframework.ru/forum/index.php?topic=2019.0). С темами, докладчиками и участниками еще определяются, поэтому у вас есть все шансы повлиять на программу конференции ;)', '<p>Активисты сайта <a href="http://zendframework.ru">zendframework.ru</a> планируют провести в Санкт-Петербурге <a href="http://zendframework.ru/forum/index.php?topic=2019.0">конференцию по Zend Framework</a>. С темами, докладчиками и участниками еще определяются, поэтому у вас есть все шансы повлиять на программу конференции ;)</p>\n', 0, '2010-01-10 05:20:56', '2010-01-10 05:20:56', 1),
+(13, 'Калькулятор сети на JavaScript', 'Kalkulyator_seti_na_JavaScript', '*Задача*. Создать калькулятор сети для рассчета адреса сети, широковещательного адреса,  диапазона допустимых адресов в сети и количества хостов на основе IP-адреса и маски, например, 192.168.0.0/16.\r\n\r\n<!-- cut -->\r\n\r\n<script type="text/javascript" src="system/js/String.js"></script>\r\n<script type="text/javascript" src="system/module/blog/data/js/IpAddress.js"></script>\r\n<script type="text/javascript">\r\n(function ($) {\r\n\r\n$(function () {\r\n\r\n    $(''#calc'').click(function () {\r\n        var ip = new IpAddress(), mask = new IpAddress();\r\n        ip.fromDec($(''#ip'').val());\r\n        mask.fill(parseInt($(''#mask'').val()));\r\n        var network = ip.mask(mask);\r\n        var wildcard = mask.invert();\r\n        var broadcast = network.wildcard(wildcard);\r\n        var minHost = network.next();\r\n        var maxHost = broadcast.prev();\r\n\r\n        var result = '''';\r\n\r\n        var ipParts = ip.toDec();\r\n        if (ipParts[0] >= 1 && ipParts[0] <= 126) {\r\n            result += ''<tr><td>Class</td><td>A (255.0.0.0)</td></tr>'';\r\n        } else if (ipParts[0] >= 128 && ipParts[0] <= 191\r\n                && ipParts[1] >= 0 && ipParts[1] <= 255) {\r\n            result += ''<tr><td>Class</td><td>B (255.255.0.0)</td></tr>'';\r\n        } else if (ipParts[0] >= 192 && ipParts[0] <= 255\r\n                && ipParts[1] >= 0 && ipParts[1] <= 254\r\n                && ipParts[2] >= 0 && ipParts[2] <= 255) {\r\n            result += ''<tr><td>Class</td><td>C (255.255.255.0)</td></tr>'';\r\n        }\r\n\r\n        result += ''<tr><td>Address ('' + ip.toDec().join(''.'') + '')</td><td>''\r\n                + ip.toBin().join('' '') + ''</td></tr>'';\r\n        result += ''<tr><td>Mask ('' + mask.toDec().join(''.'') + '')</td><td>''\r\n                + mask.toBin().join('' '') + ''</td></tr>'';\r\n        result += ''<tr><td>Wildcard ('' + wildcard.toDec().join(''.'')\r\n                + '')</td><td>'' + wildcard.toBin().join('' '') + ''</td></tr>'';\r\n        result += ''<tr><td>Network address ('' + network.toDec().join(''.'')\r\n                + '')</td><td>'' + network.toBin().join('' '') + ''</td></tr>'';\r\n        result += ''<tr><td>Broadcast ('' + broadcast.toDec().join(''.'')\r\n                + '')</td><td>'' + broadcast.toBin().join('' '') + ''</td></tr>'';\r\n        result += ''<tr><td>Min host address ('' + minHost.toDec().join(''.'')\r\n                + '')</td><td>'' + minHost.toBin().join('' '') + ''</td></tr>'';\r\n        result += ''<tr><td>Max host address ('' + maxHost.toDec().join(''.'')\r\n                + '')</td><td>'' + maxHost.toBin().join('' '') + ''</td></tr>'';\r\n        result += ''<tr><td>Host number:</td><td>'' + wildcard.countHosts()\r\n                + '' - 2</td></tr>'';\r\n\r\n        $(''#result'').html(''<table class="b-table">'' + result + ''</table>'');\r\n    });\r\n\r\n});\r\n\r\n})(jQuery);\r\n</script>\r\n\r\n<form action="">\r\n    <dl>\r\n      <dt>IP, например, 192.168.0.0/16</dt>\r\n      <dd>\r\n      <input type="text" id="ip" size="16" />\r\n      /\r\n      <input type="text" id="mask" size="3" />\r\n      <input type="button" id="calc" value="Calculate" />\r\n      </dd>\r\n    </dl>\r\n</form>\r\n\r\n<div id="result" style="padding: 20px 0;"></div>\r\n\r\nКод получился довольно простым благодаря двум очень удобным функциям JavaScript:\r\n\r\n- [parseInt()](https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/ParseInt) - позволяет преобразовать строку в определенной системе счисления в десятичное целое число;\r\n- [number.toString()](https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/toString) - обратное преобразование десятичного числа в стоку, представляющее число в другой системе счисления, например, двоичной.\r\n\r\n## Исходный код\r\n\r\nИсходный код: [IpAddress](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/js/IpAddress.js).', '<p><em>Задача</em>. Создать калькулятор сети для рассчета адреса сети, широковещательного адреса,  диапазона допустимых адресов в сети и количества хостов на основе IP-адреса и маски, например, 192.168.0.0/16.</p>\n\n<!-- cut -->\n\n<p><script type="text/javascript" src="system/js/String.js"></script>\n<script type="text/javascript" src="system/module/blog/data/js/IpAddress.js"></script></p>\n\n<script type="text/javascript">\n(function ($) {\n\n$(function () {\n\n    $(''#calc'').click(function () {\n        var ip = new IpAddress(), mask = new IpAddress();\n        ip.fromDec($(''#ip'').val());\n        mask.fill(parseInt($(''#mask'').val()));\n        var network = ip.mask(mask);\n        var wildcard = mask.invert();\n        var broadcast = network.wildcard(wildcard);\n        var minHost = network.next();\n        var maxHost = broadcast.prev();\n\n        var result = '''';\n\n        var ipParts = ip.toDec();\n        if (ipParts[0] >= 1 && ipParts[0] <= 126) {\n            result += ''<tr><td>Class</td><td>A (255.0.0.0)</td></tr>'';\n        } else if (ipParts[0] >= 128 && ipParts[0] <= 191\n                && ipParts[1] >= 0 && ipParts[1] <= 255) {\n            result += ''<tr><td>Class</td><td>B (255.255.0.0)</td></tr>'';\n        } else if (ipParts[0] >= 192 && ipParts[0] <= 255\n                && ipParts[1] >= 0 && ipParts[1] <= 254\n                && ipParts[2] >= 0 && ipParts[2] <= 255) {\n            result += ''<tr><td>Class</td><td>C (255.255.255.0)</td></tr>'';\n        }\n\n        result += ''<tr><td>Address ('' + ip.toDec().join(''.'') + '')</td><td>''\n                + ip.toBin().join('' '') + ''</td></tr>'';\n        result += ''<tr><td>Mask ('' + mask.toDec().join(''.'') + '')</td><td>''\n                + mask.toBin().join('' '') + ''</td></tr>'';\n        result += ''<tr><td>Wildcard ('' + wildcard.toDec().join(''.'')\n                + '')</td><td>'' + wildcard.toBin().join('' '') + ''</td></tr>'';\n        result += ''<tr><td>Network address ('' + network.toDec().join(''.'')\n                + '')</td><td>'' + network.toBin().join('' '') + ''</td></tr>'';\n        result += ''<tr><td>Broadcast ('' + broadcast.toDec().join(''.'')\n                + '')</td><td>'' + broadcast.toBin().join('' '') + ''</td></tr>'';\n        result += ''<tr><td>Min host address ('' + minHost.toDec().join(''.'')\n                + '')</td><td>'' + minHost.toBin().join('' '') + ''</td></tr>'';\n        result += ''<tr><td>Max host address ('' + maxHost.toDec().join(''.'')\n                + '')</td><td>'' + maxHost.toBin().join('' '') + ''</td></tr>'';\n        result += ''<tr><td>Host number:</td><td>'' + wildcard.countHosts()\n                + '' - 2</td></tr>'';\n\n        $(''#result'').html(''<table class="b-table">'' + result + ''</table>'');\n    });\n\n});\n\n})(jQuery);\n</script>\n\n<form action="">\n    <dl>\n      <dt>IP, например, 192.168.0.0/16</dt>\n      <dd>\n      <input type="text" id="ip" size="16" />\n      /\n      <input type="text" id="mask" size="3" />\n      <input type="button" id="calc" value="Calculate" />\n      </dd>\n    </dl>\n</form>\n\n<div id="result" style="padding: 20px 0;"></div>\n\n<p>Код получился довольно простым благодаря двум очень удобным функциям JavaScript:</p>\n\n<ul>\n<li><a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/ParseInt">parseInt()</a> - позволяет преобразовать строку в определенной системе счисления в десятичное целое число;</li>\n<li><a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/toString">number.toString()</a> - обратное преобразование десятичного числа в стоку, представляющее число в другой системе счисления, например, двоичной.</li>\n</ul>\n\n<h2>Исходный код</h2>\n\n<p>Исходный код: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/tip/js/IpAddress.js">IpAddress</a>.</p>\n', 1, '2010-01-22 03:25:40', '2010-01-22 03:26:45', 1),
+(14, 'Ссылки за неделю 2', 'Ssylki_za_nedelyu_2', '- [Хелперы для упаковки JS и CSS](http://hobodave.com/2010/01/17/bundle-phu-compress-your-js-css-in-zend-framework/) - 2 хелпера для Zend Framework, которые упакуют и соединят несколько JS/CSS-файлов в один ([упаковщик CSS для Phing](http://loutontheweb.co.cc/entries/read/Zadanie_dlya_Phing_po_optimizacii_CSS));\r\n- [Простые шаблоны на JavaScript](http://sreznikov.blogspot.com/2010/01/supplant.html) - простейшие шаблоны для JavaScript, чтобы отделить JavaScript от HTML;\r\n- [Перевод сообщений валидации](http://thomasweidner.com/flatpress/2010/01/17/translating-validation-messages/) - в транке Zend Framework в папке `resources/languages` появились переводы сообщений валидации, русского пока нет.', '<ul>\n<li><a href="http://hobodave.com/2010/01/17/bundle-phu-compress-your-js-css-in-zend-framework/">Хелперы для упаковки JS и CSS</a> - 2 хелпера для Zend Framework, которые упакуют и соединят несколько JS/CSS-файлов в один (<a href="http://loutontheweb.co.cc/entries/read/Zadanie_dlya_Phing_po_optimizacii_CSS">упаковщик CSS для Phing</a>);</li>\n<li><a href="http://sreznikov.blogspot.com/2010/01/supplant.html">Простые шаблоны на JavaScript</a> - простейшие шаблоны для JavaScript, чтобы отделить JavaScript от HTML;</li>\n<li><a href="http://thomasweidner.com/flatpress/2010/01/17/translating-validation-messages/">Перевод сообщений валидации</a> - в транке Zend Framework в папке <code>resources/languages</code> появились переводы сообщений валидации, русского пока нет.</li>\n</ul>\n', 1, '2010-01-22 12:02:16', '2010-01-22 12:07:34', 1),
+(15, 'Расширение Zend_Translate для поддержки переводов по умолчанию', 'Rasshirenie_Zend_Translate_dlya_podderzhki_perevodov_po_umolchaniyu', '*Задача*. Если при использовании компонента `Zend_Translate` перевода для текущей локали (языка) нет, то попытаться перевести строку, используя другие локали. Трекер Zend Framework: [ZF-2736](http://framework.zend.com/issues/browse/ZF-2736).\r\n\r\n<!-- cut -->\r\n\r\nВ трекере Zend Framework [соответствующее предложение](http://framework.zend.com/issues/browse/ZF-2736) было создано еще в 2008 году, но по-видимому не встретило большой поддержки. Суть его в том, чтобы в случае отсутствия перевода на русский язык, использовался перевод, например, на английский язык. Т.к. Zend Framework чрезвычайно гибкий и расширяемый, поэтому реализация данной возможности не составит большого труда.\r\n\r\nДля добавления необходимой функциональности мы расширяем класс `Zend_Translate`. Единственным неочевидным моментом здесь является переопределение метода `getAdapter()`, чтобы он возвращал экземпляр нашего класса. Сделано это из-за того, что Zend_Translate является декоратором для класса `Zend_Translate_Adapter_*`, поэтому, когда функции декоратора не нужны, для увеличения производительности используют напрямую `Zend_Translate_Adapter_*`, получая его экземпляр через метод `getAdapter()`:\r\n\r\n    <?php\r\n    class ZendY_Translate extends Zend_Translate\r\n    {\r\n        private static $_fallbackLocales = array();\r\n\r\n        public static function setFallbackLocales(array $value)\r\n        {\r\n            self::$_fallbackLocales = $value;\r\n        }\r\n\r\n        public static function getFallbackLocales()\r\n        {\r\n            return self::$_fallbackLocales;\r\n        }\r\n\r\n        public function getAdapter()\r\n        {\r\n            return $this;\r\n        }\r\n\r\n        public function translate($messageId, $locale = null)\r\n        {\r\n            $adapter = parent::getAdapter();\r\n\r\n            $locales = array_merge(array($locale), self::$_fallbackLocales);\r\n            foreach ($locales as $currentLocale) {\r\n                if ($adapter->isTranslated($messageId, false, $currentLocale)) {\r\n                    return $adapter->translate($messageId, $currentLocale);\r\n                }\r\n            }\r\n\r\n            return $adapter->translate($messageId, $locale);\r\n        }\r\n    }\r\n    ?>\r\n\r\n## Использование\r\n\r\nДля использования необходимо сконфигурировать класс `ZendY_Translate` и создать его экземпляр:\r\n\r\n    <?php\r\n    ZendY_Translate::setFallbackLocales(array(''en''));\r\n    $translator = new ZendY_Translate(/* ... */);\r\n\r\n    Zend_Registy::set(''Zend_Translate'', $translator);\r\n    ?>\r\n\r\n## Исходный код\r\n\r\nИсходный код: [ZendY_Translate](http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/a5c3e6e35f9c/library/ZendY/Translate.php).', '<p><em>Задача</em>. Если при использовании компонента <code>Zend_Translate</code> перевода для текущей локали (языка) нет, то попытаться перевести строку, используя другие локали. Трекер Zend Framework: <a href="http://framework.zend.com/issues/browse/ZF-2736">ZF-2736</a>.</p>\n\n<!-- cut -->\n\n<p>В трекере Zend Framework <a href="http://framework.zend.com/issues/browse/ZF-2736">соответствующее предложение</a> было создано еще в 2008 году, но по-видимому не встретило большой поддержки. Суть его в том, чтобы в случае отсутствия перевода на русский язык, использовался перевод, например, на английский язык. Т.к. Zend Framework чрезвычайно гибкий и расширяемый, поэтому реализация данной возможности не составит большого труда.</p>\n\n<p>Для добавления необходимой функциональности мы расширяем класс <code>Zend_Translate</code>. Единственным неочевидным моментом здесь является переопределение метода <code>getAdapter()</code>, чтобы он возвращал экземпляр нашего класса. Сделано это из-за того, что Zend_Translate является декоратором для класса <code>Zend_Translate_Adapter_*</code>, поэтому, когда функции декоратора не нужны, для увеличения производительности используют напрямую <code>Zend_Translate_Adapter_*</code>, получая его экземпляр через метод <code>getAdapter()</code>:</p>\n\n<pre><code>&lt;?php\nclass ZendY_Translate extends Zend_Translate\n{\n    private static $_fallbackLocales = array();\n\n    public static function setFallbackLocales(array $value)\n    {\n        self::$_fallbackLocales = $value;\n    }\n\n    public static function getFallbackLocales()\n    {\n        return self::$_fallbackLocales;\n    }\n\n    public function getAdapter()\n    {\n        return $this;\n    }\n\n    public function translate($messageId, $locale = null)\n    {\n        $adapter = parent::getAdapter();\n\n        $locales = array_merge(array($locale), self::$_fallbackLocales);\n        foreach ($locales as $currentLocale) {\n            if ($adapter-&gt;isTranslated($messageId, false, $currentLocale)) {\n                return $adapter-&gt;translate($messageId, $currentLocale);\n            }\n        }\n\n        return $adapter-&gt;translate($messageId, $locale);\n    }\n}\n?&gt;\n</code></pre>\n\n<h2>Использование</h2>\n\n<p>Для использования необходимо сконфигурировать класс <code>ZendY_Translate</code> и создать его экземпляр:</p>\n\n<pre><code>&lt;?php\nZendY_Translate::setFallbackLocales(array(''en''));\n$translator = new ZendY_Translate(/* ... */);\n\nZend_Registy::set(''Zend_Translate'', $translator);\n?&gt;\n</code></pre>\n\n<h2>Исходный код</h2>\n\n<p>Исходный код: <a href="http://bitbucket.org/vladimir_webdev/vladimirwebdev/src/a5c3e6e35f9c/library/ZendY/Translate.php">ZendY_Translate</a>.</p>\n', 0, '2010-01-28 15:38:20', '2010-01-28 15:38:51', 1);
+INSERT INTO `blog__entry` (`id`, `title`, `tid`, `content`, `contenthtml`, `comment_number`, `createdat`, `updatedat`, `user_id`) VALUES
+(16, 'Ссылки за неделю 3', 'Ssylki_za_nedelyu_3', '- [Правильное хеширование](http://benlog.com/articles/2008/06/19/dont-hash-secrets/) - подробное объяснение в каких случаях с точки зрения безопасности уместно хеширование.\r\n- [Отношения фрилансера и заказчика](http://marketing-audit.ru/notes/notes_36.html) - прекрасно описаны ожидания заказчика и фрилансера, также стоит посмотреть на [шесть ошибок фрилансеров](http://marketing-audit.ru/notes/notes_35.html).\r\n- [Zend_Auth_Adapter для Doctrine](http://zendcasts.com/writing-a-zend_auth_adapter-with-doctrine/2010/01/) - скринкаст о написании адаптера Zend_Auth_Adapter для Doctrine, [статья](http://palo-verde.us/?blog/2009/08/16/zend_auth-adapter-with-doctrine.html).\r\n- [Использование API Drupal](http://rmcreative.ru/blog/post/ispolzovanie-drupal-cherez-bootstrap) - пример как проинициализировать Drupal и использовать его API из стороннего приложения.\r\n- [Советы и трюки для Zend Framework](http://dev.juokaz.com/php/zend-framework-tips-and-tricks) - отличные советы по организации работы с Zend Framework.', '<ul>\n<li><a href="http://benlog.com/articles/2008/06/19/dont-hash-secrets/">Правильное хеширование</a> - подробное объяснение в каких случаях с точки зрения безопасности уместно хеширование.</li>\n<li><a href="http://marketing-audit.ru/notes/notes_36.html">Отношения фрилансера и заказчика</a> - прекрасно описаны ожидания заказчика и фрилансера, также стоит посмотреть на <a href="http://marketing-audit.ru/notes/notes_35.html">шесть ошибок фрилансеров</a>.</li>\n<li><a href="http://zendcasts.com/writing-a-zend_auth_adapter-with-doctrine/2010/01/">Zend_Auth_Adapter для Doctrine</a> - скринкаст о написании адаптера Zend_Auth_Adapter для Doctrine, <a href="http://palo-verde.us/?blog/2009/08/16/zend_auth-adapter-with-doctrine.html">статья</a>.</li>\n<li><a href="http://rmcreative.ru/blog/post/ispolzovanie-drupal-cherez-bootstrap">Использование API Drupal</a> - пример как проинициализировать Drupal и использовать его API из стороннего приложения.</li>\n<li><a href="http://dev.juokaz.com/php/zend-framework-tips-and-tricks">Советы и трюки для Zend Framework</a> - отличные советы по организации работы с Zend Framework.</li>\n</ul>\n', 0, '2010-01-30 05:36:26', '2010-01-30 05:44:29', 1),
+(17, 'В конце марта в Санкт-Петербурге пройдет конференция по Zend Framework', 'V_konce_marta_v_Sankt-Peterburge_projdet_konferenciya_po_Zend_Framework', '[Организаторы первой в России конференции по Zend Framework](http://zfconf.ru/) определились с темами докладов, датой и временем проведения конференции. Она пройдет в конце марта 2010 г. в Санкт-Петербурге. На данном этапе важно определить примерное число участников, поэтому желающие посетить конференцию должны [**предварительно зарегистрироваться**](http://zfconf.ru/pre-registration.php). Темы докладов доступны на [сайте конференции](http://zfconf.ru/), обсудить же их можно на форуме [zendframework.ru](http://zendframework.ru/forum/index.php?topic=2281.0). Конференция планируется бесплатной, а в качестве одного из докладчиков будет выступать [Александр Веремьев](http://veremev.moikrug.ru/), Zend Framework Core команда.\r\n\r\n<!-- cut -->\r\n\r\n## Планируемые доклады\r\n\r\n- [Жизненный цикл предложений (proposals) в проекте Zend Framework](http://zendframework.ru/forum/index.php?topic=2269.0)  \r\nАлександр Веремьев ([Zend Technologies](http://zend.com/), Zend Framework Core команда)\r\n- [Zend\\_Search\\_Lucene в деталях](http://zendframework.ru/forum/index.php?topic=2270.0)  \r\nАлександр Веремьев ([Zend Technologies](http://zend.com/), Zend Framework Core команда)\r\n- [Zend Framework и производительность](http://zendframework.ru/forum/index.php?topic=2271.0)  \r\nАлександр Махомет (создатель сообщества [ZendFramework.ru](http://zendframework.ru/))\r\n- [Zend Framework и MVC, «толстая» модель](http://zendframework.ru/forum/index.php?topic=2272.0)  \r\nАлександр Стешенко ([Norada Corporation](http://norada.com/), PHP-разработчик)\r\n- [ФотоСтрана.ru: Прототипирование с использованием ZF (история боевого применения Zend Framework в highload-проекте)](http://zendframework.ru/forum/index.php?topic=2273.0)  \r\nЛеонид Жаворонков ([ФотоСтрана.ru](http://fotostrana.ru/), тимлид)\r\n- [Использование очередей сообщений в повседневных проектах](http://zendframework.ru/forum/index.php?topic=2274.0)  \r\nДенис Баклыков ([Обновление](http://obnovlenie.ru/), веб-разработчик)\r\n- [Zend Framework и Doctrine](http://zendframework.ru/forum/index.php?topic=2275.0)  \r\nСтепан Танасийчук (руководитель веб-студии [stfalcon.com](http://stfalcon.com/))\r\n- [Zend Framework и мультиязычность](http://zendframework.ru/forum/index.php?topic=2276.0)  \r\nСтепан Танасийчук (руководитель веб-студии [stfalcon.com](http://stfalcon.com/))\r\n- [История проекта e-Штаб](http://zendframework.ru/forum/index.php?topic=2277.0)  \r\nАнатолий Ларин ([e-Легион](http://e-legion.com/), веб-программист)\r\n- [Что нового несет нам Zend Framework 2.0?](http://zendframework.ru/forum/index.php?topic=2278.0)  \r\nНадежда Блинова ([Wizartech](http://wizartech.ru/), веб-программист), Георгий Туревич ([Wizartech](http://wizartech.ru/), ведущий веб-программист)\r\n- [Интеграция Zend Framework c Javascript-фрэймворками jQuery и Dojo Toolkit](http://zendframework.ru/forum/index.php?topic=2279.0)  \r\nГеоргий Туревич ([Wizartech](http://wizartech.ru/), ведущий веб-программист) ', '<p><a href="http://zfconf.ru/">Организаторы первой в России конференции по Zend Framework</a> определились с темами докладов, датой и временем проведения конференции. Она пройдет в конце марта 2010 г. в Санкт-Петербурге. На данном этапе важно определить примерное число участников, поэтому желающие посетить конференцию должны <a href="http://zfconf.ru/pre-registration.php"><strong>предварительно зарегистрироваться</strong></a>. Темы докладов доступны на <a href="http://zfconf.ru/">сайте конференции</a>, обсудить же их можно на форуме <a href="http://zendframework.ru/forum/index.php?topic=2281.0">zendframework.ru</a>. Конференция планируется бесплатной, а в качестве одного из докладчиков будет выступать <a href="http://veremev.moikrug.ru/">Александр Веремьев</a>, Zend Framework Core команда.</p>\n\n<!-- cut -->\n\n<h2>Планируемые доклады</h2>\n\n<ul>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2269.0">Жизненный цикл предложений (proposals) в проекте Zend Framework</a><br />\nАлександр Веремьев (<a href="http://zend.com/">Zend Technologies</a>, Zend Framework Core команда)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2270.0">Zend&#95;Search&#95;Lucene в деталях</a><br />\nАлександр Веремьев (<a href="http://zend.com/">Zend Technologies</a>, Zend Framework Core команда)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2271.0">Zend Framework и производительность</a><br />\nАлександр Махомет (создатель сообщества <a href="http://zendframework.ru/">ZendFramework.ru</a>)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2272.0">Zend Framework и MVC, «толстая» модель</a><br />\nАлександр Стешенко (<a href="http://norada.com/">Norada Corporation</a>, PHP-разработчик)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2273.0">ФотоСтрана.ru: Прототипирование с использованием ZF (история боевого применения Zend Framework в highload-проекте)</a><br />\nЛеонид Жаворонков (<a href="http://fotostrana.ru/">ФотоСтрана.ru</a>, тимлид)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2274.0">Использование очередей сообщений в повседневных проектах</a><br />\nДенис Баклыков (<a href="http://obnovlenie.ru/">Обновление</a>, веб-разработчик)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2275.0">Zend Framework и Doctrine</a><br />\nСтепан Танасийчук (руководитель веб-студии <a href="http://stfalcon.com/">stfalcon.com</a>)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2276.0">Zend Framework и мультиязычность</a><br />\nСтепан Танасийчук (руководитель веб-студии <a href="http://stfalcon.com/">stfalcon.com</a>)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2277.0">История проекта e-Штаб</a><br />\nАнатолий Ларин (<a href="http://e-legion.com/">e-Легион</a>, веб-программист)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2278.0">Что нового несет нам Zend Framework 2.0?</a><br />\nНадежда Блинова (<a href="http://wizartech.ru/">Wizartech</a>, веб-программист), Георгий Туревич (<a href="http://wizartech.ru/">Wizartech</a>, ведущий веб-программист)</li>\n<li><a href="http://zendframework.ru/forum/index.php?topic=2279.0">Интеграция Zend Framework c Javascript-фрэймворками jQuery и Dojo Toolkit</a><br />\nГеоргий Туревич (<a href="http://wizartech.ru/">Wizartech</a>, ведущий веб-программист) </li>\n</ul>\n', 0, '2010-02-01 13:02:26', '2010-02-01 13:15:03', 1),
+(18, 'Добавить Spring IoC в приложение Swing Application Framework', 'Dobavit_Spring_IoC_v_prilozhenie_Swing_Application_Framework', '*Задача*. Добавить в десктопный Java-проект на основе Swing Application Framework поддержку Spring Inversion of Control (Dependency Injection).\r\n\r\n<!-- cut -->\r\n\r\nПроект был создан в NetBeans, используя встроенный архетип Maven "Swing Application Framework (JSR 296) Archetype 1.0" (Maven -> Maven Project).\r\n\r\nДля начала необходимо добавить Sping IoC в список зависимостей в файл `pom.xml`:\r\n\r\n    <dependency>\r\n      <groupId>org.springframework</groupId>\r\n      <artifactId>spring-context</artifactId>\r\n      <version>3.0.0.RELEASE</version>\r\n    </dependency>\r\n\r\nSpring также использует библиотеку cglib для работы с бинами, использующими [Java-конфигурацию](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/new-in-3.html#new-java-configuration) (Java based bean metadata):\r\n\r\n    <dependency>\r\n      <groupId>cglib</groupId>\r\n      <artifactId>cglib-nodep</artifactId>\r\n      <version>2.2</version>\r\n    </dependency>\r\n\r\nТеперь необходимо проинициализиовать `ApplicationContext` в функции `main()`. Как уже сказано выше, я задаю бины прямо на Java, не используя XML, поэтому в конструктор контекста я передаю класс, содержащий определения бинов:\r\n\r\n    public static void main(String[] args)\r\n    {\r\n        AnnotationConfigApplicationContext ctx =\r\n                new AnnotationConfigApplicationContext(ApplicationConfig.class)\r\n\r\n        launch(DesktopApplication1.class, args);\r\n    }\r\n\r\nКласс ApplicationConfig может выглядеть так (см. документацию [Java based bean metadata](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/new-in-3.html#new-java-configuration)):\r\n\r\n    @Configuration\r\n    public class ApplicationConfig\r\n    {\r\n        @Bean\r\n        public Test getTest\r\n        {\r\n            return new Test();\r\n        }\r\n    }\r\n\r\nТеперь осталось добавить поддержку IoC для юнит-тестов. Первым делом необходимо обновить версию JUnit:\r\n\r\n    <dependency>\r\n      <groupId>junit</groupId>\r\n      <artifactId>junit</artifactId>\r\n      <version>4.7</version>\r\n      <scope>test</scope>\r\n    </dependency>\r\n\r\nИ добавить специальную [Spring Test](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html) библиотеку:\r\n\r\n    <dependency>\r\n      <groupId>org.springframework</groupId>\r\n      <artifactId>spring-test</artifactId>\r\n      <version>3.0.0.RELEASE</version>\r\n      <scope>test</scope>\r\n    </dependency>\r\n\r\nСразу же неплохо бы добавить log4j и slf4j, как это [рекомендуют в документации к Spring](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/overview.html#d0e738):\r\n\r\n    <dependency>\r\n      <groupId>log4j</groupId>\r\n      <artifactId>log4j</artifactId>\r\n      <version>1.2.14</version>\r\n    </dependency>\r\n    <dependency>\r\n      <groupId>org.slf4j</groupId>\r\n      <artifactId>slf4j-api</artifactId>\r\n      <version>1.5.10</version>\r\n    </dependency>\r\n    <dependency>\r\n      <groupId>org.slf4j</groupId>\r\n      <artifactId>slf4j-log4j12</artifactId>\r\n      <version>1.5.8</version>\r\n    </dependency>\r\n\r\nЮнит-тесты, использующие Spring IoC, необходимо оформлять следующим образом:\r\n\r\n    @RunWith(SpringJUnit4ClassRunner.class)\r\n    @ContextConfiguration(loader = AnnotationConfigContextLoader.class,\r\n            value = "com.sample.ApplicationConfig")\r\n    public class IocTest {\r\n        @Autowired\r\n        Test test;\r\n\r\n        @Test\r\n        public void testIoc()\r\n        {\r\n            Assert.assertNotNull(test);\r\n        }\r\n    }\r\n\r\nОсталось только привести код специального лоадера `AnnotationConfigContextLoader`, которого пока нет в стандартной поставке (его код мне подсказали на [stackoverflow.com](http://stackoverflow.com/questions/2228820/add-spring-3-0-0-java-based-ioc-to-junit-4-7-tests)):\r\n\r\n    public class AnnotationConfigContextLoader implements ContextLoader\r\n    {\r\n        public ApplicationContext loadContext(String... locations) throws Exception\r\n        {\r\n            Class<?>[] configClasses = new Class<?>[locations.length];\r\n            for (int i = 0; i < locations.length; i++) {\r\n                configClasses[i] = Class.forName(locations[i]);\r\n            }\r\n            return new AnnotationConfigApplicationContext(configClasses);\r\n        }\r\n\r\n        public String[] processLocations(Class<?> c, String... locations)\r\n        {\r\n            return locations;\r\n        }\r\n    }', '<p><em>Задача</em>. Добавить в десктопный Java-проект на основе Swing Application Framework поддержку Spring Inversion of Control (Dependency Injection).</p>\r\n\r\n<!-- cut -->\r\n\r\n<p>Проект был создан в NetBeans, используя встроенный архетип Maven &#8220;Swing Application Framework (JSR 296) Archetype 1.0&#8221; (Maven -> Maven Project).</p>\r\n\r\n<p>Для начала необходимо добавить Sping IoC в список зависимостей в файл <code>pom.xml</code>:</p>\r\n\r\n<pre><code>&lt;dependency&gt;\r\n  &lt;groupId&gt;org.springframework&lt;/groupId&gt;\r\n  &lt;artifactId&gt;spring-context&lt;/artifactId&gt;\r\n  &lt;version&gt;3.0.0.RELEASE&lt;/version&gt;\r\n&lt;/dependency&gt;\r\n</code></pre>\r\n\r\n<p>Spring также использует библиотеку cglib для работы с бинами, использующими <a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/new-in-3.html#new-java-configuration">Java-конфигурацию</a> (Java based bean metadata):</p>\r\n\r\n<pre><code>&lt;dependency&gt;\r\n  &lt;groupId&gt;cglib&lt;/groupId&gt;\r\n  &lt;artifactId&gt;cglib-nodep&lt;/artifactId&gt;\r\n  &lt;version&gt;2.2&lt;/version&gt;\r\n&lt;/dependency&gt;\r\n</code></pre>\r\n\r\n<p>Теперь необходимо проинициализиовать <code>ApplicationContext</code> в функции <code>main()</code>. Как уже сказано выше, я задаю бины прямо на Java, не используя XML, поэтому в конструктор контекста я передаю класс, содержащий определения бинов:</p>\r\n\r\n<pre><code>public static void main(String[] args)\r\n{\r\n    AnnotationConfigApplicationContext ctx =\r\n            new AnnotationConfigApplicationContext(ApplicationConfig.class)\r\n\r\n    launch(DesktopApplication1.class, args);\r\n}\r\n</code></pre>\r\n\r\n<p>Класс ApplicationConfig может выглядеть так (см. документацию <a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/new-in-3.html#new-java-configuration">Java based bean metadata</a>):</p>\r\n\r\n<pre><code>@Configuration\r\npublic class ApplicationConfig\r\n{\r\n    @Bean\r\n    public Test getTest\r\n    {\r\n        return new Test();\r\n    }\r\n}\r\n</code></pre>\r\n\r\n<p>Теперь осталось добавить поддержку IoC для юнит-тестов. Первым делом необходимо обновить версию JUnit:</p>\r\n\r\n<pre><code>&lt;dependency&gt;\r\n  &lt;groupId&gt;junit&lt;/groupId&gt;\r\n  &lt;artifactId&gt;junit&lt;/artifactId&gt;\r\n  &lt;version&gt;4.7&lt;/version&gt;\r\n  &lt;scope&gt;test&lt;/scope&gt;\r\n&lt;/dependency&gt;\r\n</code></pre>\r\n\r\n<p>И добавить специальную <a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html">Spring Test</a> библиотеку:</p>\r\n\r\n<pre><code>&lt;dependency&gt;\r\n  &lt;groupId&gt;org.springframework&lt;/groupId&gt;\r\n  &lt;artifactId&gt;spring-test&lt;/artifactId&gt;\r\n  &lt;version&gt;3.0.0.RELEASE&lt;/version&gt;\r\n  &lt;scope&gt;test&lt;/scope&gt;\r\n&lt;/dependency&gt;\r\n</code></pre>\r\n\r\n<p>Сразу же неплохо бы добавить log4j и slf4j, как это <a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/overview.html#d0e738">рекомендуют в документации к Spring</a>:</p>\r\n\r\n<pre><code>&lt;dependency&gt;\r\n  &lt;groupId&gt;log4j&lt;/groupId&gt;\r\n  &lt;artifactId&gt;log4j&lt;/artifactId&gt;\r\n  &lt;version&gt;1.2.14&lt;/version&gt;\r\n&lt;/dependency&gt;\r\n&lt;dependency&gt;\r\n  &lt;groupId&gt;org.slf4j&lt;/groupId&gt;\r\n  &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;\r\n  &lt;version&gt;1.5.10&lt;/version&gt;\r\n&lt;/dependency&gt;\r\n&lt;dependency&gt;\r\n  &lt;groupId&gt;org.slf4j&lt;/groupId&gt;\r\n  &lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;\r\n  &lt;version&gt;1.5.8&lt;/version&gt;\r\n&lt;/dependency&gt;\r\n</code></pre>\r\n\r\n<p>Юнит-тесты, использующие Spring IoC, необходимо оформлять следующим образом:</p>\r\n\r\n<pre><code>@RunWith(SpringJUnit4ClassRunner.class)\r\n@ContextConfiguration(loader = AnnotationConfigContextLoader.class,\r\n        value = &amp;#8220;com.sample.ApplicationConfig&amp;#8221;)\r\npublic class IocTest {\r\n    @Autowired\r\n    Test test;\r\n\r\n    @Test\r\n    public void testIoc()\r\n    {\r\n        Assert.assertNotNull(test);\r\n    }\r\n}\r\n</code></pre>\r\n\r\n<p>Осталось только привести код специального лоадера <code>AnnotationConfigContextLoader</code>, которого пока нет в стандартной поставке (его код мне подсказали на <a href="http://stackoverflow.com/questions/2228820/add-spring-3-0-0-java-based-ioc-to-junit-4-7-tests">stackoverflow.com</a>):</p>\r\n\r\n<pre><code>public class AnnotationConfigContextLoader implements ContextLoader\r\n{\r\n    public ApplicationContext loadContext(String&amp;#8230; locations) throws Exception\r\n    {\r\n        Class&lt;?&gt;[] configClasses = new Class&lt;?&gt;[locations.length];\r\n        for (int i = 0; i &lt; locations.length; i++) {\r\n            configClasses[i] = Class.forName(locations[i]);\r\n        }\r\n        return new AnnotationConfigApplicationContext(configClasses);\r\n    }\r\n\r\n    public String[] processLocations(Class&lt;?&gt; c, String&amp;#8230; locations)\r\n    {\r\n        return locations;\r\n    }\r\n}\r\n</code></pre>\r\n', 0, '2010-02-10 17:26:58', '2010-02-10 17:27:49', 1),
+(19, 'Ссылки за неделю 4', 'Ssylki_za_nedelyu_4', '- [Тесты на знание JavaScript](http://codeblogz.ru/2010/02/javascript.html) - ценны не столько тесты, сколько объяснения к ним.\r\n- [Кладезь мудрости о функциях в JavaScript](https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions) - function declaration vs function expression vs function statement.\r\n- [Безопасные умолчания](http://softwaremaniacs.org/blog/2010/02/09/django-auth-safe-defaults/) - о мелочах, из которых потом складывается хороший API.\r\n- [Субъективый взгляд на Objective C](http://szotin.livejournal.com/19296.html) - несколько абзацев в целом об Objective C.', '<ul>\n<li><a href="http://codeblogz.ru/2010/02/javascript.html">Тесты на знание JavaScript</a> - ценны не столько тесты, сколько объяснения к ним.</li>\n<li><a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions">Кладезь мудрости о функциях в JavaScript</a> - function declaration vs function expression vs function statement.</li>\n<li><a href="http://softwaremaniacs.org/blog/2010/02/09/django-auth-safe-defaults/">Безопасные умолчания</a> - о мелочах, из которых потом складывается хороший API.</li>\n<li><a href="http://szotin.livejournal.com/19296.html">Субъективый взгляд на Objective C</a> - несколько абзацев в целом об Objective C.</li>\n</ul>\n', 0, '2010-02-14 14:46:11', '2010-02-14 14:46:11', 1),
+(20, 'Symfony 2.0 PR vs Zend Framework', 'Symfony_2.0_PR_vs_Zend_Framework', 'Вчера вышел Symfony 2.0 Preview Release, который показался мне достаточно интересным, чтобы сделать небольшой обзор. Сравнивать я буду с Zend Framework.\r\n\r\n- [Бандлы и микроядро](http://symfony-reloaded.org/architecture). Отнюдь не новая идея о маленьком ядре и подключаемых плагинах с викодином и медсёстрами: все есть бандлы, все прозрачно конфигурируется, XML-конфиги с XSD-схемами, встроенный DI-контейнер, Event Dispatcher, ядро только запускает нужные бандлы. Мне очень понравилось. Почти то же самое можно получить, используя [Zend_Application_Bootstrap](http://weierophinney.net/matthew/archives/230-Quick-Start-to-Zend_Application_Bootstrap.html), правда я такого нигде не видел, но обязательно попробую.\r\n- [Отладочная консоль и логи](http://symfony-reloaded.org/tools). В дополнении к уже стандартной отладочной консоли идет **очень удобный лог**. Отладочная консоль в [ZF есть](http://code.google.com/p/zfdebug/), а вот аналогичный лог получить почти нереально.\r\n- Doctrine 2.0 и Zend Framework. Doctrine используется как стандартный ORM, а из ZF ипользуется `Zend_Cache` и `Zend_Log`. Как показатель, что ребята без комплексов абсолютно.\r\n- [Скорость](http://symfony-reloaded.org/fast). Говорят о 3-кратном приросте производительности, но если учесть, что там сравнивается диспетчер и роутеры, а документации по ним очень мало, то и сравнивать нечего.\r\n\r\nИ все это сразу работает из коробки. А благодаря бандлам еще и очень гибко. Мечта. Жду такое же для Zend Framework :)', '<p>Вчера вышел Symfony 2.0 Preview Release, который показался мне достаточно интересным, чтобы сделать небольшой обзор. Сравнивать я буду с Zend Framework.</p>\n\n<ul>\n<li><a href="http://symfony-reloaded.org/architecture">Бандлы и микроядро</a>. Отнюдь не новая идея о маленьком ядре и подключаемых плагинах с викодином и медсёстрами: все есть бандлы, все прозрачно конфигурируется, XML-конфиги с XSD-схемами, встроенный DI-контейнер, Event Dispatcher, ядро только запускает нужные бандлы. Мне очень понравилось. Почти то же самое можно получить, используя <a href="http://weierophinney.net/matthew/archives/230-Quick-Start-to-Zend_Application_Bootstrap.html">Zend_Application_Bootstrap</a>, правда я такого нигде не видел, но обязательно попробую.</li>\n<li><a href="http://symfony-reloaded.org/tools">Отладочная консоль и логи</a>. В дополнении к уже стандартной отладочной консоли идет <strong>очень удобный лог</strong>. Отладочная консоль в <a href="http://code.google.com/p/zfdebug/">ZF есть</a>, а вот аналогичный лог получить почти нереально.</li>\n<li>Doctrine 2.0 и Zend Framework. Doctrine используется как стандартный ORM, а из ZF ипользуется <code>Zend_Cache</code> и <code>Zend_Log</code>. Как показатель, что ребята без комплексов абсолютно.</li>\n<li><a href="http://symfony-reloaded.org/fast">Скорость</a>. Говорят о 3-кратном приросте производительности, но если учесть, что там сравнивается диспетчер и роутеры, а документации по ним очень мало, то и сравнивать нечего.</li>\n</ul>\n\n<p>И все это сразу работает из коробки. А благодаря бандлам еще и очень гибко. Мечта. Жду такое же для Zend Framework :)</p>\n', 0, '2010-02-18 07:28:27', '2010-02-18 07:28:27', 1);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `blog__entry_alias`
+--
+
+CREATE TABLE IF NOT EXISTS `blog__entry_alias` (
+  `tid` varchar(128) collate utf8_unicode_ci NOT NULL default '',
+  `entry_id` bigint(20) default NULL,
+  PRIMARY KEY  (`tid`),
+  KEY `entry_id_idx` (`entry_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+--
+-- Dumping data for table `blog__entry_alias`
+--
+
+INSERT INTO `blog__entry_alias` (`tid`, `entry_id`) VALUES
+('Mnogoyazychnyj_router_dlya_Zend_Framework', 7),
+('Gidratory_v_Doctrine_1.1', 11),
+('Gidratory_v_Doctrine_1.2', 11),
+('Konferenciya_po_Zend_Framework_v_Rossii', 12),
+('Kalkulyator_seti_na_JavaScript', 13),
+('Ssylki_za_nedelyu_2', 14),
+('Rasshirenie_Zend_Translate_dlya_podderzhki_perevodov_po_umolchaniyu', 15),
+('Ssylki_za_nedelyu_3', 16),
+('V_konce_marta_v_Sankt-Peterburge_projdet_konferenciya_po_Zend_Framework', 17),
+('Dobavit_Spring_IoC_v_prilozhenie_Swing_Application_Framework', 18),
+('Ssylki_za_nedelyu_4', 19),
+('Symfony_2.0_PR_vs_Zend_Framework', 20);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `blog__entry_tag`
+--
+
+CREATE TABLE IF NOT EXISTS `blog__entry_tag` (
+  `entry_id` bigint(20) NOT NULL default '0',
+  `tag_id` bigint(20) NOT NULL default '0',
+  PRIMARY KEY  (`entry_id`,`tag_id`),
+  KEY `blog__entry_tag_tag_id_blog__tag_id` (`tag_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+--
+-- Dumping data for table `blog__entry_tag`
+--
+
+INSERT INTO `blog__entry_tag` (`entry_id`, `tag_id`) VALUES
+(1, 1),
+(1, 4),
+(1, 9),
+(1, 10),
+(2, 1),
+(2, 6),
+(2, 8),
+(3, 4),
+(3, 7),
+(4, 1),
+(4, 4),
+(4, 5),
+(4, 6),
+(5, 1),
+(5, 3),
+(6, 1),
+(6, 2),
+(7, 1),
+(7, 4),
+(7, 11),
+(7, 12),
+(8, 4),
+(8, 6),
+(8, 8),
+(8, 13),
+(9, 1),
+(9, 14),
+(9, 15),
+(10, 16),
+(11, 1),
+(11, 6),
+(11, 17),
+(12, 4),
+(12, 18),
+(13, 19),
+(13, 20),
+(13, 21),
+(14, 16),
+(15, 1),
+(15, 4),
+(15, 22),
+(16, 16),
+(17, 4),
+(17, 18),
+(18, 23),
+(18, 24),
+(18, 25),
+(18, 26),
+(19, 16),
+(20, 1),
+(20, 4),
+(20, 6),
+(20, 29);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `blog__tag`
+--
+
+CREATE TABLE IF NOT EXISTS `blog__tag` (
+  `id` bigint(20) NOT NULL auto_increment,
+  `tag` varchar(128) collate utf8_unicode_ci default NULL,
+  `value` varchar(128) collate utf8_unicode_ci default NULL,
+  `root_id` bigint(20) default NULL,
+  `lft` int(11) default NULL,
+  `rgt` int(11) default NULL,
+  `level` smallint(6) default NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=30 ;
+
+--
+-- Dumping data for table `blog__tag`
+--
+
+INSERT INTO `blog__tag` (`id`, `tag`, `value`, `root_id`, `lft`, `rgt`, `level`) VALUES
+(1, 'PHP', 'php', 1, 1, 34, 0),
+(2, 'PCRE', 'pcre', 1, 2, 3, 1),
+(3, 'memory', 'memory', 1, 4, 5, 1),
+(4, 'Zend Framework', 'zend framework', 1, 6, 11, 1),
+(5, 'Lucene', 'lucene', 1, 24, 25, 1),
+(6, 'Doctrine', 'doctrine', 1, 12, 17, 1),
+(7, 'Highlight.js', 'highlight.js', 28, 4, 5, 1),
+(8, 'Doctrine_Query', 'doctrine_query', 1, 13, 14, 2),
+(9, 'Zend_Registry', 'zend_registry', 1, 7, 8, 2),
+(10, 'Service Locator', 'service locator', 1, 18, 19, 1),
+(11, 'Router', 'router', 1, 22, 23, 1),
+(12, 'Multilingual', 'multilingual', 1, 20, 21, 1),
+(13, 'Pagination', 'pagination', 1, 30, 31, 1),
+(14, 'Phing', 'phing', 1, 26, 27, 1),
+(15, 'CSS', 'css', 28, 10, 11, 1),
+(16, 'Links', 'links', 16, 1, 2, 0),
+(17, 'Doctrine_Hydrator', 'doctrine_hydrator', 1, 15, 16, 2),
+(18, 'ZFConf', 'zfconf', 1, 9, 10, 2),
+(19, 'JavaScript', 'javascript', 28, 8, 9, 1),
+(20, 'Network', 'network', 28, 6, 7, 1),
+(21, 'IP', 'ip', 28, 2, 3, 1),
+(22, 'Translate', 'translate', 1, 28, 29, 1),
+(23, 'Java', 'java', 23, 1, 6, 0),
+(24, 'Spring', 'spring', 23, 2, 3, 1),
+(25, 'JUnit', 'junit', 23, 4, 5, 1),
+(26, 'NetBeans', 'netbeans', 27, 2, 3, 1),
+(27, 'Misc', 'misc', 27, 1, 4, 0),
+(28, 'Web', 'web', 28, 1, 12, 0),
+(29, 'Symfony', 'symfony', 1, 32, 33, 1);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `doctrine__record__abstract`
+--
+
+CREATE TABLE IF NOT EXISTS `doctrine__record__abstract` (
+  `id` bigint(20) NOT NULL auto_increment,
+  PRIMARY KEY  (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
+
+--
+-- Dumping data for table `doctrine__record__abstract`
+--
+
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `identity__user`
+--
+
+CREATE TABLE IF NOT EXISTS `identity__user` (
+  `id` bigint(20) NOT NULL auto_increment,
+  `name` varchar(64) collate utf8_unicode_ci default NULL,
+  `login` varchar(64) collate utf8_unicode_ci default NULL,
+  `password` char(32) collate utf8_unicode_ci default NULL,
+  `email` varchar(64) collate utf8_unicode_ci default NULL,
+  `code` char(32) collate utf8_unicode_ci default NULL,
+  `roleid` bigint(20) default NULL,
+  `createdat` datetime NOT NULL,
+  `updatedat` datetime NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=2 ;
+
+--
+-- Dumping data for table `identity__user`
+--
+
+INSERT INTO `identity__user` (`id`, `name`, `login`, `password`, `email`, `code`, `roleid`, `createdat`, `updatedat`) VALUES
+(1, 'Admin', 'admin', 'e94874ec588536c8f001fa3141920f96', 'admin@localhost', NULL, 3, '2009-10-14 04:49:26', '2009-10-14 04:49:26');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `session`
+--
+
+CREATE TABLE IF NOT EXISTS `session` (
+  `id` varchar(32) collate utf8_unicode_ci NOT NULL default '',
+  `modified` bigint(20) default NULL,
+  `lifetime` bigint(20) default NULL,
+  `data` text collate utf8_unicode_ci,
+  PRIMARY KEY  (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+--
+-- Dumping data for table `session`
+--
+
+INSERT INTO `session` (`id`, `modified`, `lifetime`, `data`) VALUES
+('bb2b6c45bc6a866d0224c3e29ba9da6a', 1268204373, 1209600, 'Zend_Auth|a:1:{s:7:"storage";O:8:"stdClass":8:{s:2:"id";s:1:"1";s:4:"name";s:5:"Admin";s:5:"login";s:5:"admin";s:5:"email";s:15:"admin@localhost";s:4:"code";N;s:6:"roleid";s:1:"3";s:9:"createdat";s:19:"2009-10-14 04:49:26";s:9:"updatedat";s:19:"2009-10-14 04:49:26";}}'),
+('f8b9d001124992083b69feb20dc73daf', 1269317709, 1440, ''),
+('559d733272544c60143212ed69f6184c', 1269532605, 1440, ''),
+('68686bd4cdcbc63a291354d32c8a7327', 1269532602, 1440, ''),
+('f0e8b2dac216eeace6927612ac45cbfb', 1269529274, 1440, ''),
+('f0ccc1c30200445cb3ab3b7557236390', 1269529253, 1440, ''),
+('fddf652b9bf5820aea744fa2f6544c28', 1269529248, 1440, ''),
+('5d4aebc2a7655446a7da9d57bdc18a2d', 1269528989, 1440, ''),
+('95e886b53386cf4717605ba18a09ef94', 1269527978, 1440, ''),
+('0038d6d5521a906b130a4f239d946c2a', 1269525589, 1440, ''),
+('73fcddb1d5e2565a5265355bd794230f', 1269525120, 1440, ''),
+('bee1a9c7c834070c16da42ac461024f7', 1269524708, 1440, ''),
+('5d2ab24e3af755c7dd1ea564bafff328', 1269523846, 1440, ''),
+('6ed7b97fd8d97d46b5f68f9c151e6c65', 1269523781, 1440, ''),
+('2e3cdb403ee1f8705866f2c57200edbb', 1269523422, 1440, ''),
+('d13c72882a567a4929061bbe01f45db6', 1269523418, 1440, ''),
+('69f1ab86ac2c6bfd77e6ccadaf5315cd', 1269521942, 1440, ''),
+('5bdf6c9d1e286af130232f1a375c70e0', 1269520259, 1440, ''),
+('529e9ca23100066ad5b6fc3eb277adf7', 1269520012, 1440, ''),
+('b9f8c249b421cbc0d54f60537a4f71e8', 1269519749, 1440, ''),
+('c8015fbaf99f697b76b8737469efb467', 1269518706, 1440, ''),
+('875912919c144d7bfcafc6d037893f84', 1269518698, 1440, ''),
+('3e883ab6fe7d93fffc25fe29e0501e95', 1269518695, 1440, ''),
+('df7b4c581f8392ba9fc598cf386499cc', 1269514409, 1440, ''),
+('17cd3662ec7675968d90f94a274f3752', 1269514406, 1440, ''),
+('c1add7bbe11e76c868d76644ef697cf4', 1269511323, 1440, ''),
+('ef4c87082b781cad4ecf40c0c7711c87', 1269511310, 1440, ''),
+('45c7b81cce5dbe5eebdf8ca3fc218c7e', 1269509651, 1440, ''),
+('c5978de493e65fed7189e6ce20e018bc', 1269509648, 1440, ''),
+('f65c009c5e41e124de333c149697d207', 1269509548, 1440, ''),
+('ca7f7bd4bd4374b3b7c7d0ae823c9bb0', 1269509399, 1440, ''),
+('48523d66c5ed929a540965328c05d27f', 1269508286, 1440, ''),
+('ff4eda4a985a3c26b79670ffefe44a48', 1269505884, 1440, ''),
+('736183c42fb4696113180b57832e8491', 1269504000, 1440, ''),
+('b0b568afcad4ed9aefef4850d88743a8', 1269503869, 1440, ''),
+('c45a99dd3b108a0e68be38ff78e83358', 1269503592, 1440, ''),
+('1dc29b939807564e953bd158db743593', 1269503588, 1440, ''),
+('37a2313452a3564bb33d97318ca80526', 1269502226, 1440, ''),
+('71e5ccab9c13dc84798bd4d1e900d303', 1269502086, 1440, ''),
+('e63630bc9d99a000ab4669fdc2d65c70', 1269500843, 1440, ''),
+('da631e79d3690c3f63e7d0eb4952d8ab', 1269500659, 1440, ''),
+('4043a8d5a070648f4f588700eaa3560f', 1269500656, 1440, ''),
+('3102075514c4a89f82c7bf9e762fa6ff', 1269497528, 1440, ''),
+('2bc9c210432b5fc9a02e2da911adcc0b', 1269496889, 1440, ''),
+('42765ac8e6e0457fc782e9e7b222e955', 1269496886, 1440, ''),
+('a23e80d7127d344973a3748ccecee35d', 1269496101, 1440, ''),
+('bdfdb9dea5d85bf10ee93df867719098', 1269496060, 1440, ''),
+('2abb418c6a0065b30b2922ea5d35aacb', 1269492705, 1440, ''),
+('4770db616cdb36dbc1cefd7b1b6c6db0', 1269492701, 1440, ''),
+('0622b6b27455c2236799b4785544a835', 1269491150, 1440, ''),
+('5f246e320345c4103d8a36f304a23d17', 1269490674, 1440, ''),
+('aa72c7cffbcd7ed623e6a88c256231eb', 1269490518, 1440, ''),
+('2488b5bdfcaa26f4e841979b90e3aa08', 1269488243, 1440, ''),
+('51260cadef27af260e028ed42e758461', 1269488240, 1440, ''),
+('00d2483eecd7735bb6e07689f57611af', 1269486145, 1440, ''),
+('ff3b05536d17aa0453d91a8cbf9b6a5c', 1269486142, 1440, ''),
+('5ebfc288d66af6f567057cfeab574a00', 1269484002, 1440, ''),
+('528d6a56c8de967ad222930ded5c5303', 1269483999, 1440, ''),
+('ece9ddad63035f82386d4d859c5eebf8', 1269481808, 1440, ''),
+('73615ca402f8afeaa6c60f1a19f19fbe', 1269479749, 1440, ''),
+('86c9944b349bca391fa2a881c423e237', 1269479746, 1440, ''),
+('5f71d8ac1b27995ab9712e030c7e34d4', 1269475567, 1440, ''),
+('bf46a1c677820c70fcbda394b0421668', 1269475564, 1440, ''),
+('b53b533d7bb33549556073de52a77e32', 1269471836, 1440, ''),
+('ecbb19cd886f8b1fa02b81dab34e2895', 1269471259, 1440, ''),
+('73ec0d7725c0a2c7d06dcea74b9b348b', 1269471256, 1440, ''),
+('b4b69d922a5e2121c084b50b80db0200', 1269469623, 1440, ''),
+('3eff37f2172825a96ce016586e1cc23f', 1269467018, 1440, ''),
+('0e788ef9e3f679519d6a252969e91840', 1269467016, 1440, ''),
+('bc36408b5b37ccba3b292ff9dfe1bad5', 1269464987, 1440, ''),
+('5d1301d913be4070f25e6e1095f595ac', 1269464985, 1440, ''),
+('8aa9bc9bf80b674416fab3e8443b1280', 1269462624, 1440, ''),
+('f3a5eb19318fb95c24ed0185cac12bb8', 1269462622, 1440, ''),
+('e7f39c4aa256fb4aa57233c28d82d505', 1269462531, 1440, ''),
+('ffb1c2cb94075fbb3acf4c712e9b2738', 1269458051, 1440, ''),
+('7b45ce0d8326275fd641673478bbbe4b', 1269454744, 1440, ''),
+('8698bfdeea5611af89ca988ee2d6b3c6', 1269451958, 1440, ''),
+('7ef60dc07d5588b5c3517963fa98191f', 1269451954, 1440, ''),
+('5b939bb19e981ad6a583c9c2ec48386e', 1269449895, 1440, ''),
+('5ab9bbf37bf52829ceb1f20892f6bdd4', 1269447389, 1440, ''),
+('ee838aed5b388238cfd045218b53bd09', 1269447385, 1440, ''),
+('b16818812676bba01c0a6add32dddb6f', 1269447438, 1440, ''),
+('01efc542330f334d985a8852f215a4ab', 1269519284, 1440, ''),
+('d5ab17fa960e91064dbb71f2517deaf4', 1269446217, 1440, ''),
+('5d5a23006e1c9d2b1f29779f750aba48', 1269445898, 1440, ''),
+('a57f137a73fde754b0ea2708e91594d3', 1269444022, 1440, ''),
+('984d640c5d8e4e2601156b05e4fd5361', 1269447343, 1440, ''),
+('860050d283f209958be8347161e057ff', 1269442868, 1440, ''),
+('145d811d7fafeb8c27e93a09e9a05bbb', 1269442865, 1440, ''),
+('2ccf8bdc96309b0435901f6fafba9243', 1269441514, 1440, ''),
+('b8ba5e59ea155f87c1ce8a41e5fa8e58', 1269440027, 1440, ''),
+('723e31327bcf668f92aa901cc5d5805f', 1269439430, 1440, ''),
+('246e030d1d06ad2727ca90c5a06f4de4', 1269439164, 1440, ''),
+('e81f68d7467a903208b4ee51f9a9f5cb', 1269438478, 1440, ''),
+('e23e70d416edf9a66bb84455a91e247d', 1269438464, 1440, ''),
+('ea205dcead0d51339d33b1e43d1d25e2', 1269434894, 1440, ''),
+('7ca62fb66671db73305253154a7aabf6', 1269432445, 1440, ''),
+('4525005d774588e8dce1e9c9bfc42f39', 1269432441, 1440, ''),
+('75f5404b1384c488b687484fe592ad3e', 1269431303, 1440, ''),
+('9f06feb9dda0ccdd5f29f91286f9ef4f', 1269429773, 1440, ''),
+('8de9bd3aed0678ae823f1af577842d9c', 1269429771, 1440, ''),
+('43565a5e60bc546e7cf3a3710125323b', 1269428782, 1440, ''),
+('5cd91f8ae490df80029bf7ad7de466ef', 1269428424, 1440, ''),
+('0a4aa5f54e731860ac439afe4f6f63f5', 1269427773, 1440, ''),
+('8cd7dcf97528b1615d257541e56eb712', 1269426797, 1440, ''),
+('688330e70345c008542c00cc5108874c', 1269426692, 1440, ''),
+('0041378f4185cc63169188b1604ccf2b', 1269426327, 1440, ''),
+('0c856a1532df8169f80a9c2595be7a12', 1269425061, 1440, ''),
+('dcece27a1eef125b50872ee76778c995', 1269424491, 1440, ''),
+('43db6968fd65956b2da831a1146441f5', 1269424488, 1440, ''),
+('6097b774d4c51c73b782d3d92202affa', 1269429532, 1440, ''),
+('c0a16ba3748104e256236c58f587d0ab', 1269419343, 1440, ''),
+('4ddff434c95213ea8ecb0fb682b7d5ea', 1269419340, 1440, ''),
+('868705b3f64b71e21a05d055d14705ef', 1269416703, 1440, ''),
+('652dda4fbe95c3454fc7f6d50d201900', 1269416696, 1440, ''),
+('cf095252c659b71a5ff24b153163b7c2', 1269416462, 1440, ''),
+('57d194051f306d0d368f5814474c22cf', 1269416041, 1440, ''),
+('2c72ce9ddb2008cc0c41c34fad13f36a', 1269415406, 1440, ''),
+('ca9b71ec61c0f796049321badc47bbbe', 1269415683, 1440, ''),
+('5f51c101984d523960597c42316757e9', 1269412462, 1440, ''),
+('124eeb9157c0ab1e5e8afef9870c444c', 1269412423, 1440, ''),
+('3307effd821df172168288b19c820ca6', 1269412384, 1440, ''),
+('cf137e9f196072187893036b5014e21b', 1269412692, 1440, ''),
+('8374c3195b62197ff5f1e47899d7df6a', 1269412639, 1440, ''),
+('399d43fec48bb0eb238ec93dffc25d02', 1269413198, 1440, ''),
+('5351988e024a41f31c2bd0d77f318a94', 1269411477, 1440, ''),
+('103bea78ff4e59a3c2910be05b0b8142', 1269411183, 1440, ''),
+('0846c9c58f89bc76e995f5d2c1c41621', 1269411183, 1440, ''),
+('540a77e891b383e1afc3c785e9a77bb9', 1269411182, 1440, ''),
+('984e953064653cbc8b84e15ed3eb418a', 1269408247, 1440, ''),
+('8fa0e7c6666459bbdaf898f6edcd5138', 1269408244, 1440, ''),
+('64f422f7798ddc70d670db64bab1e08c', 1269407135, 1440, ''),
+('364e02fcc201cab89f08399b4e65384b', 1269405237, 1440, ''),
+('39958a907cb24d4fc2288de34a752b1d', 1269404061, 1440, ''),
+('be19acca9ccfe95316c0fcee0bcbd2c8', 1269404058, 1440, ''),
+('d4ee5241855889b4cfa9f5fd76d1deac', 1269403292, 1440, ''),
+('9a056832b504ae295a8b5babe9a3c0d0', 1269402159, 1440, ''),
+('6aeb5b4e6e884bc3ac3526f25ffc8eda', 1269402054, 1440, ''),
+('c0de8044a5ffea1a226a1daf06fd85a3', 1269402051, 1440, ''),
+('508a4426f992a203e1f701df2fa53f5a', 1269399465, 1440, ''),
+('cb40c41c7f8d0f02c8c9d25e61fe4ed8', 1269399462, 1440, ''),
+('6ba9d7363fa7dba2e44698eb00da1cfa', 1269396079, 1440, ''),
+('dd53500acf2dde568ffab1e81330fb7b', 1269395534, 1440, ''),
+('01afdcc80b2327a4464f556a7f194ae4', 1269395414, 1440, ''),
+('f57b5e93fb69f4d7a6d31c5cf9657b80', 1269395411, 1440, ''),
+('4ee96858898c3d627d30a37d73d09c73', 1269391212, 1440, ''),
+('73ae5cee215baeb0e6a2052ac3fa1a0d', 1269391209, 1440, ''),
+('3231bfa28b5b8925637141e6160df924', 1269386743, 1440, ''),
+('d47063d1a010338ba3f5c1513d2c7edc', 1269386738, 1440, ''),
+('002e91f6645eba2b33649bb4a1f4f0d2', 1269386167, 1440, ''),
+('bba7ed1d48deca73bc1be0ca69b3d204', 1269382728, 1440, ''),
+('2ca7deac3c5f0f16a5f6f29ad00fcf9a', 1269382725, 1440, ''),
+('95837cb15a0ea75363d7816347165cb9', 1269376813, 1440, ''),
+('d69713230683ffc56a19b3d8ad51c1f4', 1269376811, 1440, ''),
+('d8df30ecc8f43b5ed3f947a6de086e3e', 1269374000, 1440, ''),
+('b14fcbb8c23201cb83ef23aee6e29115', 1269373997, 1440, ''),
+('5f6a27ae7fb1a09def55cc2f464f54c4', 1269373535, 1440, ''),
+('80ba2c7bfd1d8913b54c07588e543489', 1269372969, 1440, ''),
+('4552587cfc64a59b1d9c5abab0b20ad1', 1269370684, 1440, ''),
+('5dfa2bec294708ed27efb128fe590585', 1269369560, 1440, ''),
+('bd90b0de99d337555b170bb0a89fb778', 1269369557, 1440, ''),
+('300627d7ab5f34ace1d388715f165f77', 1269368522, 1440, ''),
+('3095aa6ca722337e907a64d85222d035', 1269365052, 1440, ''),
+('740a984ec7980dc0242e4e19f5a8b62d', 1269365048, 1440, ''),
+('1505f404295d948d3180e0dc1bf20c99', 1269364675, 1440, ''),
+('9f99e9c8bbcd49ce8546855bcec6c8bc', 1269363617, 1440, ''),
+('0a0146e7d0489e720158619fcb255b3a', 1269360430, 1440, ''),
+('0da6437247c07d287edb804630741f45', 1269360427, 1440, ''),
+('9ba7df3f019169adbdca71c3d3da51b5', 1269359775, 1440, ''),
+('d39981e3ffab0df4e6f09ce3a2158cb2', 1269359398, 1440, ''),
+('fe5217ec2741dc01de49cdbdb272ac1f', 1269358471, 1440, ''),
+('d3d4089a62b848ec7195851aeaa56f52', 1269356225, 1440, ''),
+('7221104034808e1fc42cc2b900616d6b', 1269355044, 1440, ''),
+('577608780bfa332740af90b6892196b0', 1269351450, 1440, ''),
+('c53296eef5ca31d81b3b1f2af1e7c5e3', 1269351447, 1440, ''),
+('ff2e80133d5a20ff7d2960ea2374deab', 1269348386, 1440, ''),
+('cc0e94b42eb0ef5f6f3964e696acaa73', 1269347600, 1440, ''),
+('fa4e77f34904fd48d810a2d192904d0d', 1269347070, 1440, ''),
+('9a75cef1ea1c983da70f4d0e5ef526cf', 1269347067, 1440, ''),
+('d85699670c16546b97df6d4971d860f8', 1269344773, 1440, ''),
+('52f812f8f16032e8afe8c828326b9a4c', 1269342798, 1440, ''),
+('ad34bc2637b1013b10d80e36c6b34d13', 1269342796, 1440, ''),
+('bb3684efe75b94e689afde9202c1f73d', 1269341505, 1440, ''),
+('5567dd50d4ae2462132a4d9a03485de6', 1269360734, 1440, ''),
+('0bdcfdad58bd30e1a29987540d25f109', 1269339212, 1440, ''),
+('f69cb102daa75f35720e5de2176d266f', 1269337448, 1440, ''),
+('fb71813652bd76f41f720bd3c15a7050', 1269337445, 1440, ''),
+('fd946ec0fd7d55e2db1f0da030905290', 1269335140, 1440, ''),
+('b6390c6f2d237bb92be90518ccfbdab4', 1269334008, 1440, ''),
+('c74650aa9009c5963d117dde01551e25', 1269334005, 1440, ''),
+('4c2bb245c7cb137d5214180c3d6fe5e9', 1269329946, 1440, ''),
+('70dcef93f919223092b11319723bf9ed', 1269329686, 1440, ''),
+('f3a8c06b3f17cd66f2bca7e582610933', 1269329684, 1440, ''),
+('d5874a639905f83e00e7385c691623df', 1269532408, 1209600, 'Zend_Auth|a:1:{s:7:"storage";O:8:"stdClass":8:{s:2:"id";s:1:"1";s:4:"name";s:5:"Admin";s:5:"login";s:5:"admin";s:5:"email";s:15:"admin@localhost";s:4:"code";N;s:6:"roleid";s:1:"3";s:9:"createdat";s:19:"2009-10-14 04:49:26";s:9:"updatedat";s:19:"2009-10-14 04:49:26";}}'),
+('efd672ba3759ce0d0808e95f8c1cd6b0', 1269329554, 1440, ''),
+('3568ba086611987f9a0d0261224449ae', 1269328409, 1440, ''),
+('1367d02d7d1a0fc0820698ceedcd10e9', 1269324730, 1440, ''),
+('ede45fe68f2fa080ae9d640a5b86ddde', 1269324728, 1440, ''),
+('383ccecdf06b24c56165ffb7f33cb447', 1269321902, 1440, ''),
+('687e4b5f45ad74dd3fd90b29f2a319bd', 1269321397, 1440, ''),
+('0bb629081d62763ac0b2e051000d5592', 1269321394, 1440, ''),
+('ac85480308df0c995844814990a22c57', 1269320808, 1440, ''),
+('0d16a657492a160980feea365dfe132a', 1269317711, 1440, '');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `system__report`
+--
+
+CREATE TABLE IF NOT EXISTS `system__report` (
+  `id` bigint(20) NOT NULL auto_increment,
+  `text` text collate utf8_unicode_ci,
+  `comment` text collate utf8_unicode_ci,
+  `url` varchar(128) collate utf8_unicode_ci default NULL,
+  `createdat` datetime NOT NULL,
+  `updatedat` datetime NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=6 ;
+
+--
+-- Dumping data for table `system__report`
+--
+
+INSERT INTO `system__report` (`id`, `text`, `comment`, `url`, `createdat`, `updatedat`) VALUES
+(1, 'Использование ZendY_Doctrine_Paginator_Adapter <!!!>используется как обычно вместе<!!!> с Zend_Paginator, отмечу лишь, что $hydrationMode задается в', '', 'http://loutontheweb.co.cc/entries/read/Zend_Paginator_adapter_dlya_Doctrine_Query', '2009-12-06 03:09:00', '2009-12-06 03:09:00'),
+(2, 'память из-за этого очень непросто. Так как на созданную строку <!!!>имеется<!!!> 2 ссылки ($string и $string2), то мы должны удалить обе', 'имеются', 'http://loutontheweb.co.cc/', '2009-12-06 06:52:18', '2009-12-06 06:52:18'),
+(3, 'покажу гидратор Pairs, представляющий результат запроса в виде <!!!>масива<!!!> ключ-значение. Этот тип гидратора удобно использовать вместе с', 'массива', 'http://loutontheweb.co.cc/', '2010-01-08 06:45:20', '2010-01-08 06:45:20'),
+(4, 'случае “en”) и текущий язык (lang). Т.к. эти данные могут <!!!>былть<!!!> полезны и в других местах, то имеет смысл расшарить их через', 'быть', 'http://loutontheweb.co.cc/entries/read/Mnogoyazychnyj_router_dlya_Zend_Framework', '2010-01-08 06:49:12', '2010-01-08 06:49:12'),
+(5, 'сети на JavaScript Задача. Создать калькулятор сети для <!!!>рассчета<!!!> адреса сети, широковещательного адреса, диапазона допустимых', 'расчета', 'http://loutontheweb.co.cc/entries/read/Kalkulyator_seti_na_JavaScript', '2010-01-30 10:04:06', '2010-01-30 10:04:06');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `system__upload`
+--
+
+CREATE TABLE IF NOT EXISTS `system__upload` (
+  `id` bigint(20) NOT NULL auto_increment,
+  `name` varchar(64) collate utf8_unicode_ci default NULL,
+  `hash` varchar(32) collate utf8_unicode_ci default NULL,
+  `size` bigint(20) default NULL,
+  `mimetype` varchar(32) collate utf8_unicode_ci default NULL,
+  `user_id` bigint(20) default NULL,
+  PRIMARY KEY  (`id`),
+  KEY `user_id_idx` (`user_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
+
+--
+-- Dumping data for table `system__upload`
+--
+
+<?php
+phpinfo();

www/system/library/Doctrine/Export/Oracle.php

 <?php
 /*
- *  $Id: Oracle.php 6896 2009-12-07 23:01:19Z adrive $
+ *  $Id: Oracle.php 6720 2009-11-12 20:18:24Z jwage $
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
  * @link        www.phpdoctrine.org
  * @since       1.0
- * @version     $Revision: 6896 $
+ * @version     $Revision: 6720 $
  */
 class Doctrine_Export_Oracle extends Doctrine_Export
 {
   -- user_tables contains also materialized views
   FOR I IN (SELECT table_name FROM user_tables WHERE table_name NOT IN (SELECT mview_name FROM user_mviews))
   LOOP 
-    EXECUTE IMMEDIATE 'DROP TABLE "'||I.table_name||'" CASCADE CONSTRAINTS';
+    EXECUTE IMMEDIATE 'DROP TABLE \"'||I.table_name||'\" CASCADE CONSTRAINTS';
   END LOOP;
   
   FOR I IN (SELECT SEQUENCE_NAME FROM USER_SEQUENCES)
   LOOP
-    EXECUTE IMMEDIATE 'DROP SEQUENCE "'||I.SEQUENCE_NAME||'"';
+    EXECUTE IMMEDIATE 'DROP SEQUENCE \"'||I.SEQUENCE_OWNER||'\".\"'||I.SEQUENCE_NAME||'\"';
   END LOOP;
 END;
 

www/system/library/Doctrine/Hydrator/ScalarDriver.php

                 }
                 // cache general information like the column name <-> field name mapping
                 $e = explode('__', $key);
-                $columnName = strtolower(array_pop($e)); 
-                $cache[$key]['dqlAlias'] = $this->_tableAliases[strtolower(implode('__', $e))];
+                $columnName = array_pop($e);              
+                $cache[$key]['dqlAlias'] = $this->_tableAliases[implode('__', $e)];
                 $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table'];
                 // check whether it's an aggregate value or a regular field
                 if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName])) {

www/system/library/Doctrine/Import/Schema.php

         $builder->setOptions($this->getOptions());
         
         $array = $this->buildSchema($schema, $format);
+
         if (count($array) == 0) { 
             throw new Doctrine_Import_Exception(
                 sprintf('No ' . $format . ' schema found in ' . implode(", ", $schema))

www/system/library/Doctrine/Migration/Diff.php

                                 'created_indexes'     =>  array(),
                                 'dropped_indexes'     =>  array()),
               $_migration,
-              $_startingModelFiles = array(),
-              $_tmpPath;
+              $_startingModelFiles = array();
 
     protected static $_toPrefix   = 'ToPrfx',
                      $_fromPrefix = 'FromPrfx';
         $this->_from = $from;
         $this->_to = $to;
         $this->_startingModelFiles = Doctrine_Core::getLoadedModelFiles();
-        $this->setTmpPath(sys_get_temp_dir() . DIRECTORY_SEPARATOR . getmypid());
 
         if ($migration instanceof Doctrine_Migration) {
             $this->_migration = $migration;
     }
 
     /**
-     * Set the temporary path to store the generated models for generating diffs
-     *
-     * @param string $tmpPath
-     * @return void
-     */
-    public function setTmpPath($tmpPath)
-    {
-        if ( ! is_dir($tmpPath)) {
-            mkdir($tmpPath, 0777, true);
-        }
-        $this->_tmpPath = $tmpPath;
-    }
-
-    /**
      * Get unique hash id for this migration instance
      *
      * @return string $uniqueId
      */
     protected function _generateModels($prefix, $item)
     {
-        $path = $this->_tmpPath . DIRECTORY_SEPARATOR . strtolower($prefix) . '_doctrine_tmp_dirs';
+        $path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . strtolower($prefix) . '_doctrine_tmp_dirs';
         $options = array(
             'classPrefix' => $prefix,
             'generateBaseClasses' => false
         }
 
         // clean up tmp directories
-        Doctrine_Lib::removeDirectories($this->_tmpPath . DIRECTORY_SEPARATOR . strtolower(self::$_fromPrefix) . '_doctrine_tmp_dirs');
-        Doctrine_Lib::removeDirectories($this->_tmpPath . DIRECTORY_SEPARATOR . strtolower(self::$_toPrefix) . '_doctrine_tmp_dirs');
+        Doctrine_Lib::removeDirectories(sys_get_temp_dir() . DIRECTORY_SEPARATOR . strtolower(self::$_fromPrefix) . '_doctrine_tmp_dirs');
+        Doctrine_Lib::removeDirectories(sys_get_temp_dir() . DIRECTORY_SEPARATOR . strtolower(self::$_toPrefix) . '_doctrine_tmp_dirs');
     }
 }

www/system/library/Doctrine/Query/JoinCondition.php

 <?php
 /*
- *  $Id: JoinCondition.php 6923 2009-12-09 21:07:16Z kriswallsmith $
+ *  $Id: JoinCondition.php 6859 2009-12-05 00:12:51Z kriswallsmith $
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
  * @link        www.phpdoctrine.org
  * @since       1.0
- * @version     $Revision: 6923 $
+ * @version     $Revision: 6859 $
  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
  */
 class Doctrine_Query_JoinCondition extends Doctrine_Query_Condition
 
                     $value = '(' . implode(', ', $value) . ')';
                 }
-            } elseif ( ! $hasRightAggExpression) {
+            } else {
                 // Possible expression found (field1 AND field2)
                 // In relation to ticket #1488
                 $e     = $this->_tokenizer->bracketExplode($value, array(' AND ', ' \&\& '), '(', ')');
                 $value = implode(' AND ', $value);
             }
 
-            if ($hasRightAggExpression) {
-                $rightExpr = $rightMatches[1] . '(' . $value . ')' . $rightMatches[3];
-                $rightExpr = $this->query->parseClause($rightExpr);
-            } else {
-                $rightExpr = $value;
+            switch ($operator) {
+                case '<':
+                case '>':
+                case '=':
+                case '!=':
+                default:
+                    $rightExpr = (($hasRightAggExpression) ? $rightMatches[1] . '(' : '')
+                              . $value
+                              . (($hasRightAggExpression) ? ')' . $rightMatches[3] : '') ;
+
+                    $condition  = $leftExpr . ' ' . $operator . ' ' . $rightExpr;
             }
 
-            $condition  = $leftExpr . ' ' . $operator . ' ' . $rightExpr;
-
             return $condition;
         }
 

www/system/library/Doctrine/Table.php

     {
         return array_values($this->_fieldNames);
     }
-    public function getFieldNamess()
-    {
-        return $this->_fieldNames;
-    }
 
     /**
      * Retrieves the definition of a field.

www/system/library/DoctrineX/Record.php

             $name = substr($method, 3);
             $table = $this->getTable();
 
-            if ($table->hasField($name) || $table->hasRelation($name)) {
+            if ($table->hasField($name) || $table->hasRelation($name)
+                    || array_key_exists($name, $this->_values)) {
                 return call_user_func_array(
                     array($this, $verb),
                     array_merge(array($name), $arguments)

www/system/library/Neno/Service/Doctrine.php

             return $this->create($data, $model);
         }
     }
+
+    public function getMultiOptionsQuery()
+    {
+        return $this->createQuery('service');
+    }
+
+    public function getMultiOptions($query = null)
+    {
+        if (null === $query) {
+            $query = $this->getMultiOptionsQuery();
+        }
+
+        $multiOptions = array();
+        foreach ($query->execute() as $record) {
+            $multiOptions[$record->getIncremented()] = (string) $record;
+        }
+
+        return $multiOptions;
+    }
 }

www/system/library/Neno/Tool.php

         return $_SERVER['DOCUMENT_ROOT'];
     }
 
-    public function langTool()
+    public function defaultLangTool()
     {
-        return Zend_Registry::get('appConfig')->settings->lang;
+        return Zend_Registry::get('appConfig')->settings->defaultLang;
+    }
+
+    public function langsTool()
+    {
+        return Zend_Registry::get('appConfig')->settings->langs->toArray();
     }
 
     public function configCacheTool()

www/system/library/Neno/Utils.php

 
         return false;
     }
+
+    public static function getCookie($name, $default = null)
+    {
+        if (isset($_COOKIE[$name])) {
+            $value = $_COOKIE[$name];
+        } else {
+            $value = null;
+        }
+
+        if (null === $value) {
+            $value = $default;
+        }
+
+        return $value;
+    }
 }

www/system/library/ZendY/Controller/Action.php

 
     protected function getCookie($name, $default = null)
     {
-        if (isset($_COOKIE[$name])) {
-            $value = $_COOKIE[$name];
-        } else {
-            $value = null;
-        }
-
-        if (null === $value) {
-            $value = $default;
-        }
-
-        return $value;
+        return Neno_Utils::getCookie($name, $default);
     }
 
     protected function reload()
     {
-        return $this->_helper->redirector->gotoUrl(Zend_Registry::get('requestUri'));
+        return $this->_helper->redirector->gotoUrl(Zend_Registry::get('requestUri'),
+                array('prependBase' => false));
     }
 
     protected function redirectToReferer()

www/system/library/ZendY/Controller/Plugin/Init.php

 {
     public function routeShutdown(Zend_Controller_Request_Abstract $request)
     {
+        //
+        $langs = Zend_Registry::get('langs');
+        if (count($langs) > 1) {
+            $view = Zend_Registry::get('zendView');
+            $currentLang = Zend_Registry::get('lang');
+
+            $view->lang($langs)->currentLang($currentLang);
+            ZendY_Controller_Router_Route_Multilingual::setCurrentLang($currentLang);
+        }
+
         // init Zend_Locale and Zend_Translate here to handle lang param in request
         Zend_Registry::set('Zend_Locale', Zend_Registry::get('zendLocale'));
         Zend_Registry::set('Zend_Translate', Zend_Registry::get('zendTranslate'));

www/system/library/ZendY/Controller/Router/Route/Module/Multilingual.php

+<?php
+class ZendY_Controller_Router_Route_Module_Multilingual extends
+        Zend_Controller_Router_Route_Module
+{
+    protected $_languageKey = 'lang';
+
+    public function __construct(array $defaults = array(),
+                Zend_Controller_Dispatcher_Interface $dispatcher = null,
+                Zend_Controller_Request_Abstract $request = null)
+    {
+        // multilingual specific
+        if (!array_key_exists($this->_languageKey, $defaults)
+                || strlen($defaults[$this->_languageKey]) != 2) {
+            throw new Zend_Controller_Router_Exception('Default language is not specified');
+        }
+
+        parent::__construct($defaults, $dispatcher, $request);
+    }
+
+    public function match($path, $partial = false)
+    {
+        $this->_setRequestKeys();
+
+        $values = array();
+        $params = array();
+
+        if (!$partial) {
+            $path = trim($path, self::URI_DELIMITER);
+        } else {
+            $matchedPath = $path;
+        }
+
+        if ($path != '') {
+            $path = explode(self::URI_DELIMITER, $path);
+
+            // multilingual specific
+            if (strlen($path[0]) == 2) {
+                $values[$this->_languageKey] = array_shift($path);
+            }
+
+            if ($this->_dispatcher && $this->_dispatcher->isValidModule($path[0])) {
+                $values[$this->_moduleKey] = array_shift($path);
+                $this->_moduleValid = true;
+            }
+
+            if (count($path) && !empty($path[0])) {
+                $values[$this->_controllerKey] = array_shift($path);
+            }
+
+            if (count($path) && !empty($path[0])) {
+                $values[$this->_actionKey] = array_shift($path);
+            }
+
+            if ($numSegs = count($path)) {
+                for ($i = 0; $i < $numSegs; $i = $i + 2) {
+                    $key = urldecode($path[$i]);
+                    $val = isset($path[$i + 1]) ? urldecode($path[$i + 1]) : null;
+                    $params[$key] = (isset($params[$key]) ? (array_merge((array) $params[$key], array($val))): $val);
+                }
+            }
+        }
+
+        if ($partial) {
+            $this->setMatchedPath($matchedPath);
+        }
+
+        $this->_values = $values + $params;
+
+        return $this->_values + $this->_defaults;
+    }
+
+    public function assemble($data = array(), $reset = false, $encode = true, $partial = false)
+    {
+        if (!$this->_keysSet) {
+            $this->_setRequestKeys();
+        }
+
+        $params = (!$reset) ? $this->_values : array();
+
+        foreach ($data as $key => $value) {
+            if ($value !== null) {
+                $params[$key] = $value;
+            } elseif (isset($params[$key])) {
+                unset($params[$key]);
+            }
+        }
+
+        $params += $this->_defaults;
+
+        $url = '';
+
+        // multilingual specific
+        if (array_key_exists($this->_languageKey, $data)) {
+            if ($params[$this->_languageKey] != $this->_defaults[$this->_languageKey]) {
+                $language = $params[$this->_languageKey];
+            }
+        }
+        unset($params[$this->_languageKey]);
+
+        if ($this->_moduleValid || array_key_exists($this->_moduleKey, $data)) {
+            if ($params[$this->_moduleKey] != $this->_defaults[$this->_moduleKey]) {
+                $module = $params[$this->_moduleKey];
+            }
+        }
+        unset($params[$this->_moduleKey]);
+
+        $controller = $params[$this->_controllerKey];
+        unset($params[$this->_controllerKey]);
+
+        $action = $params[$this->_actionKey];
+        unset($params[$this->_actionKey]);
+
+        foreach ($params as $key => $value) {
+            $key = ($encode) ? urlencode($key) : $key;
+            if (is_array($value)) {
+                foreach ($value as $arrayValue) {
+                    $arrayValue = ($encode) ? urlencode($arrayValue) : $arrayValue;
+                    $url .= '/' . $key;
+                    $url .= '/' . $arrayValue;
+                }
+            } else {
+                if ($encode) $value = urlencode($value);
+                $url .= '/' . $key;
+                $url .= '/' . $value;
+            }
+        }
+
+        if (!empty($url) || $action !== $this->_defaults[$this->_actionKey]) {
+            if ($encode) $action = urlencode($action);
+            $url = '/' . $action . $url;
+        }
+
+        if (!empty($url) || $controller !== $this->_defaults[$this->_controllerKey]) {
+            if ($encode) $controller = urlencode($controller);
+            $url = '/' . $controller . $url;
+        }
+
+        if (isset($module)) {
+            if ($encode) $module = urlencode($module);
+            $url = '/' . $module . $url;
+        }
+
+        // multilingual specific
+        if (isset($language)) {
+            if ($encode) $language = urlencode($language);
+            $url = '/' . $language . $url;
+        }
+
+        return ltrim($url, self::URI_DELIMITER);
+    }
+}

www/system/library/ZendY/Controller/Router/Route/Multilingual.php

+<?php
+class ZendY_Controller_Router_Route_Multilingual extends
+        Zend_Controller_Router_Route
+{
+    protected static $defaultLang = '';
+    protected static $currentLang = '';
+    protected static $languagePrefixes = array();
+
+    public static function init($langs, $defaultLang = '', $currentLang = '')
+    {
+        self::setLanguagePrefixes($langs);
+        if ($defaultLang) { self::setDefaultLang($defaultLang); }
+        if ($currentLang) { self::setCurrentLang($currentLang); }
+    }
+
+    public static function setDefaultLang($lang)
+    {
+        self::$defaultLang = $lang;
+    }
+
+    public static function setCurrentLang($lang)
+    {
+        self::$currentLang = $lang;
+    }
+
+    public static function setLanguagePrefixes(array $prefixes)
+    {
+        self::$languagePrefixes = $prefixes;
+    }
+
+    public function __construct($route, $defaults = array(), $reqs = array(),
+            Zend_Translate $translator = null, $locale = null)
+    {
+        if (!isset($defaults['lang']) && self::$defaultLang) {
+            $defaults['lang'] = self::$defaultLang;
+        }
+        parent::__construct($route, $defaults, $reqs, $translator, $locale);
+    }
+
+    public static function getInstance(Zend_Config $config)
+    {
+        $reqs = ($config->reqs instanceof Zend_Config)
+                ? $config->reqs->toArray() : array();
+        $defs = ($config->defaults instanceof Zend_Config)
+                ? $config->defaults->toArray() : array();
+        return new self($config->route, $defs, $reqs);
+    }
+
+    public function match($path)
+    {
+        $path = trim($path, $this->_urlDelimiter);
+        $pathParts = explode($this->_urlDelimiter, $path, 2);
+
+        if (in_array($pathParts[0], self::$languagePrefixes)) {
+            $path = (sizeof($pathParts) > 1) ? $pathParts[1] : '';
+            $currentLanguage = $pathParts[0];
+        } else {
+            $currentLanguage = $this->_defaults['lang'];
+        }
+
+        $params = parent::match($path);
+        if ($params) {
+            $params = array_merge($params, array('lang' => $currentLanguage));
+        }
+
+        return $params;
+    }
+
+
+    public function assemble($data = array(), $reset = false, $encode = false)
+    {
+        if (!isset($data['lang']) && self::$currentLang) {
+            $data['lang'] = self::$currentLang;
+        }
+
+        if (isset($data['lang'])) {
+            $lang = $data['lang'];
+            unset($data['lang']);
+
+            $assemble = parent::assemble($data, $reset, $encode);
+            if (in_array($lang, self::$languagePrefixes)) {
+                if ($lang != $this->_defaults['lang']) {
+                    $assemble = implode(
+                        $this->_urlDelimiter,
+                        array($lang, $assemble)
+                    );
+                }
+            }
+            return $assemble;
+        } else {
+            return parent::assemble($data, $reset);
+        }
+    }
+}

www/system/library/ZendY/Form/Doctrine.php

 
     public function create(Doctrine_Record $model = null)
     {
-        if ($model) {
+        if (null !== $model) {
             $this->setModel($model);
         }
         return $this->service->create($this->getValues(), $this->model);
 
     public function update(Doctrine_Record $model = null)
     {
-        if ($model) {
+        if (null !== $model) {
             $this->setModel($model);
         }
-        return $this->service->update($this->getValues(), $model);
+        return $this->service->update($this->getValues(), $this->model);
     }
 
-    public function save($model = null)
+    public function save(Doctrine_Record $model = null)
     {
-        if ($model) {
+        if (null !== $model) {
             $this->setModel($model);
         }
 

www/system/library/ZendY/Form/Element/Checkbox.php

+<?php
+class ZendY_Form_Element_Checkbox extends Zend_Form_Element_Checkbox
+{
+    public function loadDefaultDecorators()
+    {
+        if ($this->loadDefaultDecoratorsIsDisabled()) {
+            return;
+        }
+
+        $decorators = $this->getDecorators();
+        if (empty($decorators)) {
+            $this->addDecorator('ViewHelper')
+                ->addDecorator('Errors')
+                ->addDecorator('Description', array('tag' => 'p', 'class' => 'description'))
+                ->addDecorator('HtmlTag', array('tag' => 'dd',
+                                                'class' => 'zend_form_checkbox',
+                                                'id'  => $this->getName() . '-element'))
+                ->addDecorator('Label', array('tag' => 'dt'));
+        }
+    }
+}

www/system/library/ZendY/Form/Element/MyCheckbox.php

-<?php
-class ZendY_Form_Element_MyCheckbox extends Zend_Form_Element_Checkbox
-{
-    public function loadDefaultDecorators()
-    {
-        if ($this->loadDefaultDecoratorsIsDisabled()) {
-            return;
-        }
-
-        $decorators = $this->getDecorators();
-        if (empty($decorators)) {
-            $this->addDecorator('ViewHelper')
-                ->addDecorator('Errors')
-                ->addDecorator('Description', array('tag' => 'p', 'class' => 'description'))
-                ->addDecorator('HtmlTag', array('tag' => 'dd',
-                                                'class' => 'zend_form_checkbox',
-                                                'id'  => $this->getName() . '-element'))
-                ->addDecorator('Label', array('tag' => 'dt'));
-        }
-    }
-}

www/system/library/ZendY/Form/Element/MySelect.php

-<?php
-class ZendY_Form_Element_Select extends Zend_Form_Element_Select
-{
-    public function setRootMultiOption($option, $value = '')
-    {
-        $option  = (string) $option;
-        $this->_getMultiOptions();
-        if (!$this->_translateOption($option, $value)) {
-            $this->options[$option] = $value;
-        }
-
-        return $this;
-    }
-}

www/system/library/ZendY/Form/Element/MySubmit.php

-<?php
-class ZendY_Form_Element_MySubmit extends Zend_Form_Element_Submit
-{
-    public function loadDefaultDecorators()
-    {
-        if ($this->loadDefaultDecoratorsIsDisabled()) {
-            return;
-        }
-
-        $decorators = $this->getDecorators();
-        if (empty($decorators)) {
-            $this->addDecorators(array(
-                array('ViewHelper'),
-                array('HtmlTag', array('tag' => 'span',
-                        'class' => 'zend_form_submit',
-                        'id' => $this->getName() . '-span')),
-                array('Tooltip'),
-                array('DtDdWrapper'),
-            ));
-        }
-    }
-}

www/system/library/ZendY/Form/Element/Select.php

+<?php
+class ZendY_Form_Element_Select extends Zend_Form_Element_Select
+{
+    public function setRootMultiOption($options)
+    {
+        if (!is_array($options)) {
+            $options = array('' => $options);
+        }
+
+        foreach ($options as $key => &$value) {
+            $value = $this->_translateValue($value);
+        }
+        $this->options = $options + $this->options;
+
+        return $this;
+    }
+}

www/system/library/ZendY/Tool.php

 <?php
 class ZendY_Tool extends ZendY_Registry_Tool
 {
+    public function langTool()
+    {
+        $request = Zend_Controller_Front::getInstance()->getRequest();
+        if (null === $request) {
+            throw new Exception('Can not define lang as $request object is not initialized yet');
+        }
+
+        if (null !== ($lang = $request->getParam('lang'))) {
+            return $lang;
+        } else {
+            return Zend_Registry::get('defaultLang');
+        }
+    }
+
     public function zendAclTool()
     {
         $acl = new Zend_Acl();
     {
         $zendLocale = Zend_Registry::get('zendLocale');
 
-        $translator = new Zend_Translate(
+        $translator = new ZendY_Translate(
             'array',
             array(),
             (string) $zendLocale,
                 'disableNotices' => true,
             )
         );
+        $translator->setFallbackLocales(array('en'));
 
         $cache = Zend_Registry::get('configCache');
         $cacheId = 'translate' . (string) $zendLocale;

www/system/library/ZendY/Translate.php

+<?php
+class ZendY_Translate extends Zend_Translate
+{
+    protected $fallbackLocales = array();
+
+    public function setFallbackLocales(array $value)
+    {
+        $this->fallbackLocales = $value;
+    }
+
+    public function getFallbackLocales()
+    {
+        return $this->fallbackLocales;
+    }
+
+    public function getAdapter()
+    {
+        return $this;
+    }
+
+    public function translate($messageId, $locale = null)
+    {
+        $adapter = parent::getAdapter();
+
+        $locales = array_merge(array($locale), $this->fallbackLocales);
+        foreach ($locales as $currentLocale) {
+            if ($adapter->isTranslated($messageId, false, $currentLocale)) {
+                return $adapter->translate($messageId, $currentLocale);
+            }
+        }
+
+        return $adapter->translate($messageId, $locale);
+    }
+}

www/system/library/ZendY/View/Helper/Lang.php

+<?php
+class ZendY_View_Helper_Lang extends Zend_View_Helper_Abstract
+        implements Iterator
+{
+    protected $currentLang = '';
+    protected $langs = array();
+
+    public function currentLang($currentLang = '')
+    {
+        if ('' !== $currentLang) {
+            $this->currentLang = $currentLang;
+        }
+        return $this->currentLang;
+    }
+
+    public function lang($langs = array())
+    {
+        if (array() !== $langs) {
+            $this->setLangs($langs);
+        }
+        return $this;
+    }
+
+    public function clearLangs()
+    {
+        $this->langs = array();
+        return $this;
+    }
+
+    public function add($lang)
+    {
+        if (!is_array($lang)) {
+            $lang = array('lang' => $lang);
+        }
+        $this->langs[] = $lang;
+        return $this;
+    }
+
+    public function addLangs($langs)
+    {
+        foreach ($langs as $lang) {
+            $this->add($lang);
+        }
+        return $this;
+    }
+
+    public function setLangs($langs)
+    {
+        $this->clearLangs();
+        $this->addLangs($langs);
+        return $this;
+    }
+
+    /**
+     * @see Iterator::current()
+     *
+     */
+    public function current()
+    {
+        $lang = current($this->langs);
+        return array_merge($lang, array('isCurrent' =>
+                $lang['lang'] === $this->currentLang()));
+    }
+
+    /**
+     * @see Iterator::key()
+     *
+     */
+    public function key()
+    {
+        return key($this->langs);
+    }
+
+    /**
+     * @see Iterator::next()
+     *
+     */
+    public function next()
+    {
+        return next($this->langs);
+    }
+
+    /**
+     * @see Iterator::rewind()
+     *
+     */
+    public function rewind()
+    {
+        return reset($this->langs);
+    }
+
+    /**
+     * @see Iterator::valid()
+     *
+     */
+    public function valid()
+    {
+        return false !== current($this->langs);
+    }
+}

www/system/library/ZendY/View/Helper/Page.php

 
     public function page($label, $info = array())
     {
+        if (is_array($label) && Zend_Registry::isRegistered('Zend_Translate')) {
+            $t = Zend_Registry::get('Zend_Translate');
+            $label[0] = $t->translate($label[0]);
+            $label = call_user_func_array('sprintf', $label);
+        }
+
         $info['label'] = $label;
 
         if (isset($info['route'])) {

www/system/modules/blog/Bootstrap.php

     // disable ResourceLoader
     protected $_resourceLoader = false;
 
-    protected function _initCollector()
+    protected function _initModule()
     {
         $baseDir = dirname(__FILE__);
         Neno_Loader::getInstance()->registerPluginPrefixes(array(
             'Blog_' => $baseDir . '/library',
         ));
 
-        Zend_Layout::getMvcInstance()
-                ->setLayoutPath(MODULES_DIR . '/blog/views/scripts');
+        $view = Zend_Registry::get('zendView');
+        $view->addBasePath(MODULES_DIR . '/blog/views', 'Blog_View');
     }
 }

www/system/modules/blog/configs/doctrine/schema.yml

     Sluggable: { name: name_slug, fields: [ name ] }
   columns:
     name: { type: string(255), notnull: true, unique: true }
+  relations:
+    Entries: { class: Blog_Entry, local: tag_id, foreign: entry_id, refClass: Blog_EntryTag, foreignAlias: Tags }
+    EntryTags: { class: Blog_EntryTag, local: id, foreign: tag_id }
 
 Blog_Comment:
   actAs:
     text: { type: string, notnull: true }
     text_html: { type: string, notnull: true }
     entry_id: { type: integer, notnull: true }
-    user_id: { type: integer, notnull: true }
+    user_id: { type: integer }
+  relations:
+    Entry: { class: Blog_Entry, local: entry_id, foreign: id, onDelete: CASCADE, foreignAlias: Comments }
+    User: { class: Identity_User, local: user_id, foreign: id }
 
 Blog_Entry:
   actAs:
     text_html: { type: string, notnull: true }
     comment_number: { type: integer, notnull: true, default: 0 }
     is_published: { type: boolean, notnull: true, default: 0 }
+    is_feed: { type: boolean, notnull: true, default: 0 }
     user_id: { type: integer, notnull: true }
+    category_id: { type: integer, notnull: true }
   relations:
-    EntryTags: { class: Blog_EntryTag, onDelete: CASCADE, local: id, foreign: entry_id }
-    Tags: { class: Blog_Tag, local: entry_id, foreign: tag_id, refClass: Blog_EntryTag, foreignAlias: Entries }
-    Comments: { class: Blog_Comment, onDelete: CASCADE, local: id, foreign: entry_id, foreignAlias: Entry, type: many }
-    Aliases: { class: Blog_EntryAlias, onDelete: CASCADE, local: id, foreign: entry_id }
+    EntryTags: { class: Blog_EntryTag, local: id, foreign: entry_id, onDelete: CASCADE }
     User: { class: Identity_User, local: user_id, foreign: id }
+    Category: { class: Blog_Category, local: category_id, foreign: id }
 
 Blog_EntryAlias:
   columns:
     title_slug: { type: string(255), primary: true }
     entry_id: { type: integer, notnull: true }
+  relations:
+    Entry: { class: Blog_Entry, local: entry_id, foreign: id, onDelete: CASCADE, foreignAlias: Aliases }
 
 Blog_EntryTag:
   columns:
     entry_id: { type: integer, primary: true }
     tag_id: { type: integer, primary: true }
   relations:
-    Blog_Entry: { onDelete: CASCADE, local: entry_id, foreign: id }
-    Blog_Tag: { onDelete: CASCADE, local: tag_id, foreign: id }
+    Entry: { class: Blog_Entry, local: entry_id, foreign: id, onDelete: CASCADE }
+    Tag: { class: Blog_Tag, local: tag_id, foreign: id, onDelete: CASCADE }
+
+Blog_Category:
+  actAs:
+    Sluggable: { name: name_slug, fields: [ name ] }
+  columns:
+    name: { type: string(255), notnull: true, unique: true }