1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4/**
5 * Contains the Translation2_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-2008 Lorenzo Alberton
34 * @license   http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
35 * @version   CVS: $Id: mdb2.php 305985 2010-12-05 22:55:33Z clockwerx $
36 * @link      http://pear.php.net/package/Translation2
37 */
38
39/**
40 * require Translation2_Container class
41 */
42require_once 'Translation2/Container.php';
43
44/**
45 * Storage driver for fetching data from a database
46 *
47 * This storage driver can use all databases which are supported
48 * by the PEAR::MDB2 abstraction layer to fetch data.
49 *
50 * @category  Internationalization
51 * @package   Translation2
52 * @author    Lorenzo Alberton <l.alberton@quipo.it>
53 * @copyright 2004-2008 Lorenzo Alberton
54 * @license   http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
55 * @version   CVS: $Id: mdb2.php 305985 2010-12-05 22:55:33Z clockwerx $
56 * @link      http://pear.php.net/package/Translation2
57 */
58class Translation2_Container_mdb2 extends Translation2_Container
59{
60    // {{{ class vars
61
62    /**
63     * MDB2 object
64     * @var object
65     */
66    var $db = null;
67
68    /**
69     * query counter
70     * @var integer
71     * @access private
72     */
73    var $_queries = 0;
74
75    // }}}
76    // {{{ init
77
78    /**
79     * Initialize the container
80     *
81     * @param string &$db Connection data or MDB2 object
82     *
83     * @return boolean|PEAR_Error object if something went wrong
84     */
85    function init(&$db)
86    {
87        $this->_setDefaultOptions();
88        if (PEAR::isError($err = $this->_connect($db))) {
89            return $err;
90        }
91        return true;
92    }
93
94    // }}}
95    // {{{ _connect()
96
97    /**
98     * Connect to database by using the given DSN string
99     *
100     * @param mixed &$db DSN string | array | MDB2 object
101     *
102     * @return boolean|PEAR_Error on error
103     * @access private
104     */
105    function _connect(&$db)
106    {
107        if (is_object($db) && is_a($db, 'MDB2_Driver_Common')) {
108            $this->db = &$db;
109        } elseif (is_string($db) || is_array($db)) {
110            include_once 'MDB2.php';
111            $this->db =& MDB2::connect($db);
112        } elseif (is_object($db) && MDB2::isError($db)) {
113            return PEAR::raiseError($db->getMessage(), $db->code);
114        } else {
115            return PEAR::raiseError('The given dsn was not valid in file '
116                                    . __FILE__ . ' at line ' . __LINE__,
117                                    TRANSLATION2_ERROR_CANNOT_CONNECT,
118                                    PEAR_ERROR_RETURN);
119        }
120
121        if (PEAR::isError($this->db)) {
122            return $this->db;
123        }
124        return true;
125    }
126
127    // }}}
128    // {{{ _setDefaultOptions()
129
130    /**
131     * Set some default options
132     *
133     * @return void
134     * @access private
135     */
136    function _setDefaultOptions()
137    {
138        $this->options['langs_avail_table'] = 'langs';
139        $this->options['lang_id_col']       = 'id';
140        $this->options['lang_name_col']     = 'name';
141        $this->options['lang_meta_col']     = 'meta';
142        $this->options['lang_errmsg_col']   = 'error_text';
143        $this->options['lang_encoding_col'] = 'encoding';
144
145        $this->options['strings_default_table'] = 'i18n';
146        $this->options['strings_tables']        = array(); // 'lang_id' => 'table_name'
147        $this->options['string_id_col']         = 'id';
148        $this->options['string_page_id_col']    = 'page_id';
149        $this->options['string_page_id_col_length'] = 50;
150        $this->options['string_text_col']       = '%s'; // col_name if one table per lang is used,
151                                                        // or a pattern (i.e. "tr_%s" => "tr_EN_US")
152    }
153
154    // }}}
155    // {{{ setCharset()
156
157    /**
158     * Set charset used to read/store the translations
159     *
160     * @param string $charset character set (encoding)
161     *
162     * @return PEAR_Error on error
163     */
164    function setCharset($charset)
165    {
166        return $this->db->setCharset($charset);
167    }
168
169    // }}}
170    // {{{ fetchLangs()
171
172    /**
173     * Fetch the available langs if they're not cached yet.
174     *
175     * @return PEAR_Error on error
176     */
177    function fetchLangs()
178    {
179        $query = sprintf('SELECT %s AS id, %s AS name, %s AS meta, %s AS error_text, %s AS encoding FROM %s',
180            $this->db->quoteIdentifier($this->options['lang_id_col'], true),
181            $this->db->quoteIdentifier($this->options['lang_name_col'], true),
182            $this->db->quoteIdentifier($this->options['lang_meta_col'], true),
183            $this->db->quoteIdentifier($this->options['lang_errmsg_col'], true),
184            $this->db->quoteIdentifier($this->options['lang_encoding_col'], true),
185            $this->db->quoteIdentifier($this->options['langs_avail_table'], true)
186        );
187
188        ++$this->_queries;
189        $res = $this->db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
190        if (PEAR::isError($res)) {
191            return $res;
192        }
193        foreach ($res as $row) {
194            $row = array_change_key_case($row, CASE_LOWER);
195            $this->langs[$row['id']] = $row;
196        }
197    }
198
199    // }}}
200    // {{{ getPage()
201
202    /**
203     * Returns an array of the strings in the selected page
204     *
205     * @param string $pageID page/group ID
206     * @param string $langID language ID
207     *
208     * @return array
209     */
210    function getPage($pageID = null, $langID = null)
211    {
212        $langID   = $this->_getLangID($langID);
213        if (PEAR::isError($langID)) {
214            return $langID;
215        }
216        $lang_col = $this->_getLangCol($langID);
217        $table    = $this->_getLangTable($langID);
218
219        $query = sprintf('SELECT %s, %s FROM %s WHERE %s ',
220             $this->db->quoteIdentifier($this->options['string_id_col'], true),
221             $this->db->quoteIdentifier($lang_col, true),
222             $this->db->quoteIdentifier($table, true),
223             $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
224        );
225
226        if (is_null($pageID)) {
227            $query .= 'IS NULL';
228        } else {
229            $query .= ' = ' . $this->db->quote($pageID, 'text');
230        }
231
232        ++$this->_queries;
233        $res = $this->db->query($query);
234        if (PEAR::isError($res)) {
235            return $res;
236        }
237
238        $strings = array();
239        while (list($key, $value) = $res->fetchRow(MDB2_FETCHMODE_ORDERED)) {
240            $strings[$key] = $value;
241        }
242        $res->free();
243        return $strings;
244    }
245
246    // }}}
247    // {{{ getOne()
248
249    /**
250     * Get a single item from the container
251     *
252     * @param string $stringID string ID
253     * @param string $pageID   page/group ID
254     * @param string $langID   language ID
255     *
256     * @return string
257     */
258    function getOne($stringID, $pageID = null, $langID = null)
259    {
260        $langID   = $this->_getLangID($langID);
261        if (PEAR::isError($langID)) {
262            return $langID;
263        }
264        $lang_col = $this->_getLangCol($langID);
265        $table    = $this->_getLangTable($langID);
266
267        $query = sprintf('SELECT %s FROM %s WHERE %s = %s AND %s',
268             $this->db->quoteIdentifier($lang_col, true),
269             $this->db->quoteIdentifier($table, true),
270             $this->db->quoteIdentifier($this->options['string_id_col'], true),
271             $this->db->quote($stringID, 'text'),
272             $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
273        );
274
275        if (is_null($pageID)) {
276            $query .= ' IS NULL';
277        } else {
278            $query .= ' = ' . $this->db->quote($pageID, 'text');
279        }
280
281        ++$this->_queries;
282        return $this->db->queryOne($query);
283    }
284
285    // }}}
286    // {{{ getStringID()
287
288    /**
289     * Get the stringID for the given string
290     *
291     * @param string $string string
292     * @param string $pageID page/group ID
293     *
294     * @return string
295     */
296    function getStringID($string, $pageID = null)
297    {
298        $lang_col = $this->_getLangCol($this->currentLang['id']);
299        $table = $this->_getLangTable($this->currentLang['id']);
300        $query = sprintf('SELECT %s FROM %s WHERE %s = %s AND %s',
301             $this->db->quoteIdentifier($this->options['string_id_col'], true),
302             $this->db->quoteIdentifier($table, true),
303             $this->db->quoteIdentifier($lang_col, true),
304             $this->db->quote($string, 'text'),
305             $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
306        );
307        if (is_null($pageID)) {
308            $query .= ' IS NULL';
309        } else {
310            $query .= ' = ' . $this->db->quote($pageID, 'text');
311        }
312        ++$this->_queries;
313        return $this->db->queryOne($query);
314    }
315
316    // }}}
317    // {{{ _getLangTable()
318
319    /**
320     * Get the table a language is stored in
321     *
322     * @param string $langID language ID
323     *
324     * @return string table $langID is stored in
325     * @access private
326     */
327    function _getLangTable($langID)
328    {
329        if (isset($this->options['strings_tables'][$langID])) {
330            return $this->options['strings_tables'][$langID];
331        }
332        return str_replace('%s', $langID, $this->options['strings_default_table']);
333    }
334
335    // }}}
336    // {{{ _getLangCol()
337
338    /**
339     * Get the column a language's string is stored in
340     *
341     * @param string $langID language ID
342     *
343     * @return string column $langID is stored in
344     * @access private
345     */
346    function _getLangCol($langID)
347    {
348        static $cols;
349        if (!isset($cols[$langID])) {
350            if (isset($this->options['string_text_col']) &&
351                !empty($this->options['string_text_col'])) {
352                $cols[$langID] = str_replace('%s', $langID, $this->options['string_text_col']);
353            } else {
354                $cols[$langID] = $langID;
355            }
356        }
357        return $cols[$langID];
358    }
359
360    // }}}
361}
362?>