http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 12 LOC: 288 Statements: 96
Legend: executednot executeddead code
Source file Statements Methods Total coverage
Translator.php 100.0% 100.0% 100.0%
 
1
<?php
2
3
/**
4
 * Xyster Framework
5
 *
6
 * This source file is subject to the new BSD license that is bundled
7
 * with this package in the file LICENSE.txt.
8
 * It is also available through the world-wide-web at this URL:
9
 * http://www.opensource.org/licenses/bsd-license.php
10
 *
11
 * @category  Xyster
12
 * @package   Xyster_Db
13
 * @copyright Copyright LibreWorks, LLC (http://libreworks.net)
14
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
15
 * @version   $Id: Translator.php 418 2010-10-18 21:40:08Z jonathanhawk $
16
 */
17
namespace Xyster\Db;
18
use Xyster\Data\Symbol\ISymbol;
19
use Xyster\Data\Symbol\Field;
20
use Xyster\Data\Symbol\Sort;
21
use Xyster\Data\Symbol\Criterion;
22
use Xyster\Data\Symbol\Expression;
23
use Xyster\Data\Symbol\Junction;
24
use Xyster\Data\Symbol\IClause;
25
/**
26
 * Translates objects in the Xyster_Data package into SQL fragments
27
 *
28
 * @category  Xyster
29
 * @package   Xyster_Db
30
 * @copyright Copyright LibreWorks, LLC (http://libreworks.net)
31
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
32
 */
