class DatabaseCondition

Generic class for a series of conditions in a query.

Hierarchy

Expanded class hierarchy of DatabaseCondition

Related topics

File

includes/database/query.inc, line 1674

View source
class DatabaseCondition implements QueryConditionInterface, Countable {
    
    /**
     * Array of conditions.
     *
     * @var array
     */
    protected $conditions = array();
    
    /**
     * Array of arguments.
     *
     * @var array
     */
    protected $arguments = array();
    
    /**
     * Whether the conditions have been changed.
     *
     * TRUE if the condition has been changed since the last compile.
     * FALSE if the condition has been compiled and not changed.
     *
     * @var bool
     */
    protected $changed = TRUE;
    
    /**
     * The identifier of the query placeholder this condition has been compiled against.
     */
    protected $queryPlaceholderIdentifier;
    
    /**
     * Contains the string version of the Condition.
     *
     * @var string
     */
    protected $stringVersion;
    
    /**
     * Constructs a DataBaseCondition object.
     *
     * @param string $conjunction
     *   The operator to use to combine conditions: 'AND' or 'OR'.
     */
    public function __construct($conjunction) {
        $this->conditions['#conjunction'] = $conjunction;
    }
    
    /**
     * Implements Countable::count().
     *
     * Returns the size of this conditional. The size of the conditional is the
     * size of its conditional array minus one, because one element is the
     * conjunction.
     */
    public function count() {
        return count($this->conditions) - 1;
    }
    
    /**
     * Implements QueryConditionInterface::condition().
     */
    public function condition($field, $value = NULL, $operator = NULL) {
        if (!isset($operator)) {
            if (is_array($value)) {
                $operator = 'IN';
            }
            elseif (!isset($value)) {
                $operator = 'IS NULL';
            }
            else {
                $operator = '=';
            }
        }
        $this->conditions[] = array(
            'field' => $field,
            'value' => $value,
            'operator' => $operator,
        );
        $this->changed = TRUE;
        return $this;
    }
    
    /**
     * Implements QueryConditionInterface::where().
     */
    public function where($snippet, $args = array()) {
        $this->conditions[] = array(
            'field' => $snippet,
            'value' => $args,
            'operator' => NULL,
        );
        $this->changed = TRUE;
        return $this;
    }
    
    /**
     * Implements QueryConditionInterface::isNull().
     */
    public function isNull($field) {
        return $this->condition($field);
    }
    
    /**
     * Implements QueryConditionInterface::isNotNull().
     */
    public function isNotNull($field) {
        return $this->condition($field, NULL, 'IS NOT NULL');
    }
    
    /**
     * Implements QueryConditionInterface::exists().
     */
    public function exists(SelectQueryInterface $select) {
        return $this->condition('', $select, 'EXISTS');
    }
    
    /**
     * Implements QueryConditionInterface::notExists().
     */
    public function notExists(SelectQueryInterface $select) {
        return $this->condition('', $select, 'NOT EXISTS');
    }
    
    /**
     * Implements QueryConditionInterface::conditions().
     */
    public function &conditions() {
        return $this->conditions;
    }
    
    /**
     * Implements QueryConditionInterface::arguments().
     */
    public function arguments() {
        // If the caller forgot to call compile() first, refuse to run.
        if ($this->changed) {
            return NULL;
        }
        return $this->arguments;
    }
    
