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
Non-specific Database query code. Used by all engines.

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.
   */

  #[\ReturnTypeWillChange]
  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

Namesort descending Modifiers Type Description Overrides
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
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.