http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 11 LOC: 282 Statements: 98

Source file Statements Methods Total coverage
Translator.php 87.8% 100.0% 89.0%
   
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 202 2008-01-20 16:20:09Z 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 29
		$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 20
		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 19
		$this->_renameCallback = $callback;
74 19
		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 9
		$this->_table = $table;
86 9
		return $this;
87
	}
88
89
	/**
90
	 * Translates one of the Xyster Data objects into a SQL token
91
	 *
92
	 * @param mixed $object
93
     * @param boolean $quote Whether to quote field names
94
	 * @return Xyster_Db_Token
95
	 */
96
	public function translate( $object, $quote = true )
97
	{
98 23
		if ( $object instanceof Xyster_Data_Field ) {
99 3
			return $this->translateField($object, $quote);
100 20
		} else if ( $object instanceof Xyster_Data_Sort ) {
101 1
			return $this->translateSort($object, $quote);
102 19
		} else if ( $object instanceof Xyster_Data_Criterion ) {
103 18
			return $this->translateCriterion($object, $quote);
104 0
		}
105 1
		require_once 'Xyster/Db/Exception.php';
106 1
		throw new Xyster_Db_Exception('Invalid object');
107
	}
108
109
	/**
110
	 * Converts a field to SQL
111
	 *
112
	 * @param Xyster_Data_Field $tosql  The field to translate
113
     * @param boolean $quote Whether to quote field names
114
	 * @return Xyster_Db_Token  The translated SQL syntax
115
	 */
116
	public function translateField( Xyster_Data_Field $tosql, $quote = true )
117
	{
118 24
		$rename = $this->_getRenamedField($tosql);
119 24
		$tableName = $this->_getTableName($tosql);
120
121 24
		$field = ( $quote ) ? $this->_adapter->quoteIdentifier($rename) : $rename;
122 24
		if ( $tableName ) {
123 10
			$field = "$tableName.$field";
124 10
		}
125
126 24
		return new Xyster_Db_Token( $tosql instanceof Xyster_Data_Field_Aggregate ?
127 24
				$tosql->getFunction()->getValue().'('.$field.')' : $field );
128
	}
129
130
	/**
131
	 * Converts a sort to SQL
132
	 *
133
	 * @param Xyster_Data_Sort $tosql  The Sort to translate
134
     * @param boolean $quote Whether to quote field names
135
	 * @return Xyster_Db_Token  The translated SQL syntax
136
	 */
137
	public function translateSort( Xyster_Data_Sort $tosql, $quote = true )
138
	{
139 4
		return new Xyster_Db_Token(
140 4
		    $this->translateField($tosql->getField(), $quote)->getSql() . " "
141 4
			. $tosql->getDirection() );
142
	}
143
144
	/**
145
	 * Converts a criterion to SQL
146
	 *
147
	 * @param Xyster_Data_Criterion $tosql  The Criterion to translate
148
     * @param boolean $quote Whether to quote field names
149
	 * @return Xyster_Db_Token  The translated SQL syntax
150
	 */
151
	public function translateCriterion( Xyster_Data_Criterion $tosql, $quote = true )
152
	{
153 20
	    $token = null;
154 20
		if ( $tosql instanceof Xyster_Data_Expression ) {
155 20
			$token = $this->translateExpression($tosql, $quote);
156 20
		} else if ( $tosql instanceof Xyster_Data_Junction ) {
157 7
			$token = $this->translateJunction($tosql, $quote);
158 7
		}
159 20
		return $token;
160
	}
161
162
	/**
163
	 * Converts a Junction to SQL
164
	 *
165
	 * @param Xyster_Data_Junction $tosql  The Junction to translate
166
     * @param boolean $quote Whether to quote field names
167
	 * @return Xyster_Db_Token  The translated SQL syntax
168
	 */
169
	public function translateJunction( Xyster_Data_Junction $tosql, $quote = true )
170
	{
171 7
		$criteria = array();
172 7
		foreach( $tosql->getCriteria() as $v ) {
173 7
			$loopToken = $this->translateCriterion($v, $quote);
174 7
			$criteria[$loopToken->getSql()] = $loopToken;
175 7
		}
176 7
		$token = new Xyster_Db_Token( "( " .
177 7
			implode( " ".$tosql->getOperator()." ", array_keys($criteria) ) .
178 7
			" )" );
179 7
		foreach( $criteria as $v ) {
180 7
			$token->addBindValues( $v );
181 7
		}
182 7
		return $token;
183
	}
