Commits

Anonymous committed eaa78d8

Refactor Doctrine_Lucene

  • Participants
  • Parent commits f11f516

Comments (0)

Files changed (14)

File doctrine_lucene/Lucene/branches/1.2-1.0/lib/Doctrine/Template/Listener/Lucene.php

+<?php
+/*
+ *  $Id$
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.phpdoctrine.org>.
+ */
+
+/**
+ * Doctrine_Template_Listener_Lucene
+ *
+ * @package     Doctrine
+ * @author      Vladimir Mihailenco <vladimir.webdev@gmail.com>
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @category    Object Relational Mapping
+ * @link        www.phpdoctrine.org
+ * @since       1.2
+ * @version     $Revision$
+ */
+
+class Doctrine_Template_Listener_Lucene extends Doctrine_Record_Listener
+{
+    /**
+     * Creates new document in index
+     *
+     * @param Doctrine_Event $event
+     */
+    public function postInsert(Doctrine_Event $event)
+    {
+        $invoker = $event->getInvoker();
+        $index = $invoker->getLuceneIndex();
+
+        $doc = $invoker->createLuceneDocument();
+        $index->addDocument($doc);
+        $index->commit();
+    }
+
+    /**
+     * Updates document in index
+     *
+     * @param Doctrine_Event $event
+     */
+    public function postUpdate(Doctrine_Event $event)
+    {
+        $invoker = $event->getInvoker();
+        $index = $invoker->getLuceneIndex();
+
+        $hits = $invoker->findLuceneDocument();
+        foreach ($hits as $hit) {
+            $index->delete($hit->id);
+        }
+
+        $doc = $invoker->createLuceneDocument();
+        $index->addDocument($doc);
+        $index->commit();
+    }
+
+    /**
+     * Deletes document in index
+     *
+     * @param Doctrine_Event $event
+     */
+    public function postDelete($event)
+    {
+        $invoker = $event->getInvoker();
+        $index = $invoker->getLuceneIndex();
+
+        $hits = $invoker->findLuceneDocument();
+        foreach ($hits as $hit) {
+            $index->delete($hit->id);
+        }
+        $index->commit();
+    }
+}

File doctrine_lucene/Lucene/branches/1.2-1.0/lib/Doctrine/Template/Lucene.php

