| <?php |
| /** |
| * File containing the ezcQuery class. |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @package Database |
| * @version //autogentag// |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| */ |
| |
| /** |
| * The ezcQuery class provides the common API for all Query objects. |
| * |
| * ezcQuery has three main purposes: |
| * - it provides a common API for building queries through the getQuery() method. |
| * - it provides a common API for binding parameters to queries through |
| * bindValue() and bindParam() |
| * - it provides internal aliasing functionality that allows you to use |
| * aliases for table and column names. The substitution is done inside of the |
| * query classes before the query itself is built. |
| * |
| * Through the bind methods you can bind parameters and values to your |
| * query. Finally you can use prepare to get a PDOStatement object |
| * from your query object. |
| * |
| * Subclasses should provide functionality to build an actual query. |
| * |
| * @package Database |
| * @version //autogentag// |
| */ |
| abstract class ezcQuery |
| { |
| /** |
| * A pointer to the database handler to use for this query. |
| * |
| * @var PDO |
| */ |
| protected $db; |
| |
| /** |
| * The column and table name aliases. |
| * |
| * Format: array('alias' => 'realName') |
| * @var array(string=>string) |
| */ |
| private $aliases = null; |
| |
| /** |
| * Counter used to create unique ids in the bind methods. |
| * |
| * @var int |
| */ |
| private $boundCounter = 0; |
| |
| /** |
| * Stores the list of parameters that will be bound with doBind(). |
| * |
| * Format: array( ':name' => &mixed ) |
| * @var array(string=>&mixed) |
| */ |
| private $boundParameters = array(); |
| |
| /** |
| * Stores the type of a value which will we used when the value is bound. |
| * |
| * @var array(string=>int) |
| */ |
| private $boundParametersType = array(); |
| |
| /** |
| * Stores the list of values that will be bound with doBind(). |
| * |
| * Format: array( ':name' => mixed ) |
| * @var array(string=>mixed) |
| */ |
| private $boundValues = array(); |
| |
| /** |
| * Stores the type of a value which will we used when the value is bound. |
| * |
| * @var array(string=>int) |
| */ |
| private $boundValuesType = array(); |
| |
| /** |
| * The expression object for this class. |
| * |
| * @var ezcQueryExpression |
| */ |
| public $expr = null; |
| |
| /** |
| * Constructs a new ezcQuery that works on the database $db and with the aliases $aliases. |
| * |
| * The aliases can be used to substitute the column and table names with more |
| * friendly names. E.g PersistentObject uses it to allow using property and class |
| * names instead of column and table names. |
| * |
| * @param PDO $db |
| * @param array(string=>string) $aliases |
| */ |
| public function __construct( PDO $db, array $aliases = array() ) |
| { |
| $this->db = $db; |
| if ( $this->expr == null ) |
| { |
| $this->expr = $db->createExpression(); |
| } |
| if ( !empty( $aliases ) ) |
| { |
| $this->aliases = $aliases; |
| $this->expr->setAliases( $this->aliases ); |
| } |
| } |
| |
| /** |
| * Sets the aliases $aliases for this object. |
| * |
| * The aliases should be in the form array( "aliasName" => "databaseName" ) |
| * Each alias defines a relation between a user-defined name and a name |
| * in the database. This is supported for table names as column names. |
| * |
| * The aliases can be used to substitute the column and table names with more |
| * friendly names. The substitution is done when the query is built, not using |
| * AS statements in the database itself. |
| * |
| * Example of a select query with aliases: |
| * <code> |
| * <?php |
| * $q->setAliases( array( 'Identifier' => 'id', 'Company' => 'company' ) ); |
| * $q->select( 'Company' ) |
| * ->from( 'table' ) |
| * ->where( $q->expr->eq( 'Identifier', 5 ) ); |
| * echo $q->getQuery(); |
| * ?> |
| * </code> |
| * |
| * This example will output SQL similar to: |
| * <code> |
| * SELECT company FROM table WHERE id = 5 |
| * </code> |
| * |
| * Aliasses also take effect for composite names in the form |
| * tablename.columnname as the following example shows: |
| * <code> |
| * <?php |
| * $q->setAliases( array( 'Order' => 'orders', 'Recipient' => 'company' ) ); |
| * $q->select( 'Order.Recipient' ) |
| * ->from( 'Order' ); |
| * echo $q->getQuery(); |
| * ?> |
| * </code> |
| * |
| * This example will output SQL similar to: |
| * <code> |
| * SELECT orders.company FROM orders; |
| * </code> |
| * |
| * It is even possible to have an alias point to a table name/column name |
| * combination. This will only work for alias names without a . (dot): |
| * <code> |
| * <?php |
| * $q->setAliases( array( 'Order' => 'orders', 'Recipient' => 'orders.company' ) ); |
| * $q->select( 'Recipient' ) |
| * ->from( 'Order' ); |
| * echo $q->getQuery(); |
| * ?> |
| * </code> |
| * |
| * This example will output SQL similar to: |
| * <code> |
| * SELECT orders.company FROM orders; |
| * </code> |
| * |
| * In the following example, the Recipient alias will not be used, as it is |
| * points to a fully qualified name - the Order alias however is used: |
| * <code> |
| * <?php |
| * $q->setAliases( array( 'Order' => 'orders', 'Recipient' => 'orders.company' ) ); |
| * $q->select( 'Order.Recipient' ) |
| * ->from( 'Order' ); |
| * echo $q->getQuery(); |
| * ?> |
| * </code> |
| * |
| * This example will output SQL similar to: |
| * <code> |
| * SELECT orders.Recipient FROM orders; |
| * </code> |
| * |
| * @param array(string=>string) $aliases |
| * @return void |
| */ |
| public function setAliases( array $aliases ) |
| { |
| $this->aliases = $aliases; |
| $this->expr->setAliases( $aliases ); |
| } |
| |
| /** |
| * Returns true if this object has aliases. |
| * |
| * @return bool |
| */ |
| public function hasAliases() |
| { |
| return $this->aliases !== null ? true : false; |
| } |
| |
| /** |
| * Returns the correct identifier for the alias $alias. |
| * |
| * If the alias does not exists in the list of aliases |
| * it is returned unchanged. |
| * |
| * This can method handles composite identifiers separated by a dot ('.'). |
| * |
| * @param string $alias |
| * @return string |
| */ |
| protected function getIdentifier( $alias ) |
| { |
| $aliasParts = explode( '.', $alias ); |
| $identifiers = array(); |
| // If the alias consists of one part, then we just look it up in the |
| // array. If we find it, we use it, otherwise we return the name as-is |
| // and assume it's just a column name. The alias target can be a fully |
| // qualified name (table.column). |
| if ( count( $aliasParts ) == 1 ) |
| { |
| if ( $this->aliases !== null && |
| is_string( $alias ) && |
| array_key_exists( $alias, $this->aliases ) ) |
| { |
| $alias = $this->aliases[$alias]; |
| } |
| return $alias; |
| } |
| // If the passed name consist of two parts, we need to check all parts |
| // of the passed-in name for aliases, because an alias can be made for |
| // both a table name and a column name. For each element we try to find |
| // whether we have an alias mapping. Unlike the above case, the alias |
| // target can in this case *not* consist of a fully qualified name as |
| // this would introduce another part of the name (with two dots). |
| for ( $i = 0; $i < count( $aliasParts ); $i++ ) |
| { |
| if ( $this->aliases !== null && |
| array_key_exists( $aliasParts[$i], $this->aliases ) ) |
| { |
| // We only use the found alias if the alias target is not a fully |
| // qualified name (table.column). |
| $tmpAlias = $this->aliases[$aliasParts[$i]]; |
| if ( count( explode( '.', $tmpAlias ) ) === 1 ) |
| { |
| $aliasParts[$i] = $this->aliases[$aliasParts[$i]]; |
| } |
| } |
| } |
| $alias = join( '.', $aliasParts ); |
| return $alias; |
| } |
| |
| /** |
| * Returns the correct identifiers for the aliases found in $aliases. |
| * |
| * This method is similar to getIdentifier except that it works on an array. |
| * |
| * @param array(string) $aliasList |
| * @return array(string) |
| */ |
| protected function getIdentifiers( array $aliasList ) |
| { |
| if ( $this->aliases !== null ) |
| { |
| foreach ( $aliasList as $key => $alias ) |
| { |
| $aliasList[$key] = $this->getIdentifier( $alias ); |
| } |
| } |
| return $aliasList; |
| } |
| |
| /** |
| * Binds the value $value to the specified variable name $placeHolder. |
| * |
| * This method provides a shortcut for PDOStatement::bindValue |
| * when using prepared statements. |
| * |
| * The parameter $value specifies the value that you want to bind. If |
| * $placeholder is not provided bindValue() will automatically create a |
| * placeholder for you. An automatic placeholder will be of the name |
| * 'ezcValue1', 'ezcValue2' etc. |
| * |
| * For more information see {@link http://php.net/pdostatement-bindparam} |
| * |
| * Example: |
| * <code> |
| * $value = 2; |
| * $q->eq( 'id', $q->bindValue( $value ) ); |
| * $stmt = $q->prepare(); // the value 2 is bound to the query. |
| * $value = 4; |
| * $stmt->execute(); // executed with 'id = 2' |
| * </code> |
| * |
| * @see doBind() |
| * @param mixed $value |
| * @param string $placeHolder the name to bind with. The string must start with a colon ':'. |
| * @return string the placeholder name used. |
| */ |
| public function bindValue( $value, $placeHolder = null, $type = PDO::PARAM_STR ) |
| { |
| if ( $placeHolder === null ) |
| { |
| $this->boundCounter++; |
| $placeHolder = ":ezcValue{$this->boundCounter}"; |
| } |
| |
| $this->boundValues[$placeHolder] = $value; |
| $this->boundValuesType[$placeHolder] = $type; |
| |
| return $placeHolder; |
| } |
| |
| /** |
| * Binds the parameter $param to the specified variable name $placeHolder.. |
| * |
| * This method provides a shortcut for PDOStatement::bindParam |
| * when using prepared statements. |
| * |
| * The parameter $param specifies the variable that you want to bind. If |
| * $placeholder is not provided bind() will automatically create a |
| * placeholder for you. An automatic placeholder will be of the name |
| * 'ezcValue1', 'ezcValue2' etc. |
| * |
| * For more information see {@link http://php.net/pdostatement-bindparam} |
| * |
| * Example: |
| * <code> |
| * $value = 2; |
| * $q->eq( 'id', $q->bindParam( $value ) ); |
| * $stmt = $q->prepare(); // the parameter $value is bound to the query. |
| * $value = 4; |
| * $stmt->execute(); // executed with 'id = 4' |
| * </code> |
| * |
| * @see doBind() |
| * @param &mixed $param |
| * @param string $placeHolder the name to bind with. The string must start with a colon ':'. |
| * @return string the placeholder name used. |
| */ |
| public function bindParam( &$param, $placeHolder = null, $type = PDO::PARAM_STR ) |
| { |
| if ( $placeHolder === null ) |
| { |
| $this->boundCounter++; |
| $placeHolder = ":ezcValue{$this->boundCounter}"; |
| } |
| |
| $this->boundParameters[$placeHolder] =& $param; |
| $this->boundParametersType[$placeHolder] = $type; |
| |
| return $placeHolder; |
| } |
| |
| /** |
| * Resets the bound values and parameters to empty. |
| * |
| * This is useful if your query can be reset and used multiple times. |
| * |
| * @return void |
| */ |
| protected function resetBinds() |
| { |
| $this->boundCounter = 0; |
| $this->boundParameters = array(); |
| $this->boundValues = array(); |
| } |
| |
| /** |
| * Performs binding of variables bound with bindValue and bindParam on the statement $stmt. |
| * |
| * This method must be called if you have used the bind methods |
| * in your query and you build the method yourself using build. |
| * |
| * @param PDOStatement $stmt |
| * @return void |
| */ |
| public function doBind( PDOStatement $stmt ) |
| { |
| foreach ( $this->boundValues as $key => $value ) |
| { |
| try |
| { |
| $stmt->bindValue( $key, $value, $this->boundValuesType[$key] ); |
| } |
| catch ( PDOException $e ) |
| { |
| // see comment below |
| } |
| } |
| foreach ( $this->boundParameters as $key => &$value ) |
| { |
| try |
| { |
| $stmt->bindParam( $key, $value, $this->boundParametersType[$key] ); |
| } |
| catch ( PDOException $e ) |
| { |
| // we are ignoring this exception since it may only occur when |
| // a bound parameter is not found in the query anymore. |
| // this can happen if either drop an expression with a bound value |
| // created with this query or if you remove a bind in a query by |
| // replacing it with another one. |
| // the only other way to avoid this problem is parse the string for the |
| // bound variables. Note that a simple search will not do since the variable |
| // name may occur in a string. |
| } |
| } |
| } |
| |
| /** |
| * Returns a prepared statement from this query which can be used for execution. |
| * |
| * The returned object is a PDOStatement for which you can find extensive |
| * documentation in the PHP manual: |
| * {@link http://php.net/pdostatement-bindcolumn} |
| * |
| * prepare() automatically calls doBind() on the statement. |
| * @return PDOStatement |
| */ |
| public function prepare() |
| { |
| $stmt = $this->db->prepare( $this->getQuery() ); |
| $this->doBind( $stmt ); |
| return $stmt; |
| } |
| |
| /** |
| * Returns all the elements in $array as one large single dimensional array. |
| * |
| * @todo public? Currently this is needed for QueryExpression. |
| * @param array $array |
| * @return array |
| */ |
| static public function arrayFlatten( array $array ) |
| { |
| $flat = array(); |
| foreach ( $array as $arg ) |
| { |
| switch ( gettype( $arg ) ) |
| { |
| case 'array': |
| $flat = array_merge( $flat, $arg ); |
| break; |
| |
| default: |
| $flat[] = $arg; |
| break; |
| } |
| } |
| return $flat; |
| } |
| |
| /** |
| * Return SQL string for query. |
| * |
| * Typecasting to (string) should be used to make __toString() to be called |
| * with PHP 5.1. This will not be needed in PHP 5.2 and higher when this |
| * object is used in a string context. |
| * |
| * Example: |
| * <code> |
| * $q->select('*') |
| * ->from( 'table1' ) |
| * ->where ( $q->expr->eq( 'name', $q->bindValue( "Beeblebrox" ) ) ); |
| * echo $q, "\n"; |
| * </code> |
| * |
| * @return string |
| */ |
| public function __toString() |
| { |
| return $this->getQuery(); |
| } |
| |
| /** |
| * Returns the ezcQuerySubSelect query object. |
| * |
| * This method creates new ezcQuerySubSelect object |
| * that could be used for building correct |
| * subselect inside query. |
| * |
| * @return ezcQuerySubSelect |
| */ |
| public function subSelect() |
| { |
| return new ezcQuerySubSelect( $this ); |
| } |
| |
| /** |
| * Returns the query string for this query object. |
| * |
| * @throws ezcQueryInvalidException if it was not possible to build a valid query. |
| * @return string |
| */ |
| abstract public function getQuery(); |
| } |
| ?> |