33
class Translator
34
{
35
    const QM = '?';
36
    /**
37
     * The quote character for database identifiers
38
     *
39
     * @var string
40
     */
41
    protected $_identifierQuote = '"';
42
    /**
43
     * The callback for column renaming
44
     *
45
     * @var mixed
46
     */
47
    protected $_renameCallback;
48
    /**
49
     * A table name to prefix columns
50
     *
51
     * @var mixed
52
     */
53
    protected $_table;
54
55
    /**
56
     * Creates a new translator for a given SQL connection
57
     *
58
     * @param string $identifierQuote
59
     */
60
    public function __construct($identifierQuote = '"')
61
    {
62 11
        if ( strlen(trim($identifierQuote)) ) {
63 11
            $this->_identifierQuote = $identifierQuote;
64 11
        }
65
    }
66
67
    /**
68
     * Sets the callback for column renaming
69
     *
70
     * This can be any valid PHP callback.  It's passed the column object.
71
     *
72
     * @param mixed $callback
73
     * @return Translator  Provides a fluent interface
74
     */
75
    public function setRenameCallback($callback)
76
    {
77 4
        if (!is_callable($callback)) {
78 1
            throw new DbException('This is not a valid callback');
79
        }
80 3
        $this->_renameCallback = $callback;
81 3
        return $this;
82
    }
83
84
    /**
85
     * Sets a table name to prefix to columns
86
     *
87
     * @param string $table
88
     * @return Translator  Provides a fluent interface
89
     */
90
    public function setTable($table)
91
    {
92 3
        $this->_table = $table;
93 3
        return $this;
94
    }
95
96
    /**
97
     * Translates one of the Xyster Data objects into a SQL token
98
     *
99
     * @param ISymbol $object
100
     * @param boolean $quote Whether to quote field names
101
     * @return Token
102
     */
103
    public function translate(ISymbol $object, $quote = true)
104
    {
105 7
        if ($object instanceof Field) {
106 3
            return $this->translateField($object, $quote);
107 4
        } else if ($object instanceof Sort) {
108 2
            return $this->translateSort($object, $quote);
109 3
        } else if ($object instanceof Criterion) {
110 2
            return $this->translateCriterion($object, $quote);
111 1
        } else if ($object instanceof IClause) {
112 1
            return $this->translateClause($object, $quote);
113
        }
114
    }
115
116
    /**
117
     * Translates a clause
118
     *
119
     * @param IClause $clause
120
     * @param boolean $quote
121
     * @return Token
122
     */
123
    public function translateClause(IClause $clause, $quote = true)
124
    {
125 2
        $translated = null;
126 2
        if ($clause instanceof Junction) {
127 1
            $translated = $this->translateJunction($clause, $quote);
128 1
        } else {
129 1
            $sql = array();
130 1
            $binds = array();
131 1
            foreach ($clause as $k => $symbol) {
132 1
                $token = $this->translate($symbol, $quote);
133 1
                $sql[] = $token->getSql();
134 1
                $binds += $token->getBindValues();
135 1
            }
136 1
            $translated = new Token(implode(', ', $sql), $binds);
137
        }
138 2
        return $translated;
139
    }
140
141
    /**
142
     * Converts a field to SQL
143
     *
144
     * @param Field $tosql  The field to translate
145
     * @param boolean $quote Whether to quote field names
146
     * @return Token  The translated SQL syntax
147
     */
148
    public function translateField(Field $tosql, $quote = true)
149
    {
150 8
        $rename = $this->_getRenamedField($tosql);
151 8
        $tableName = $this->_getTableName($tosql);
152
153 8
        $q = $this->_identifierQuote;
154 8
        $field = ( $quote ) ? $q . str_replace($q, $q . $q, $rename) . $q : $rename;
155 8
        if ($tableName) {
156 2
            $field = "$tableName.$field";
157 2
        }
158
159 8
        return new Token($tosql instanceof \Xyster\Data\Symbol\AggregateField ?
160 8
                        $tosql->getFunction()->getValue() . '(' . $field . ')' : $field);
161
    }
162
163
    /**
164
     * Converts a sort to SQL
165
     *
166
     * @param Sort $tosql  The Sort to translate
167
     * @param boolean $quote Whether to quote field names
168
     * @return Token  The translated SQL syntax
169
     */
170
    public function translateSort(Sort $tosql, $quote = true)
171
    {
172 2
        return new Token($this->translateField($tosql->getField(),
173 2
                $quote)->getSql() . " " . $tosql->getDirection());
174
    }
175
176
    /**
177
     * Converts a criterion to SQL
178
     *
179
     * @param Criterion $tosql  The Criterion to translate
180
     * @param boolean $quote Whether to quote field names
181
     * @return Token  The translated SQL syntax
182
     */
183
    public function translateCriterion(Criterion $tosql, $quote = true)
184
    {
185 3
        $token = null;
186 3
        if ($tosql instanceof Expression) {
187 3
            $token = $this->translateExpression($tosql, $quote);
188 3
        } else if ($tosql instanceof Junction) {
189 1
            $token = $this->translateJunction($tosql, $quote);
190 1
        }
191 3
        return $token;
192
    }
193
194
    /**
195
     * Converts a Junction to SQL
196
     *
197
     * @param Junction $tosql  The Junction to translate
198
     * @param boolean $quote Whether to quote field names
199
     * @return Token  The translated SQL syntax
200
     */
201
    public function translateJunction(Junction $tosql, $quote = true)
202
    {
203 2
        $criteria = array();
204 2
        foreach ($tosql->getCriteria() as $v) {
205 2
            $loopToken = $this->translateCriterion($v, $quote);
206 2
            $criteria[$loopToken->getSql()] = $loopToken;
207 2
        }
208 2
        $token = new Token("( " .
209 2
                        implode(" " . $tosql->getOperator() . " ", array_keys($criteria)) .
210 2
                        " )");
211 2
        foreach ($criteria as $v) {
212 2
            $token->addBindValues($v);
213 2
        }
214 2
        return $token;
215
    }
216
217
    /**
218
     * Converts an expression to SQL
219
     *
220
     * @param Expression $tosql  The Expression to translate
221
     * @param boolean $quote Whether to quote field names
222
     * @return Token The translated SQL syntax
223
     */
224
    public function translateExpression(Expression $tosql, $quote = true)
225
    {
226 3
        $binds = array();
227
228 3
        $sql = $this->translateField($tosql->getLeft(), $quote)->getSql() . ' ';
229 3
        $val = $tosql->getRight();
230 3
        $operator = $tosql->getOperator()->getValue();
231 3
        if ($val === null || $val == "NULL") {
232 2
            $sql .= ( $operator == '=' ) ? 'IS' : 'IS NOT';
233 2
        } else {
234 3
            $sql .= $operator;
235
        }
236 3
        $sql .= ' ';
237
238 3
        if ($val == "NULL" || $val === null) {
239 2
            $sql .= 'NULL';
240 3
        } else if ($val instanceof Field) {
241 2
            $sql .= $this->translateField($val, $quote)->getSql();
242 2
        } else {
243 3
            if (is_array($val)) {
244 1
                if (substr($operator, -7) == 'BETWEEN') {
245 1
                    $sql .= self::QM . " AND " . self::QM;
246 1
                    $binds = array($val[0], $val[1]);
247 1
                } else if (substr($operator, -2) == 'IN') {
248 1
                    $binds = array_values($val);
249 1
                    $sql .= '(' . implode(',', array_fill(0, count($val), self::QM)) . ')';
250 1
                }
251 1
            } else {
252 2
                $sql .= self::QM;
253 2
                $binds[] = $val;
254
            }
255
        }
256 3
        return new Token($sql, $binds);
257
    }
258
259
    /**
260
     * Gets the renamed value of the field if appropriate
261
     *
262
     * This can be overridden to provide a custom renaming strategy
263
     *
264
     * @param Field $field
265
     * @return string
266
     */
267
    protected function _getRenamedField(Field $field)
268
    {
269 8
        $rename = $field->getName();
270 8
        if ($this->_renameCallback !== null) {
271 2
            $rename = call_user_func($this->_renameCallback, $rename);
272 2
        }
273 8
        return $rename;
274
    }
275
276
    /**
277
     * Gets the name of the table to use to prefix columns
278
     *
279
     * This can be extended to provide a custom table name
280
     *
281
     * @param Field $field
282
     * @return string
283
     */
284
    protected function _getTableName(Field $field)
285
    {
286 8
        return $this->_table;
287
    }
288 1
}


Report generated at 2010-10-18T17:19:48-04:00