+<?php
+/*
+ *  $Id$
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.phpdoctrine.org>.
+ */
+
+/**
+ * Doctrine_Template_Lucene
+ *
+ * @package     Doctrine
+ * @author      Vladimir Mihailenco <vladimir.webdev@gmail.com>
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @category    Object Relational Mapping
+ * @link        www.phpdoctrine.org
+ * @since       1.2
+ * @version     $Revision$
+ */
+
+class Doctrine_Template_Lucene extends Doctrine_Template
+{
+    /**
+     * Directory, where indexes will be stored
+     *
+     * @var string
+     */
+    protected $indexesDir = '';
+
+    /**
+     * Index file
+     *
+     * @var string
+     */
+    protected $indexFile = '';
+
+    /**
+     * Zend_Search_Lucene_Interface instance
+     *
+     * @var Zend_Search_Lucene_Interface
+     */
+    protected $index = null;
+
+    /**
+     * Fields to identify record in index
+     *
+     * @var array
+     */
+    protected $identifierFields = array();
+
+    /**
+     * Fields to be stored in index
+     *
+     * @var array
+     */
+    protected $userFields = array();
+
+    /**
+     * $identifierFields + $userFields
+     *
+     * @var array
+     */
+    protected $fields = array();
+
+    /**
+     * Encoding to use with Zend_Search_Lucene
+     *
+     * @var string
+     */
+    protected $encoding = '';
+
+    /**
+     * Sets path to index file
+     *
+     * @param string $file
+     * @return Doctrine_Template_Lucene
+     */
+    public function setIndexFile($file)
+    {
+        $this->indexFile = $file;
+        return $this;
+    }
+
+    /**
+     * Sets path to indexes dir
+     *
+     * @param string $dir
+     * @return Doctrine_Template_Lucene
+     */
+    public function setIndexesDir($dir)
+    {
+        $this->indexesDir = $dir;
+        return $this;
+    }
+
+    /**
+     * Encoding to use with Zend_Search_Lucene
+     *
+     * @param string $encoding
+     * @return Doctrine_Template_Lucene
+     */
+    public function setEncoding($encoding)
+    {
+        $this->encoding = $options['encoding'];
+        Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding($this->encoding);
+        return $this;
+    }
+
+    /**
+     * Default analyzer
+     *
+     * @param string|Zend_Search_Lucene_Analysis_Analyzer $analyzer
+     * @return Doctrine_Template_Lucene
+     */
+    public function setAnalyzer($analyzer)
+    {
+        if ($analyzer instanceof Zend_Search_Lucene_Analysis_Analyzer) {
+            Zend_Search_Lucene_Analysis_Analyzer::setDefault(
+                    $analyzer);
+        } else if (is_string($analyzer)) {
+            Zend_Search_Lucene_Analysis_Analyzer::setDefault(
+                    new $analyzer());
+        } else {
+            throw new Doctrine_Record_Exception('Analyzer should be class name'
+                    . ' or object instance');
+        }
+        return $this;
+    }
+
+    /**
+     * Sets filters for default analyzer
+     *
+     * @param array $filters
+     * @return * @return Doctrine_Template_Lucene
+     */
+    public function setFilters(array $filters)
+    {
+        foreach ($filters as $filter) {
+            $this->addFilter($filter);
+        }
+        return $this;
+    }
+
+    /**
+     * Stop words for Zend_Search_Lucene_Analysis_TokenFilter_StopWords
+     *
+     * @param array $words
+     * @return Doctrine_Template_Lucene
+     */
+    public function setStopWords(array $words)
+    {
+        $this->addFilter(new Zend_Search_Lucene_Analysis_TokenFilter_StopWords(
+                    $words));
+        return $this;
+    }
+
+    /**
+     *
+     * @param array $options
+     */
+    public function __construct(array $options = array())
+    {
+        if (!isset($options['indexes_dir']) && !isset($options['index_file'])) {
+            throw new Doctrine_Record_Exception('You have to define at least one'
+                . ' of these option: "indexes_dir" or "index_file"');
+        }
+
+        if (!isset($options['fields'])) {
+            throw new Doctrine_Record_Exception('Option "fields" is required');
+        }
+
+        foreach ($options as $key => $value) {
+            $setter = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ',
+                    $key)));
+            if (method_exists($this, $setter)) {
+                $this->$setter($value);
+            }
+        }
+
+        parent::__construct($options);
+    }
+
+    /**
+     * 
+     */
+    public function setTableDefinition()
+    {
+        $this->addListener(new Doctrine_Template_Listener_Lucene());
+    }
+
+    /**
+     * Path to lucene index file
+     *
+     * @return string
+     */
+    public function getLuceneIndexFile()
+    {
+        if (!$this->indexFile) {
+            $this->indexFile = $this->indexesDir . '/' . get_class($this->_invoker);
+        }
+        return $this->indexFile;
+    }
+
+    /**
+     * Zend_Search_Lucene_Interface instance
+     *
+     * @return Zend_Search_Lucene_Interface
+     */
+    public function getLuceneIndex()
+    {
+        if (null === $this->index) {
+            if (file_exists($index = $this->getLuceneIndexFile())) {
+                $this->index = Zend_Search_Lucene::open($index);
+            } else {
+                $this->index = Zend_Search_Lucene::create($index);
+            }
+        }
+        return $this->index;
+    }
+
+    /**
+     * Finds lucene document associated with current record,
+     * using indentifier fields
+     *
+     * @return array Zend_Search_Lucene_Search_QueryHit
+     */
+    public function findLuceneDocument()
+    {
+        $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
+        foreach ($this->getLuceneIdentifierFields() as $field) {
+            $name = $field['name'];
+            $query->addTerm(new Zend_Search_Lucene_Index_Term($this->_invoker->$name,
+                    $name), true);
+        }
+        return $this->getLuceneIndex()->find($query);
+    }
+
+    /**
+     * Creates lucene document based on current record
+     *
+     * @return Zend_Search_Lucene_Document
+     */
+    public function createLuceneDocument()
+    {
+        $doc = new Zend_Search_Lucene_Document();
+        foreach ($this->getLuceneFields() as $field) {
+            $type = $field['type'];
+            $name = $field['name'];
+
+            $docField = Zend_Search_Lucene_Field::$type($name,
+                    $this->_invoker->$name, $this->encoding);
+            if (isset($field['boost'])) {
+                $docField->boost = $field['boost'];
+            }
+            $doc->addField($docField);
+        }
+        return $doc;
+    }
+
+    /**
+     * Returns identifier fields
+     *
+     * @return array
+     */
+    public function getLuceneIdentifierFields()
+    {
+        if (array() === $this->identifierFields) {
+            $identifier = $this->_invoker->getTable()->getIdentifier();
+            foreach ((array) $identifier as $id) {
+                $this->identifierFields[] = array(
+                    'name' => $id,
+                    'type' => 'Keyword',
+                );
+            }
+        }
+        return $this->identifierFields;
+    }
+
+    /**
+     * Returns user defined fields, which will be stored in index
+     *
+     * @return array
+     */
+    public function getLuceneUserFields()
+    {
+        if (array() === $this->userFields) {
+            $this->userFields = $this->_options['fields'];
+        }
+        return $this->userFields;
+    }
+
+    /**
+     * getLuceneIdentifierFields() + getLuceneUserFields()
+     *
+     * @return array
+     */
+    public function getLuceneFields()
+    {
+        if (array() === $this->fields) {
+            $this->fields = array_merge($this->getLuceneIdentifierFields(),
+                    $this->getLuceneUserFields());
+        }
+        return $this->fields;
+    }
+
+    /**
+     * Adds filter to default analyzer
+     *
+     * @param <type> $filter
+     * @return Doctrine_Template_Lucene
+     */
+    public function addFilter($filter)
+    {
+        $analyzer = Zend_Search_Lucene_Analysis_Analyzer::getDefault();
+        $analyzer->addFilter($filter);
+
+        return $this;
+    }
+}