184
185
	/**
186
	 * Converts an expression to SQL
187
	 *
188
	 * @param Xyster_Data_Expression $tosql  The Expression to translate
189
	 * @param boolean $quote Whether to quote field names
190
	 * @return Xyster_Db_Token  The translated SQL syntax
191
	 */
192
	public function translateExpression( Xyster_Data_Expression $tosql, $quote = true )
193
	{
194 20
		$binds = array();
195
196 20
		$sql = $this->translateField($tosql->getLeft(), $quote)->getSql() . ' ';
197 20
		$val = $tosql->getRight();
198 20
		if ( $val === null || $val == "NULL" ) {
199 5
		    $sql .= ( $tosql->getOperator() == '=' ) ? 'IS' : 'IS NOT';
200 5
		} else {
201 18
		    $sql .= $tosql->getOperator();
202
		}
203 20
		$sql .= ' ';
204
205 20
		if ( $val == "NULL" || $val === null ) {
206 5
			$sql .= 'NULL';
207 20
		} else if ( $val instanceof Xyster_Data_Field ) {
208 1
			$sql .= $this->translateField($val, $quote)->getSql();
209 1
		} else {
210
211 18
			$bindName = ':P'.str_pad(dechex(crc32((string)$tosql)), 8, '0', STR_PAD_LEFT);
212
213 18
			if ( is_array($val) ) {
214
215 1
				if ( substr($tosql->getOperator(),-7) == 'BETWEEN' ) {
216 1
					$sql .= "{$bindName}1 AND {$bindName}2";
217 1
					$binds[$bindName.'1'] = $val[0];
218 1
					$binds[$bindName.'2'] = $val[1];
219 1
				} else if ( substr($tosql->getOperator(),-2) == 'IN' ) {
220 1
					$quoted = array();
221 1
					foreach( $val as $k=>$v ) {
222 1
						$quoted[] = $bindName.$k;
223 1
						$binds[$bindName.$k] = $v;
224 1
					}
225 1
					$sql .= '('. implode(',',$quoted) . ')';
226 1
				}
227
228 1
			} else {
229 17
				$sql .= $bindName;
230 17
				$binds[$bindName] = $val;
231
			}
232
233
		}
234
235 20
        if ( $this->_adapter instanceof Zend_Db_Adapter_Mysqli ) {
236
            // mysqli has no support for :name style binding, we must replace
237
            // bind values with '?'
238 0
            $matches = array();
239
240 0
            if ( preg_match_all('/:P[a-z0-9]+/', $sql, $matches, PREG_SET_ORDER) ) {
241 0
                $newBinds = array();
242 0
                foreach( $matches as $match ) {
243 0
                    $sql = str_replace($match[0], '?', $sql);
244 0
                    $newBinds[] = $binds[$match[0]];
245 0
                }
246 0
                $binds = $newBinds;
247 0
            }
248 0
        }
249
250 20
		return new Xyster_Db_Token($sql, $binds);
251
	}
252
253
	/**
254
	 * Gets the renamed value of the field if appropriate
255
	 *
256
	 * This can be overridden to provide a custom renaming strategy
257
	 *
258
	 * @param Xyster_Data_Field $field
259
	 * @return string
260
	 */
261
	protected function _getRenamedField( Xyster_Data_Field $field )
262
	{
263 22
	    $rename = $field->getName();
264 22
		if ( $this->_renameCallback !== null ) {
265 18
			$rename = call_user_func($this->_renameCallback, $rename);
266 18
		}
267 22
		return $rename;
268
	}
269
270
	/**
271
	 * Gets the name of the table to use to prefix columns
272
	 *
273
	 * This can be extended to provide a custom table name
274
	 *
275
	 * @param Xyster_Data_Field $field
276
	 * @return string
277
	 */
278
	protected function _getTableName( Xyster_Data_Field $field )
279
	{
280 22
	    return $this->_table;
281
	}
282
}


Report generated at 2008-01-20T12:13:39-05:00