http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 12 LOC: 308 Statements: 112

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


Report generated at 2008-03-05T18:27:43-05:00