http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 11 LOC: 287 Statements: 98

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


Report generated at 2007-11-05T09:09:02-05:00