File doctrine_lucene/Lucene/branches/1.2-1.0/package.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.4.1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+  <name>Lucene</name>
+  <channel>pear.phpdoctrine.org</channel>
+  <summary>Adds Zend_Search_Lucene integration</summary>
+  <description>Adds ability to store/remove/find records in Zend_Search_Lucene index.</description>
+  <lead>
+    <name>Vladimir Mihailenco</name>
+    <user>vladimir.webdev</user>
+    <email></email>
+    <active>yes</active>
+  </lead>
+  <date>2010-04-01</date>
+  <version>
+    <release>1.0</release>
+    <api>1.0</api>
+  </version>
+  <stability>
+    <release>1.0</release>
+    <api>stable</api>
+  </stability>
+  <notes>-</notes>
+  <contents>
+  </contents>
+  <dependencies>
+    <required>
+      <php>
+        <min>5.2.4</min>
+      </php>
+      <pearinstaller>
+        <min>1.4.1</min>
+      </pearinstaller>
+      <package>
+        <name>Doctrine</name>
+        <channel>pear.phpdoctrine.org</channel>
+        <min>1.2.0</min>
+        <max>1.2.0</max>
+      </package>
+    </required>
+  </dependencies>
+  <phprelease></phprelease>
+  <changelog></changelog>
+</package>

File doctrine_lucene/README