    /**
     * Implements QueryConditionInterface::compile().
     *
     * @throws InvalidQueryConditionOperatorException
     */
    public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
        // Re-compile if this condition changed or if we are compiled against a
        // different query placeholder object.
        if ($this->changed || isset($this->queryPlaceholderIdentifier) && $this->queryPlaceholderIdentifier != $queryPlaceholder->uniqueIdentifier()) {
            $this->queryPlaceholderIdentifier = $queryPlaceholder->uniqueIdentifier();
            $condition_fragments = array();
            $arguments = array();
            $conditions = $this->conditions;
            $conjunction = $conditions['#conjunction'];
            unset($conditions['#conjunction']);
            foreach ($conditions as $condition) {
                if (empty($condition['operator'])) {
                    // This condition is a literal string, so let it through as is.
                    $condition_fragments[] = ' (' . $condition['field'] . ') ';
                    $arguments += $condition['value'];
                }
                else {
                    // It's a structured condition, so parse it out accordingly.
                    // Note that $condition['field'] will only be an object for a dependent
                    // DatabaseCondition object, not for a dependent subquery.
                    if ($condition['field'] instanceof QueryConditionInterface) {
                        // Compile the sub-condition recursively and add it to the list.
                        $condition['field']->compile($connection, $queryPlaceholder);
                        $condition_fragments[] = '(' . (string) $condition['field'] . ')';
                        $arguments += $condition['field']->arguments();
                    }
                    else {
                        // If the operator contains an invalid character, throw an
                        // exception to protect against SQL injection attempts.
                        if (stripos($condition['operator'], 'UNION') !== FALSE || strpbrk($condition['operator'], '[-\'"();') !== FALSE) {
                            throw new InvalidQueryConditionOperatorException('Invalid characters in query operator: ' . $condition['operator']);
                        }
                        // For simplicity, we treat all operators as the same data structure.
                        // In the typical degenerate case, this won't get changed.
                        $operator_defaults = array(
                            'prefix' => '',
                            'postfix' => '',
                            'delimiter' => '',
                            'operator' => $condition['operator'],
                            'use_value' => TRUE,
                        );
                        $operator = $connection->mapConditionOperator($condition['operator']);
                        if (!isset($operator)) {
                            $operator = $this->mapConditionOperator($condition['operator']);
                        }
                        $operator += $operator_defaults;
                        $placeholders = array();
                        if ($condition['value'] instanceof SelectQueryInterface) {
                            $condition['value']->compile($connection, $queryPlaceholder);
                            $placeholders[] = (string) $condition['value'];
                            $arguments += $condition['value']->arguments();
                            // Subqueries are the actual value of the operator, we don't
                            // need to add another below.
                            $operator['use_value'] = FALSE;
                        }
                        elseif (!$operator['delimiter']) {
                            $condition['value'] = array(
                                $condition['value'],
                            );
                        }
                        if ($operator['use_value']) {
                            foreach ($condition['value'] as $value) {
                                $placeholder = ':db_condition_placeholder_' . $queryPlaceholder->nextPlaceholder();
                                $arguments[$placeholder] = $value;
                                $placeholders[] = $placeholder;
                            }
                        }
                        $condition_fragments[] = ' (' . $connection->escapeField($condition['field']) . ' ' . $operator['operator'] . ' ' . $operator['prefix'] . implode($operator['delimiter'], $placeholders) . $operator['postfix'] . ') ';
                    }
                }
            }
            $this->changed = FALSE;
            $this->stringVersion = implode($conjunction, $condition_fragments);
            $this->arguments = $arguments;
        }
    }
    
    /**
     * Implements QueryConditionInterface::compiled().
     */
    public function compiled() {
        return !$this->changed;
    }
    
    /**
     * Implements PHP magic __toString method to convert the conditions to string.
     *
     * @return string
     *   A string version of the conditions.
     */
    public function __toString() {
        // If the caller forgot to call compile() first, refuse to run.
        if ($this->changed) {
            return '';
        }
        return $this->stringVersion;
    }
    
    /**
     * PHP magic __clone() method.
     *
     * Only copies fields that implement QueryConditionInterface. Also sets
     * $this->changed to TRUE.
     */
    function __clone() {
        $this->changed = TRUE;
        foreach ($this->conditions as $key => $condition) {
            if ($key !== '#conjunction') {
                if ($condition['field'] instanceof QueryConditionInterface) {
                    $this->conditions[$key]['field'] = clone $condition['field'];
                }
                if ($condition['value'] instanceof SelectQueryInterface) {
                    $this->conditions[$key]['value'] = clone $condition['value'];
                }
            }
        }
    }
    
    /**
     * Gets any special processing requirements for the condition operator.
     *
     * Some condition types require special processing, such as IN, because
     * the value data they pass in is not a simple value. This is a simple
     * overridable lookup function.
     *
     * @param $operator
     *   The condition operator, such as "IN", "BETWEEN", etc. Case-sensitive.
     *
     * @return
     *   The extra handling directives for the specified operator, or NULL.
     */
    protected function mapConditionOperator($operator) {
        // $specials does not use drupal_static as its value never changes.
        static $specials = array(
            'BETWEEN' => array(
                'delimiter' => ' AND ',
            ),
            'IN' => array(
                'delimiter' => ', ',
                'prefix' => ' (',
                'postfix' => ')',
            ),
            'NOT IN' => array(
                'delimiter' => ', ',
                'prefix' => ' (',
                'postfix' => ')',
            ),
            'EXISTS' => array(
                'prefix' => ' (',
                'postfix' => ')',
            ),
            'NOT EXISTS' => array(
                'prefix' => ' (',
                'postfix' => ')',
            ),
            'IS NULL' => array(
                'use_value' => FALSE,
            ),
            'IS NOT NULL' => array(
                'use_value' => FALSE,
            ),
            // Use backslash for escaping wildcard characters.
'LIKE' => array(
                'postfix' => " ESCAPE '\\\\'",
            ),
            'NOT LIKE' => array(
                'postfix' => " ESCAPE '\\\\'",
            ),
            // These ones are here for performance reasons.
'=' => array(),
            '<' => array(),
            '>' => array(),
            '>=' => array(),
            '<=' => array(),
        );
        if (isset($specials[$operator])) {
            $return = $specials[$operator];
        }
        else {
            // We need to upper case because PHP index matches are case sensitive but
            // do not need the more expensive drupal_strtoupper because SQL statements are ASCII.
            $operator = strtoupper($operator);
            $return = isset($specials[$operator]) ? $specials[$operator] : array();
        }
        $return += array(
            'operator' => $operator,
        );
        return $return;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
DatabaseCondition::$arguments protected property Array of arguments.
DatabaseCondition::$changed protected property Whether the conditions have been changed.
DatabaseCondition::$conditions protected property Array of conditions.
DatabaseCondition::$queryPlaceholderIdentifier protected property The identifier of the query placeholder this condition has been compiled against.
DatabaseCondition::$stringVersion protected property Contains the string version of the Condition.
DatabaseCondition::arguments public function Implements QueryConditionInterface::arguments(). Overrides QueryConditionInterface::arguments
DatabaseCondition::compile public function Implements QueryConditionInterface::compile(). Overrides QueryConditionInterface::compile
DatabaseCondition::compiled public function Implements QueryConditionInterface::compiled(). Overrides QueryConditionInterface::compiled
DatabaseCondition::condition public function Implements QueryConditionInterface::condition(). Overrides QueryConditionInterface::condition
DatabaseCondition::conditions public function Implements QueryConditionInterface::conditions(). Overrides QueryConditionInterface::conditions
DatabaseCondition::count public function Implements Countable::count().
DatabaseCondition::exists public function Implements QueryConditionInterface::exists(). Overrides QueryConditionInterface::exists
DatabaseCondition::isNotNull public function Implements QueryConditionInterface::isNotNull(). Overrides QueryConditionInterface::isNotNull
DatabaseCondition::isNull public function Implements QueryConditionInterface::isNull(). Overrides QueryConditionInterface::isNull
DatabaseCondition::mapConditionOperator protected function Gets any special processing requirements for the condition operator.
DatabaseCondition::notExists public function Implements QueryConditionInterface::notExists(). Overrides QueryConditionInterface::notExists
DatabaseCondition::where public function Implements QueryConditionInterface::where(). Overrides QueryConditionInterface::where
DatabaseCondition::__clone function PHP magic __clone() method.
DatabaseCondition::__construct public function Constructs a DataBaseCondition object.
DatabaseCondition::__toString public function Implements PHP magic __toString method to convert the conditions to string.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.