Database
[ ]
[ ]
[ ]
[ ]
[ ]
Source for file query.php
Documentation is available at query.php
1. <?php
2. /**
3. * File containing the ezcQuery class.
4. *
5. * @package Database
6. * @version 1.4
7. * @copyright Copyright (C) 2005-2008 eZ systems as. All rights reserved.
8. * @license http://ez.no/licenses/new_bsd New BSD License
9. */
10.
11. /**
12. * The ezcQuery class provides the common API for all Query objects.
13. *
14. * ezcQuery has three main purposes:
15. * - it provides a common API for building queries through the getQuery() method.
16. * - it provides a common API for binding parameters to queries through
17. * bindValue() and bindParam()
18. * - it provides internal aliasing functionality that allows you to use
19. * aliases for table and column names. The substitution is done inside of the
20. * query classes before the query itself is built.
21. *
22. * Through the bind methods you can bind parameters and values to your
23. * query. Finally you can use prepare to get a PDOStatement object
24. * from your query object.
25. *
26. * Subclasses should provide functionality to build an actual query.
27. *
28. * @package Database
29. * @version 1.4
30. */
31. abstract class ezcQuery
32. {
33. /**
34. * A pointer to the database handler to use for this query.
35. *
36. * @var PDO
37. */
38. protected $db;
39.
40. /**
41. * The column and table name aliases.
42. *
43. * Format: array('alias' => 'realName')
44. * @var array(string=>string)
45. */
46. private $aliases = null;
47.
48. /**
49. * Counter used to create unique ids in the bind methods.
50. *
51. * @var int
52. */
53. private $boundCounter = 0;
54.
55. /**
56. * Stores the list of parameters that will be bound with doBind().
57. *
58. * Format: array( ':name' => &mixed )
59. * @var array(string=>&mixed)
60. */
61. private $boundParameters = array();
62.
63. /**
64. * Stores the type of a value which will we used when the value is bound.
65. *
66. * @var array(string=>int)
67. */
68. private $boundParametersType = array();
69.
70. /**
71. * Stores the list of values that will be bound with doBind().
72. *
73. * Format: array( ':name' => mixed )
74. * @var array(string=>mixed)
75. */
76. private $boundValues = array();
77.
78. /**
79. * Stores the type of a value which will we used when the value is bound.
80. *
81. * @var array(string=>int)
82. */
83. private $boundValuesType = array();
84.
85. /**
86. * The expression object for this class.
87. *
88. * @var ezcQueryExpression
89. */
90. public $expr = null;
91.
92. /**
93. * Constructs a new ezcQuery that works on the database $db and with the aliases $aliases.
94. *
95. * The aliases can be used to substitute the column and table names with more
96. * friendly names. E.g PersistentObject uses it to allow using property and class
97. * names instead of column and table names.
98. *
99. * @param PDO $db
100. * @param array(string=>string) $aliases
101. */
102. public function __construct( PDO $db, array $aliases = array() )
103. {
104. $this->db = $db;
105. if ( $this->expr == null )
106. {
107. $this->expr = $db->createExpression();
108. }
109. if ( !empty( $aliases ) )
110. {
111. $this->aliases = $aliases;
112. $this->expr->setAliases( $this->aliases );
113. }
114. }
115.
116. /**
117. * Sets the aliases $aliases for this object.
118. *
119. * The aliases should be in the form array( "aliasName" => "databaseName" )
120. * Each alias defines a relation between a user-defined name and a name
121. * in the database. This is supported for table names as column names.
122. *
123. * The aliases can be used to substitute the column and table names with more
124. * friendly names. The substitution is done when the query is built, not using
125. * AS statements in the database itself.
126. *
127. * Example of a select query with aliases:
128. * <code>
129. * <?php
130. * $q->setAliases( array( 'Identifier' => 'id', 'Company' => 'company' ) );
131. * $q->select( 'Company' )
132. * ->from( 'table' )
133. * ->where( $q->expr->eq( 'Identifier', 5 ) );
134. * echo $q->getQuery();
135. * ?>
136. * </code>
137. *
138. * This example will output SQL similar to:
139. * <code>
140. * SELECT company FROM table WHERE id = 5
141. * </code>
142. *
143. * Aliasses also take effect for composite names in the form
144. * tablename.columnname as the following example shows:
145. * <code>
146. * <?php
147. * $q->setAliases( array( 'Order' => 'orders', 'Recipient' => 'company' ) );
148. * $q->select( 'Order.Recipient' )
149. * ->from( 'Order' );
150. * echo $q->getQuery();
151. * ?>
152. * </code>
153. *
154. * This example will output SQL similar to:
155. * <code>
156. * SELECT orders.company FROM orders;
157. * </code>
158. *
159. * It is even possible to have an alias point to a table name/column name
160. * combination. This will only work for alias names without a . (dot):
161. * <code>
162. * <?php
163. * $q->setAliases( array( 'Order' => 'orders', 'Recipient' => 'orders.company' ) );
164. * $q->select( 'Recipient' )
165. * ->from( 'Order' );
166. * echo $q->getQuery();
167. * ?>
168. * </code>
169. *
170. * This example will output SQL similar to:
171. * <code>
172. * SELECT orders.company FROM orders;
173. * </code>
174. *
175. * In the following example, the Recipient alias will not be used, as it is
176. * points to a fully qualified name - the Order alias however is used:
177. * <code>
178. * <?php
179. * $q->setAliases( array( 'Order' => 'orders', 'Recipient' => 'orders.company' ) );
180. * $q->select( 'Order.Recipient' )
181. * ->from( 'Order' );
182. * echo $q->getQuery();
183. * ?>
184. * </code>
185. *
186. * This example will output SQL similar to:
187. * <code>
188. * SELECT orders.Recipient FROM orders;
189. * </code>
190. *
191. * @param array(string=>string) $aliases
192. * @return void
193. */
194. public function setAliases( array $aliases )
195. {
196. $this->aliases = $aliases;
197. $this->expr->setAliases( $aliases );
198. }
199.
200. /**
201. * Returns true if this object has aliases.
202. *
203. * @return bool
204. */
205. public function hasAliases()
206. {
207. return $this->aliases !== null ? true : false;
208. }
209.
210. /**
211. * Returns the correct identifier for the alias $alias.
212. *
213. * If the alias does not exists in the list of aliases
214. * it is returned unchanged.
215. *
216. * This can method handles composite identifiers separated by a dot ('.').
217. *
218. * @param string $alias
219. * @return string
220. */
221. protected function getIdentifier( $alias )
222. {
223. $aliasParts = explode( '.', $alias );
224. $identifiers = array();
225. // If the alias consists of one part, then we just look it up in the
226. // array. If we find it, we use it, otherwise we return the name as-is
227. // and assume it's just a column name. The alias target can be a fully
228. // qualified name (table.column).
229. if ( count( $aliasParts ) == 1 )
230. {
231. if ( $this->aliases !== null &&
232. array_key_exists( $alias, $this->aliases ) )
233. {
234. $alias = $this->aliases[$alias];
235. }
236. return $alias;
237. }
238. // If the passed name consist of two parts, we need to check all parts
239. // of the passed-in name for aliases, because an alias can be made for
240. // both a table name and a column name. For each element we try to find
241. // whether we have an alias mapping. Unlike the above case, the alias
242. // target can in this case *not* consist of a fully qualified name as
243. // this would introduce another part of the name (with two dots).
244. for ( $i = 0; $i < count( $aliasParts ); $i++ )
245. {
246. if ( $this->aliases !== null &&
247. array_key_exists( $aliasParts[$i], $this->aliases ) )
248. {
249. // We only use the found alias if the alias target is not a fully
250. // qualified name (table.column).
251. $tmpAlias = $this->aliases[$aliasParts[$i]];
252. if ( count( explode( '.', $tmpAlias ) ) === 1 )
253. {
254. $aliasParts[$i] = $this->aliases[$aliasParts[$i]];
255. }
256. }
257. }
258. $alias = join( '.', $aliasParts );
259. return $alias;
260. }
261.
262. /**
263. * Returns the correct identifiers for the aliases found in $aliases.
264. *
265. * This method is similar to getIdentifier except that it works on an array.
266. *
267. * @param array(string) $aliasList
268. * @return array(string)
269. */
270. protected function getIdentifiers( array $aliasList )
271. {
272. if ( $this->aliases !== null )
273. {
274. foreach ( $aliasList as $key => $alias )
275. {
276. $aliasList[$key] = $this->getIdentifier( $alias );
277. }
278. }
279. return $aliasList;
280. }
281.
282. /**
283. * Binds the value $value to the specified variable name $placeHolder.
284. *
285. * This method provides a shortcut for PDOStatement::bindValue
286. * when using prepared statements.
287. *
288. * The parameter $value specifies the value that you want to bind. If
289. * $placeholder is not provided bindValue() will automatically create a
290. * placeholder for you. An automatic placeholder will be of the name
291. * 'ezcValue1', 'ezcValue2' etc.
292. *
293. * For more information see {@link http://php.net/pdostatement-bindparam}
294. *
295. * Example:
296. * <code>
297. * $value = 2;
298. * $q->eq( 'id', $q->bindValue( $value ) );
299. * $stmt = $q->prepare(); // the value 2 is bound to the query.
300. * $value = 4;
301. * $stmt->execute(); // executed with 'id = 2'
302. * </code>
303. *
304. * @see doBind()
305. * @param mixed $value
306. * @param string $placeHolder the name to bind with. The string must start with a colon ':'.
307. * @return string the placeholder name used.
308. */
309. public function bindValue( $value, $placeHolder = null, $type = PDO::PARAM_STR )
310. {
311. if ( $placeHolder === null )
312. {
313. $this->boundCounter++;
314. $placeHolder = ":ezcValue{$this->boundCounter}";
315. }
316.
317. $this->boundValues[$placeHolder] = $value;
318. $this->boundValuesType[$placeHolder] = $type;
319.
320. return $placeHolder;
321. }
322.
323. /**
324. * Binds the parameter $param to the specified variable name $placeHolder..
325. *
326. * This method provides a shortcut for PDOStatement::bindParam
327. * when using prepared statements.
328. *
329. * The parameter $param specifies the variable that you want to bind. If
330. * $placeholder is not provided bind() will automatically create a
331. * placeholder for you. An automatic placeholder will be of the name
332. * 'ezcValue1', 'ezcValue2' etc.
333. *
334. * For more information see {@link http://php.net/pdostatement-bindparam}
335. *
336. * Example:
337. * <code>
338. * $value = 2;
339. * $q->eq( 'id', $q->bindParam( $value ) );
340. * $stmt = $q->prepare(); // the parameter $value is bound to the query.
341. * $value = 4;
342. * $stmt->execute(); // executed with 'id = 4'
343. * </code>
344. *
345. * @see doBind()
346. * @param &mixed $param
347. * @param string $placeHolder the name to bind with. The string must start with a colon ':'.
348. * @return string the placeholder name used.
349. */
350. public function bindParam( &$param, $placeHolder = null, $type = PDO::PARAM_STR )
351. {
352. if ( $placeHolder === null )
353. {
354. $this->boundCounter++;
355. $placeHolder = ":ezcValue{$this->boundCounter}";
356. }
357.
358. $this->boundParameters[$placeHolder] =& $param;
359. $this->boundParametersType[$placeHolder] = $type;
360.
361. return $placeHolder;
362. }
363.
364. /**
365. * Resets the bound values and parameters to empty.
366. *
367. * This is useful if your query can be reset and used multiple times.
368. *
369. * @return void
370. */
371. protected function resetBinds()
372. {
373. $this->boundCounter = 0;
374. $this->boundParameters = array();
375. $this->boundValues = array();
376. }
377.
378. /**
379. * Performs binding of variables bound with bindValue and bindParam on the statement $stmt.
380. *
381. * This method must be called if you have used the bind methods
382. * in your query and you build the method yourself using build.
383. *
384. * @param PDOStatement $stmt
385. * @return void
386. */
387. public function doBind( PDOStatement $stmt )
388. {
389. foreach ( $this->boundValues as $key => $value )
390. {
391. try
392. {
393. $stmt->bindValue( $key, $value, $this->boundValuesType[$key] );
394. }
395. catch ( PDOException $e )
396. {
397. // see comment below
398. }
399. }
400. foreach ( $this->boundParameters as $key => &$value )
401. {
402. try
403. {
404. $stmt->bindParam( $key, $value, $this->boundParametersType[$key] );
405. }
406. catch ( PDOException $e )
407. {
408. // we are ignoring this exception since it may only occur when
409. // a bound parameter is not found in the query anymore.
410. // this can happen if either drop an expression with a bound value
411. // created with this query or if you remove a bind in a query by
412. // replacing it with another one.
413. // the only other way to avoid this problem is parse the string for the
414. // bound variables. Note that a simple search will not do since the variable
415. // name may occur in a string.
416. }
417. }
418. }
419.
420. /**
421. * Returns a prepared statement from this query which can be used for execution.
422. *
423. * The returned object is a PDOStatement for which you can find extensive
424. * documentation in the PHP manual:
425. * {@link http://php.net/pdostatement-bindcolumn}
426. *
427. * prepare() automatically calls doBind() on the statement.
428. * @return PDOStatement
429. */
430. public function prepare()
431. {
432. $stmt = $this->db->prepare( $this->getQuery() );
433. $this->doBind( $stmt );
434. return $stmt;
435. }
436.
437. /**
438. * Returns all the elements in $array as one large single dimensional array.
439. *
440. * @todo public? Currently this is needed for QueryExpression.
441. * @param array $array
442. * @return array
443. */
444. static public function arrayFlatten( array $array )
445. {
446. $flat = array();
447. foreach ( $array as $arg )
448. {
449. switch ( gettype( $arg ) )
450. {
451. case 'array':
452. $flat = array_merge( $flat, $arg );
453. break;
454.
455. default:
456. $flat[] = $arg;
457. break;
458. }
459. }
460. return $flat;
461. }
462.
463. /**
464. * Return SQL string for query.
465. *
466. * Typecasting to (string) should be used to make __toString() to be called
467. * with PHP 5.1. This will not be needed in PHP 5.2 and higher when this
468. * object is used in a string context.
469. *
470. * Example:
471. * <code>
472. * $q->select('*')
473. * ->from( 'table1' )
474. * ->where ( $q->expr->eq( 'name', $q->bindValue( "Beeblebrox" ) ) );
475. * echo $q, "\n";
476. * </code>
477. *
478. * @return string
479. */
480. public function __toString()
481. {
482. return $this->getQuery();
483. }
484.
485. /**
486. * Returns the ezcQuerySubSelect query object.
487. *
488. * This method creates new ezcQuerySubSelect object
489. * that could be used for building correct
490. * subselect inside query.
491. *
492. * @return ezcQuerySubSelect
493. */
494. public function subSelect()
495. {
496. return new ezcQuerySubSelect( $this );
497. }
498.
499. /**
500. * Returns the query string for this query object.
501. *
502. * @throws ezcQueryInvalidException if it was not possible to build a valid query.
503. * @return string
504. */
505. abstract public function getQuery();
506.
507. ?>
Last updated: Wed, 18 Jun 2008