Translation2
[ class tree: Translation2 ] [ index: Translation2 ] [ all elements ]

Source for file mdb2.php

Documentation is available at mdb2.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. /**
  5.  * Contains the Translation2_Admin_Container_mdb2 class
  6.  *
  7.  * PHP versions 4 and 5
  8.  *
  9.  * LICENSE: Redistribution and use in source and binary forms, with or without
  10.  * modification, are permitted provided that the following conditions are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. The name of the author may not be used to endorse or promote products
  17.  *    derived from this software without specific prior written permission.
  18.  *
  19.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
  20.  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  22.  * IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
  23.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  26.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  28.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  *
  30.  * @category  Internationalization
  31.  * @package   Translation2
  32.  * @author    Lorenzo Alberton <l.alberton@quipo.it>
  33.  * @copyright 2004-2007 Lorenzo Alberton
  34.  * @license   http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
  35.  * @version   CVS: $Id: mdb2.php,v 1.41 2008/05/03 09:17:59 quipo Exp $
  36.  * @link      http://pear.php.net/package/Translation2
  37.  */
  38.  
  39. /**
  40.  * require Translation2_Container_mdb2 class
  41.  */
  42. require_once 'Translation2/Container/mdb2.php';
  43.  
  44. /**
  45.  * Storage driver for storing/fetching data to/from a database
  46.  *
  47.  * This storage driver can use all databases which are supported
  48.  * by the PEAR::MDB2 abstraction layer to store and fetch data.
  49.  *
  50.  * @category  Internationalization
  51.  * @package   Translation2
  52.  * @author    Lorenzo Alberton <l.alberton@quipo.it>
  53.  * @copyright 2004-2007 Lorenzo Alberton
  54.  * @license   http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
  55.  * @link      http://pear.php.net/package/Translation2
  56.  */
  57. {
  58.     // {{{
  59.  
  60.     /**
  61.      * Fetch the table names from the db
  62.      *
  63.      * @access private
  64.      * @return array|PEAR_Error
  65.      */
  66.     function _fetchTableNames()
  67.     {
  68.         $this->db->loadModule('Manager');
  69.         return $this->db->manager->listTables();
  70.     }
  71.  
  72.     // }}}
  73.     // {{{ addLang()
  74.  
  75.     /**
  76.      * Creates a new table to store the strings in this language.
  77.      * If the table is shared with other langs, it is ALTERed to
  78.      * hold strings in this lang too.
  79.      *
  80.      * @param array $langData array('lang_id'    => 'en',
  81.      *                               'table_name' => 'i18n',
  82.      *                               'name'       => 'english',
  83.      *                               'meta'       => 'some meta info',
  84.      *                               'error_text' => 'not available');
  85.      * @param array $options  array('charset'   => 'utf8',
  86.      *                               'collation' => 'utf8_general_ci');
  87.      *
  88.      * @return true|PEAR_Error
  89.      */
  90.     function addLang($langData$options = array())
  91.     {
  92.         $tables $this->_fetchTableNames();
  93.         if (PEAR::isError($tables)) {
  94.             return $tables;
  95.         }
  96.  
  97.         $lang_col  $this->_getLangCol($langData['lang_id']);
  98.         $charset   = empty($options['charset'])   ? null : $options['charset'];
  99.         $collation = empty($options['collation']? null : $options['collation'];
  100.         $this->db->loadModule('Manager');
  101.  
  102.         if (in_array($langData['table_name']$tables)) {
  103.             //table exists
  104.             $table_changes = array(
  105.                 'add' => array(
  106.                     $lang_col => array(
  107.                         'type' => 'text',
  108.                         'charset'   => $charset,
  109.                         'collation' => $collation,
  110.                     )
  111.                 )
  112.             );
  113.             ++$this->_queries;
  114.             return $this->db->manager->alterTable($langData['table_name']$table_changesfalse);
  115.         }
  116.  
  117.         //table does not exist
  118.         $table_definition = array(
  119.             $this->options['string_page_id_col'=> array(
  120.                 'type'      => 'text',
  121.                 'length'    => $this->options['string_page_id_col_length'],
  122.                 'default'   => null,
  123.                 'charset'   => $charset,
  124.                 'collation' => $collation,
  125.             ),
  126.             $this->options['string_id_col'=> array(
  127.                 'type'      => 'text',
  128.                 'notnull'   => 1,
  129.                 'charset'   => $charset,
  130.                 'collation' => $collation,
  131.             ),
  132.             $lang_col => array(
  133.                 'type'      => 'text',
  134.                 'charset'   => $charset,
  135.                 'collation' => $collation,
  136.             ),
  137.         );
  138.         ++$this->_queries;
  139.         $res $this->db->manager->createTable($langData['table_name']$table_definition);
  140.         if (PEAR::isError($res)) {
  141.             return $res;
  142.         }
  143.         $mysqlClause ($this->db->phptype == 'mysql''(255)' '';
  144.         
  145.         $constraint_name $langData['table_name']
  146.             .'_'$this->options['string_page_id_col']
  147.             .'_'$this->options['string_id_col'];
  148.         $constraint_definition = array(
  149.             'fields' => array(
  150.                 $this->options['string_page_id_col'=> array(),
  151.                 $this->options['string_id_col'].$mysqlClause => array(),
  152.             ),
  153.             'unique' => true,
  154.         );
  155.         ++$this->_queries;
  156.         $res $this->db->manager->createConstraint($langData['table_name']$constraint_name$constraint_definition);
  157.         if (PEAR::isError($res)) {
  158.             return $res;
  159.         }
  160.  
  161.         $index_name $langData['table_name'.'_'$this->options['string_page_id_col'];
  162.         $index_definition = array(
  163.             'fields' => array($this->options['string_page_id_col'=> array())
  164.         );
  165.         ++$this->_queries;
  166.         $res $this->db->manager->createIndex($langData['table_name']$index_name$index_definition);
  167.         if (PEAR::isError($res)) {
  168.             return $res;
  169.         }
  170.         
  171.         $index_name $langData['table_name'.'_'$this->options['string_id_col'];
  172.         $index_definition = array(
  173.             'fields' => array($this->options['string_id_col'=> array('length' => 255))
  174.         );
  175.         ++$this->_queries;
  176.         $res $this->db->manager->createIndex($langData['table_name']$index_name$index_definition);
  177.         if (PEAR::isError($res)) {
  178.             return $res;
  179.         }
  180.         
  181.         return true;
  182.     }
  183.  
  184.     // }}}
  185.     // {{{ addLangToList()
  186.  
  187.     /**
  188.      * Creates a new entry in the langsAvail table.
  189.      * If the table doesn't exist yet, it is created.
  190.      *
  191.      * @param array $langData array('lang_id'    => 'en',
  192.      *                               'table_name' => 'i18n',
  193.      *                               'name'       => 'english',
  194.      *                               'meta'       => 'some meta info',
  195.      *                               'error_text' => 'not available',
  196.      *                               'encoding'   => 'iso-8859-1');
  197.      *
  198.      * @return true|PEAR_Error
  199.      */
  200.     function addLangToList($langData)
  201.     {
  202.         $tables $this->_fetchTableNames();
  203.         if (PEAR::isError($tables)) {
  204.             return $tables;
  205.         }
  206.  
  207.         if (!in_array($this->options['langs_avail_table']$tables)) {
  208.             $queries   = array();
  209.             $queries[sprintf('CREATE TABLE %s ('
  210.                                 .'%s VARCHAR(16), '
  211.                                 .'%s VARCHAR(200), '
  212.                                 .'%s TEXT, '
  213.                                 .'%s VARCHAR(250), '
  214.                                 .'%s VARCHAR(16) )',
  215.                 $this->db->quoteIdentifier($this->options['langs_avail_table']true),
  216.                 $this->db->quoteIdentifier($this->options['lang_id_col']true),
  217.                 $this->db->quoteIdentifier($this->options['lang_name_col']true),
  218.                 $this->db->quoteIdentifier($this->options['lang_meta_col']true),
  219.                 $this->db->quoteIdentifier($this->options['lang_errmsg_col']true),
  220.                 $this->db->quoteIdentifier($this->options['lang_encoding_col']true)
  221.             );
  222.             $queries[sprintf('CREATE UNIQUE INDEX %s_%s_index ON %s (%s)',
  223.                 $this->db->quoteIdentifier($this->options['langs_avail_table']true),
  224.                 $this->db->quoteIdentifier($this->options['lang_id_col']true),
  225.                 $this->db->quoteIdentifier($this->options['langs_avail_table']true),
  226.                 $this->db->quoteIdentifier($this->options['lang_id_col']true)
  227.             );
  228.  
  229.             foreach ($queries as $query{
  230.                 ++$this->_queries;
  231.                 $res $this->db->exec($query);
  232.                 if (PEAR::isError($res)) {
  233.                     return $res;
  234.                 }
  235.             }
  236.         }
  237.  
  238.         $query sprintf('INSERT INTO %s (%s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s)',
  239.             $this->db->quoteIdentifier($this->options['langs_avail_table']true),
  240.             $this->db->quoteIdentifier($this->options['lang_id_col']true),
  241.             $this->db->quoteIdentifier($this->options['lang_name_col']true),
  242.             $this->db->quoteIdentifier($this->options['lang_meta_col']true),
  243.             $this->db->quoteIdentifier($this->options['lang_errmsg_col']true),
  244.             $this->db->quoteIdentifier($this->options['lang_encoding_col']true),
  245.             $this->db->quote($langData['lang_id']),
  246.             $this->db->quote($langData['name']),
  247.             $this->db->quote($langData['meta']),
  248.             $this->db->quote($langData['error_text']),
  249.             $this->db->quote($langData['encoding'])
  250.         );
  251.  
  252.         ++$this->_queries;
  253.         $res $this->db->exec($query);
  254.         $this->options['strings_tables'][$langData['lang_id']] $langData['table_name'];
  255.         if (PEAR::isError($res)) {
  256.             return $res;
  257.         }
  258.         return true;
  259.     }
  260.  
  261.     // }}}
  262.     // {{{ removeLang()
  263.  
  264.     /**
  265.      * Remove the lang from the langsAvail table and drop the strings table.
  266.      * If the strings table holds other langs and $force==false, then
  267.      * only the lang column is dropped. If $force==true the whole
  268.      * table is dropped without any check
  269.      *
  270.      * @param string  $langID language ID
  271.      * @param boolean $force  if true, the whole table is dropped without checks
  272.      *
  273.      * @return true|PEAR_Error
  274.      */
  275.     function removeLang($langID$force)
  276.     {
  277.         //remove from langsAvail
  278.         $query sprintf('DELETE FROM %s WHERE %s = %s',
  279.             $this->db->quoteIdentifier($this->options['langs_avail_table']true),
  280.             $this->db->quoteIdentifier($this->options['lang_id_col']true),
  281.             $this->db->quote($langID'text')
  282.         );
  283.         ++$this->_queries;
  284.         $res $this->db->exec($query);
  285.         if (PEAR::isError($res)) {
  286.             return $res;
  287.         }
  288.  
  289.         $this->db->loadModule('Manager');
  290.         $lang_table $this->_getLangTable($langID);
  291.         if ($force{
  292.             //remove the whole table
  293.             ++$this->_queries;
  294.             return $this->db->manager->dropTable($lang_table);
  295.         }
  296.  
  297.         //drop only the column for this lang
  298.         $table_changes = array(
  299.             'remove' => array($this->_getLangCol($langID=> array())
  300.         );
  301.         ++$this->_queries;
  302.         return $this->db->manager->alterTable($lang_table$table_changesfalse);
  303.     }
  304.  
  305.     // }}}
  306.     // {{{ updateLang()
  307.  
  308.     /**
  309.      * Update the lang info in the langsAvail table
  310.      *
  311.      * @param array $langData language data
  312.      *
  313.      * @return true|PEAR_Error
  314.      */
  315.     function updateLang($langData)
  316.     {
  317.         $allFields = array(
  318.             //'lang_id'    => 'lang_id_col',
  319.             'name'       => 'lang_name_col',
  320.             'meta'       => 'lang_meta_col',
  321.             'error_text' => 'lang_errmsg_col',
  322.             'encoding'   => 'lang_encoding_col',
  323.         );
  324.         $updateFields array_keys($langData);
  325.         $langSet  = array();
  326.         foreach ($allFields as $field => $col{
  327.             if (in_array($field$updateFields)) {
  328.                 $langSet[$this->db->quoteIdentifier($this->options[$col]true' = ' .
  329.                              $this->db->quote($langData[$field]);
  330.             }
  331.         }
  332.         $query sprintf('UPDATE %s SET %s WHERE %s=%s',
  333.             $this->db->quoteIdentifier($this->options['langs_avail_table']true),
  334.             implode(', '$langSet),
  335.             $this->db->quoteIdentifier($this->options['lang_id_col']true),
  336.             $this->db->quote($langData['lang_id'])
  337.         );
  338.  
  339.         ++$this->_queries;
  340.         $res $this->db->exec($query);
  341.         $this->fetchLangs();  //update memory cache
  342.         if (PEAR::isError($res)) {
  343.             return $res;
  344.         }
  345.         return true;
  346.     }
  347.  
  348.     // }}}
  349.     // {{{ add()
  350.  
  351.     /**
  352.      * Add a new entry in the strings table.
  353.      *
  354.      * @param string $stringID    string ID
  355.      * @param string $pageID      page/group ID
  356.      * @param array  $stringArray Associative array with string translations.
  357.      *                Sample format: array('en' => 'sample', 'it' => 'esempio')
  358.      *
  359.      * @return true|PEAR_Error
  360.      */
  361.     function add($stringID$pageID$stringArray)
  362.     {
  363.         $langs array_intersect(
  364.             array_keys($stringArray),
  365.             $this->getLangs('ids')
  366.         );
  367.  
  368.         if (!count($langs)) {
  369.             //return error: no valid lang provided
  370.             return true;
  371.         }
  372.  
  373.         // Langs may be in different tables - we need to split up queries along
  374.         // table lines, so we can keep DB traffic to a minimum.
  375.  
  376.         $unquoted_stringID $stringID;
  377.         $unquoted_pageID   $pageID;
  378.         $stringID          $this->db->quote($stringID'text');
  379.         $pageID            is_null($pageID'NULL' $this->db->quote($pageID'text');
  380.         // Loop over the tables we need to insert into.
  381.         foreach ($this->_tableLangs($langsas $table => $tableLangs{
  382.             $exists $this->_recordExists($unquoted_stringID$unquoted_pageID$table);
  383.             if (PEAR::isError($exists)) {
  384.                 return $exists;
  385.             }
  386.             $func  $exists '_getUpdateQuery' '_getInsertQuery';
  387.             $query $this->$func($table$tableLangs$stringID$pageID$stringArray);
  388.             
  389.             ++$this->_queries;
  390.             $res $this->db->exec($query);
  391.             if (PEAR::isError($res)) {
  392.                 return $res;
  393.             }
  394.         }
  395.  
  396.         return true;
  397.     }
  398.  
  399.     // }}}
  400.     // {{{ update()
  401.  
  402.     /**
  403.      * Update an existing entry in the strings table.
  404.      *
  405.      * @param string $stringID    string ID
  406.      * @param string $pageID      page/group ID
  407.      * @param array  $stringArray Associative array with string translations.
  408.      *                Sample format: array('en' => 'sample', 'it' => 'esempio')
  409.      *
  410.      * @return true|PEAR_Error
  411.      */
  412.     function update($stringID$pageID$stringArray)
  413.     {
  414.         return $this->add($stringID$pageID$stringArray);
  415.     }
  416.  
  417.     // }}}
  418.     // {{{ _getInsertQuery()
  419.  
  420.     /**
  421.      * Build a SQL query to INSERT a record
  422.      *
  423.      * @param string $table        table name
  424.      * @param array  &$tableLangs  tables containing the languages
  425.      * @param string $stringID     string ID
  426.      * @param string $pageID       page/group ID
  427.      * @param array  &$stringArray array of strings
  428.      *
  429.      * @return string INSERT query
  430.      * @access private
  431.      */
  432.     function _getInsertQuery($table&$tableLangs$stringID$pageID&$stringArray)
  433.     {
  434.         $tableCols $this->_getLangCols($tableLangs);
  435.         $langData = array();
  436.         foreach ($tableLangs as $lang{
  437.             $langData[$lang$this->db->quote($stringArray[$lang]'text');
  438.         }
  439.         foreach (array_keys($tableColsas $k{
  440.             $tableCols[$k$this->db->quoteIdentifier($tableCols[$k]true);
  441.         }
  442.  
  443.         return sprintf('INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s)',
  444.             $this->db->quoteIdentifier($tabletrue),
  445.             $this->db->quoteIdentifier($this->options['string_id_col']true),
  446.             $this->db->quoteIdentifier($this->options['string_page_id_col']true),
  447.             implode(', '$tableCols),
  448.             $stringID,
  449.             $pageID,
  450.             implode(', '$langData)
  451.         );
  452.     }
  453.  
  454.     // }}}
  455.     // {{{ _getUpdateQuery()
  456.  
  457.     /**
  458.      * Build a SQL query to UPDATE a record
  459.      *
  460.      * @param string $table        table name
  461.      * @param array  &$tableLangs  tables containing the languages
  462.      * @param string $stringID     string ID
  463.      * @param string $pageID       page/group ID
  464.      * @param array  &$stringArray array of strings
  465.      *
  466.      * @return string UPDATE query
  467.      * @access private
  468.      */
  469.     function _getUpdateQuery($table&$tableLangs$stringID$pageID&$stringArray)
  470.     {
  471.         $tableCols $this->_getLangCols($tableLangs);
  472.         $langSet = array();
  473.         foreach ($tableLangs as $lang{
  474.             $langSet[$this->db->quoteIdentifier($tableCols[$lang]true' = ' .
  475.                          $this->db->quote($stringArray[$lang]'text');
  476.         }
  477.  
  478.         return sprintf('UPDATE %s SET %s WHERE %s = %s AND %s = %s',
  479.             $this->db->quoteIdentifier($tabletrue),
  480.             implode(', '$langSet),
  481.             $this->db->quoteIdentifier($this->options['string_id_col']true),
  482.             $stringID,
  483.             $this->db->quoteIdentifier($this->options['string_page_id_col']true),
  484.             $pageID
  485.         );
  486.     }
  487.  
  488.     // }}}
  489.     // {{{ remove()
  490.  
  491.     /**
  492.      * Remove an entry from the strings table.
  493.      *
  494.      * @param string $stringID string ID
  495.      * @param string $pageID   page/group ID
  496.      *
  497.      * @return mixed true on success, PEAR_Error on failure
  498.      */
  499.     function remove($stringID$pageID)
  500.     {
  501.         $tables array_unique($this->_getLangTables());
  502.  
  503.         $stringID $this->db->quote($stringID'text');
  504.         // get the tables and skip the non existent ones
  505.         $dbTables $this->_fetchTableNames();
  506.         foreach ($tables as $table{
  507.             if (!in_array($table$dbTables)) {
  508.                 continue;
  509.             }
  510.             $query sprintf('DELETE FROM %s WHERE %s = %s AND %s',
  511.                  $this->db->quoteIdentifier($tabletrue),
  512.                  $this->db->quoteIdentifier($this->options['string_id_col']true),
  513.                  $stringID,
  514.                  $this->db->quoteIdentifier($this->options['string_page_id_col']true)
  515.             );
  516.             if (is_null($pageID)) {
  517.                 $query .= ' IS NULL';
  518.             else {
  519.                 $query .= ' = ' $this->db->quote($pageID'text');
  520.             }
  521.  
  522.             ++$this->_queries;
  523.             $res $this->db->exec($query);
  524.             if (PEAR::isError($res)) {
  525.                 return $res;
  526.             }
  527.         }
  528.  
  529.         return true;
  530.     }
  531.  
  532.     // }}}
  533.     // {{{ removePage
  534.  
  535.     /**
  536.      * Remove all the strings in the given page/group
  537.      *
  538.      * @param string $pageID page/group ID
  539.      *
  540.      * @return mixed true on success, PEAR_Error on failu