+Doctrine template for Zend_Search_Lucene
+========================================
+
+- [Zend_Search_Lucene documentation](http://framework.zend.com/manual/en/zend.search.lucene.html).
+
+Usage
+=====
+
+Define your models:
+
+    <?php
+    class Article extends Doctrine_Record
+    {
+        public function setTableDefinition()
+        {
+            $this->hasColumn('id', 'integer', null, array('primary' => true,
+                    'autoincrement' => true));
+            $this->hasColumn('name', 'string', 128);
+            $this->hasColumn('details', 'string');
+
+            $this->actAs(new DoctrineX_Template_Lucene(array(
+                'indexes_dir' => 'application/data/indexes',
+                'fields' => array(
+                    array('name' => 'name', 'type' => 'text', 'boost' => 5.0),
+                    array('name' => 'details', 'type' => 'unStored'),
+                ),
+                'encoding' => 'UTF-8',
+                'analyzer' => new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive(),
+                'stopWords' => array('и', 'в', 'по'),
+            )));
+        }
+    }
+
+Use it:
+
+    <?php
+    class Article extends Doctrine_Record
+    {
+        public function search($q)
+        {
+            $hits = $this->getLuceneIndex()->find($q);
+            return new Zend_Paginator(new Zend_Paginator_Adapter_Array($hits));
+        }
+    }
+
+## Source code
+
+Source code: [Doctrine_Template_Lucene](http://bitbucket.org/vladimir_webdev/doctrine_lucene/src/).

File doctrine_lucene/library/DoctrineX/Template/Listener/Lucene.php

-<?php
-class DoctrineX_Template_Listener_Lucene extends Doctrine_Record_Listener
-{
-    public function __construct(array $options)
-    {
-        $this->_options = $options;
-    }
-
-    /**
-     *
-     * @param Doctrine_Event $event
-     */
-    public function postInsert(Doctrine_Event $event)
-    {
-        $invoker = $event->getInvoker();
-        $index = $invoker->getLuceneIndex();
-
-        if (method_exists($invoker, 'preLucene')) {
-            $invoker->preLucene();
-        }
-
-        $doc = $invoker->createLuceneDocument();
-        $index->addDocument($doc);
-        $index->commit();
-
-        if (method_exists($invoker, 'postLucene')) {
-            $invoker->postLucene();
-        }
-    }
-
-    public function postUpdate(Doctrine_Event $event)
-    {
-        $invoker = $event->getInvoker();
-        $index = $invoker->getLuceneIndex();
-
-        $hits = $invoker->findLuceneDocument();
-        foreach ($hits as $hit) {
-            $index->delete($hit->id);
-        }
-
-        $doc = $invoker->createLuceneDocument();
-        $index->addDocument($doc);
-        $index->commit();
-    }
-
-    public function postDelete(Doctrine_Event $event)
-    {
-        $invoker = $event->getInvoker();
-        $index = $invoker->getLuceneIndex();
-
-        $hits = $invoker->findLuceneDocument();
-        foreach ($hits as $hit) {
-            $index->delete($hit->id);
-        }
-        $index->commit();
-    }
-}

File doctrine_lucene/library/DoctrineX/Template/Lucene.php

-<?php
-class DoctrineX_Template_Lucene extends Doctrine_Template
-{
-    /**
-     *
-     * @var Zend_Search_Lucene_Interface
-     */
-    protected $index = null;
-
-    /**
-     *
-     * @var array
-     */
-    protected $identifierFields = array();
-
-    /**
-     *
-     * @var array
-     */
-    protected $userFields = array();
-
-    /**
-     *
-     * @var array
-     */
-    protected $fields = array();
-
-    /**
-     *
-     * @var string
-     */
-    protected $encoding = '';
-
-    public function __construct(array $options = array())
-    {
-        if (!isset($options['indexesPath']) && !isset($options['indexFile'])) {
-            throw new Doctrine_Record_Exception('Option "indexesPath" or "indexFile" is required');
-        }
-
-        if (!isset($options['fields'])) {
-            throw new Doctrine_Record_Exception('Option "fields" is required');
-        }
-
-        if (isset($options['encoding'])) {
-            $this->encoding = $options['encoding'];
-            Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding($this->encoding);
-        }
-
-        if (isset($options['analyzer'])) {
-            Zend_Search_Lucene_Analysis_Analyzer::setDefault(
-                new $options['analyzer']()
-            );
-        }
-
-        if (isset($options['filters'])) {
-            $filters = $options['filters'];
-        } else {
-            $filters = array();
-        }
-        if (isset($options['stopWords'])) {
-            $filters[] = new Zend_Search_Lucene_Analysis_TokenFilter_StopWords(
-                    $options['stopWords']);
-        }
-        if (isset($options['stopWordsFile'])) {
-            $stopWordsFilter = new Zend_Search_Lucene_Analysis_TokenFilter_StopWords();
-            $stopWordsFilter->loadFromFile($options['stopWordsFile']);
-            $filters[] = $stopWordsFilter;
-        }
-        if (!empty($filters)) {
-            $analyzer = Zend_Search_Lucene_Analysis_Analyzer::getDefault();
-            foreach ($filters as $filter) {
-                $analyzer->addFilter($filter);
-            }
-        }
-
-        parent::__construct($options);
-    }
-
-    public function setTableDefinition()
-    {
-        $this->addListener(new DoctrineX_Template_Listener_Lucene($this->_options));
-    }
-
-    /**
-     *
-     * @return string
-     */
-    public function getLuceneIndexFile()
-    {
-        if (isset($this->_options['indexFile'])) {
-            return $this->_options['indexFile'];
-        } else {
-            return $this->_options['indexesPath'] . '/' . get_class($this->_invoker);
-        }
-    }
-
-    /**
-     *
-     * @return Zend_Search_Lucene_Interface
-     */
-    public function getLuceneIndex()
-    {
-        if (null === $this->index) {
-            if (file_exists($index = $this->getLuceneIndexFile())) {
-                $this->index = Zend_Search_Lucene::open($index);
-            } else {
-                $this->index = Zend_Search_Lucene::create($index);
-            }
-        }
-        return $this->index;
-    }
-
-    public function findLuceneDocument()
-    {
-        $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
-        foreach ($this->getLuceneIdentifierFields() as $field) {
-            $name = $field['name'];
-            $query->addTerm(new Zend_Search_Lucene_Index_Term($this->_invoker->$name,
-                    $name), true);
-        }
-        return $this->getLuceneIndex()->find($query);
-    }
-
-    public function createLuceneDocument()
-    {
-        $doc = new Zend_Search_Lucene_Document();
-        foreach ($this->getLuceneFields() as $field) {
-            $type = $field['type'];
-            $name = $field['name'];
-
-            $docField = Zend_Search_Lucene_Field::$type($name,
-                    $this->_invoker->$name, $this->encoding);
-            if (isset($field['boost'])) {
-                $docField->boost = $field['boost'];
-            }
-            $doc->addField($docField);
-        }
-        return $doc;
-    }
-
-    /**
-     *
-     * @return array
-     */
-    public function getLuceneIdentifierFields()
-    {
-        if (array() === $this->identifierFields) {
-            $identifier = $this->_invoker->getTable()->getIdentifier();
-            foreach ((array) $identifier as $id) {
-                $this->identifierFields[] = array(
-                    'name' => $id,
-                    'type' => 'Keyword',
-                );
-            }
-        }
-        return $this->identifierFields;
-    }
-
-    public function getLuceneUserFields()
-    {
-        if (array() === $this->userFields) {
-            $this->userFields = $this->_options['fields'];
-        }
-        return $this->userFields;
-    }
-
-    /**
-     *
-     * @return array
-     */
-    public function getLuceneFields()
-    {
-        if (array() === $this->fields) {
-            $this->fields = array_merge($this->getLuceneIdentifierFields(),
-                    $this->getLuceneUserFields());
-        }
-        return $this->fields;
-    }
-}

File nbproject/private/private.properties

+index.file=index.php
+url=http://localhost/lout/

File nbproject/private/private.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
+    <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
+</project-private>

File nbproject/project.properties

+include.path=${php.global.include.path}
+php.version=PHP_5
+source.encoding=UTF-8
+src.dir=.
+tags.asp=false
+tags.short=true
+web.root=.

File nbproject/project.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.php.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/php-project/1">
+            <name>lout</name>
+        </data>
+    </configuration>
+</project>

File zf_bundles/README

+# JavaScript and CSS bundles for Zend Framework
+
+- [HeadLink and HeadScript helpers](http://framework.zend.com/manual/en/zend.view.helpers.html).
+- [bundle-phu - minify, bundle, and compress your js/css in Zend Framework](http://hobodave.com/2010/01/17/bundle-phu-compress-your-js-css-in-zend-framework/).
+
+## Usage
+
+    <head>
+    <!-- ... -->
+    <?php echo $this->bundleLink($this->jQuery(), $this->headLink()); ?>
+    <?php echo $this->headLink(); ?>
+    <?php echo $this->headStyle(); ?>
+    <?php echo $this->bundleScript($this->jQuery(), $this->headScript()); ?>
+    <?php echo $this->jQuery(); ?>
+    <?php echo $this->headScript(); ?>
+    </head>
+
+## Source code
+
+Source code: [JavaScript and CSS bundles for Zend Framework](http://bitbucket.org/vladimir_webdev/zf_bundles/src/).

File zf_highlight/README

+# Highlight.js view helper for Zend Framework
+
+- [HeadLink and HeadScript helpers](http://framework.zend.com/manual/en/zend.view.helpers.html).
+
+# Usage
+
+    $this->view->highlight()
+            ->setBasePath('js/text/highlight')
+            ->setScriptPath('/highlight.pack.js')
+            ->setStylePath('/styles/vs.css')
+            ->setTabReplace('    ')
+            ->setLanguages(array('php', 'js'))
+            ->inject();
+
+or
+
+    $this->view->highlight(array(
+        'basePath' => 'js/text/highlight',
+        'scriptPath' => '/highlight.pack.js',
+        'stylePath' => '/styles/vs.css',
+        'tabReplace' => '    ',
+        'languages' => array('php', 'js'),
+    ))->inject();
+
+# Source code
+
+Source code: [Text_View_Helper_Highlight](http://bitbucket.org/vladimir_webdev/zf_highlight/src/).
+
+    <?php
+    class Text_View_Helper_Highlight extends Zend_View_Helper_Abstract
+    {
+        protected $basePath = '';
+        protected $scriptPath = '';
+        protected $stylePath = '';
+        protected $tabReplace = '';
+        protected $languages = array();
+
+        public function setBasePath($path)
+        {
+            $this->basePath = $path;
+            return $this;
+        }
+
+        public function getBasePath()
+        {
+            return $this->basePath;
+        }
+
+        public function setScriptPath($path)
+        {
+            $this->scriptPath = $this->getBasePath() . $path;
+            return $this;
+        }
+
+        public function getScriptPath()
+        {
+            return $this->scriptPath;
+        }
+
+        public function setStylePath($path)
+        {
+            $this->stylePath = $this->getBasePath() . $path;
+            return $this;
+        }
+
+        public function getStylePath()
+        {
+            return $this->stylePath;
+        }
+
+        public function setTabReplace($tabReplace)
+        {
+            $this->tabReplace = $tabReplace;
+            return $this;
+        }
+
+        public function getTabReplace()
+        {
+            return $this->tabReplace;
+        }
+
+        public function setLanguages(array $languages)
+        {
+            $this->languages = $languages;
+            return $this;
+        }
+
+        public function getLanguages()
+        {
+            return $this->languages;
+        }
+
+        public function setOptions(array $options)
+        {
+            foreach ($options as $key => $value) {
+                $methodName = 'set' . $key;
+                if (method_exists($this, $methodName)) {
+                    $this->$methodName($value);
+                } else {
+                    throw new Zend_View_Exception('Unknown option "' . $key . '"');
+                }
+            }
+            return $this;
+        }
+
+        public function highlight(array $options = array())
+        {
+            $this->setOptions($options);
+            return $this;
+        }
+
+        protected function _getScript()
+        {
+            $script = '';
+
+            $tabReplace = $this->getTabReplace();
+            if ('' !== $tabReplace) {
+                $tabReplace = str_replace('"', '\"', $tabReplace);
+                $script .= 'hljs.tabReplace = "' . $tabReplace . '";' . PHP_EOL;
+            }
+
+            $languages = $this->getLanguages();
+            if (array() !== $languages) {
+                $languages = '"' . implode('", "', $languages) . '"';
+            } else {
+                $languages = '';
+            }
+            $script .= 'hljs.initHighlightingOnLoad(' . $languages . ');' . PHP_EOL;
+
+            return $script;
+        }
+
+        public function inject()
+        {
+            $localPath = $this->getScriptPath();
+            if (!empty($localPath)) {
+                $this->view->headScript()
+                        ->appendFile($localPath)
+                        ->appendScript($this->_getScript());
+            } else {
+                throw new Zend_View_Exception('Option "scriptPath" must be set');
+            }
+
+            $stylePath = $this->getStylePath();
+            if (!empty($stylePath)) {
+                $this->view->headLink()->appendStylesheet($stylePath);
+            }
+        }
+
+        public function __toString()
+        {
+            $this->inject();
+            return '';
+        }
+    }

File zf_multilingual/README

+# Multilingual router for Zend Framework
+
+## Usage
+
+    <?php
+    $langs = array('en', 'ru');
+    $defaultLang = 'en';
+
+    ZendY_Controller_Router_Route_Multilingual::init(array(
+        'langs' => $langs,
+        'defaultLang' => $defaultLang,
+    ));
+
+    Zend_Controller_Front::getInstance()->getRouter()
+            ->addRoutes(array(
+                'gallery_index' => array(
+                    'type' => 'ZendY_Controller_Router_Route_Multilingual',
+                    'route' => 'gallery',
+                    'defaults' => array(
+                        'module' => 'gallery',
+                        'controller' => 'gallery',
+                        'action' => 'index',
+                    ),
+                ),
+            ));
+
+    // at route shutdown, lang is defined in request
+    $lang = Zend_Controller_Front::getInstance()->getRequest()->getParam('lang'));
+
+## Source code
+
+Source code: [ZendY_Controller_Router_Route_Multilingual](http://bitbucket.org/vladimir_webdev/zf_multilingual/src/).
+
+    <?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($options)
+        {
+            if (isset($options['languagePrefixes'])) {
+                self::setLanguagePrefixes($options['languagePrefixes']);
+            }
+            if (isset($options['defaultLang'])) {
+                self::setDefaultLang($options['defaultLang']);
+            }
+            if (isset($options['currentLang'])) {
+                self::setCurrentLang($options['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];
+
+                self::setCurrentLang($currentLanguage);
+            } 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);
+            }
+        }
+    }

File zf_translate/README

+# Default languages for non-translated messages
+
+Strings which are not translated in one language will be returned in another language.
+
+## Usage
+
+i18n/en.php:
+
+    <?php
+    return array(
+        'lang' => 'en',
+        'lol' => 'laughing out loud',
+    );
+
+i18n/ru.php (note that "lol" is not translated for this language):
+
+    <?php
+    return array(
+        'lang' => 'ru',
+    );
+
+ZendY/TranslateTestCase.php:
+
+    <?php
+    class ZendY_TranslateTestCase extends PHPUnit_Framework_TestCase
+    {
+        public function testTranslate()
+        {
+            $translator = new ZendY_Translate(
+                'array',
+                dirname(__FILE__) . '/i18n',
+                'ru' // default locale
+            );
+            // if we are unable to find translation for language "ru",
+            // use translation for language "en"
+            $translator->setDefault(array('en'));
+
+            // translation from language "ru"
+            $this->assertEquals('ru', $translator->translate('lang'));
+
+            // translation from language "en"
+            $this->assertEquals('laughing out loud', $translator->translate('lol'));
+        }
+    }
+
+## Source code
+
+Source code: [ZendY_Translate](http://bitbucket.org/vladimir_webdev/zf_translate/src/).
+
+    <?php
+    class ZendY_Translate extends Zend_Translate
+    {
+        protected $default = array();
+
+        public function setDefault(array $value)
+        {
+            $this->default = $value;
+        }
+
+        public function getDefault()
+        {
+            return $this->default;
+        }
+
+        public function getAdapter()
+        {
+            return $this;
+        }
+
+        public function translate($messageId, $locale = null)
+        {
+            $adapter = parent::getAdapter();
+
+            $locales = array_merge(array($locale), $this->default);
+            foreach ($locales as $currentLocale) {
+                if ($adapter->isTranslated($messageId, false, $currentLocale)) {
+                    return $adapter->translate($messageId, $currentLocale);
+                }
+            }
+
+            return $adapter->translate($messageId, $locale);
+        }
+    }