Condition.php 5.77 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
<?php

/**
 * @file
 * Contains \Drupal\Core\Config\Entity\Query\Condition.
 */

namespace Drupal\Core\Config\Entity\Query;

use Drupal\Core\Entity\Query\ConditionBase;
use Drupal\Core\Entity\Query\ConditionInterface;
12
use Drupal\Core\Entity\Query\QueryException;
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

/**
 * Defines the condition class for the config entity query.
 *
 * @see \Drupal\Core\Config\Entity\Query\Query
 */
class Condition extends ConditionBase {

  /**
   * Implements \Drupal\Core\Entity\Query\ConditionInterface::compile().
   */
  public function compile($configs) {
    $and = strtoupper($this->conjunction) == 'AND';
    $single_conditions = array();
    $condition_groups = array();
    foreach ($this->conditions as $condition) {
      if ($condition['field'] instanceOf ConditionInterface) {
        $condition_groups[] = $condition;
      }
      else {
        if (!isset($condition['operator'])) {
          $condition['operator'] = is_array($condition['value']) ? 'IN' : '=';
        }
        $single_conditions[] = $condition;
      }
    }
    $return = array();
    if ($single_conditions) {
      foreach ($configs as $config_name => $config) {
        foreach ($single_conditions as $condition) {
          $match = $this->matchArray($condition, $config, explode('.', $condition['field']));
          // If AND and it's not matching, then the rest of conditions do not
          // matter and this config object does not match.
          // If OR and it is matching, then the rest of conditions do not
          // matter and this config object does match.
          if ($and != $match ) {
            break;
          }
        }
        if ($match) {
          $return[$config_name] = $config;
        }
      }
    }
    elseif (!$condition_groups || $and) {
      // If there were no single conditions then either:
      // - Complex conditions, OR: need to start from no entities.
      // - Complex conditions, AND: need to start from all entities.
      // - No complex conditions (AND/OR doesn't matter): need to return all
      //   entities.
      $return = $configs;
    }
    foreach ($condition_groups as $condition) {
      $group_entities = $condition['field']->compile($configs);
      if ($and) {
        $return = array_intersect_key($return, $group_entities);
      }
      else {
        $return = $return + $group_entities;
      }
    }

    return $return;
  }

  /**
   * Implements \Drupal\Core\Entity\Query\ConditionInterface::exists().
   */
  public function exists($field, $langcode = NULL) {
    return $this->condition($field, NULL, 'IS NOT NULL', $langcode);
  }

  /**
   * Implements \Drupal\Core\Entity\Query\ConditionInterface::notExists().
   */
  public function notExists($field, $langcode = NULL) {
    return $this->condition($field, NULL, 'IS NULL', $langcode);
  }

  /**
   * Matches for an array representing one or more config paths.
   *
   * @param array $condition
   *   The condition array as created by the condition() method.
   * @param array $data
   *   The config array or part of it.
   * @param array $needs_matching
   *   The list of config array keys needing a match. Can contain config keys
   *   and the * wildcard.
   * @param array $parents
   *   The current list of parents.
   *
   * @return bool
   *   TRUE when the condition matched to the data else FALSE.
   */
  protected function matchArray(array $condition, array $data, array $needs_matching, array $parents = array()) {
    $parent = array_shift($needs_matching);
    $candidates = array();
    if ($parent === '*') {
      $candidates = array_keys($data);
    }
114 115 116 117 118
    else {
      // Avoid a notice when calling match() later.
      if (!isset($data[$parent])) {
        $data[$parent] = NULL;
      }
119 120 121
      $candidates = array($parent);
    }
    foreach ($candidates as $key) {
122 123 124 125 126 127 128
      if ($needs_matching) {
        if (is_array($data[$key])) {
          $new_parents = $parents;
          $new_parents[] = $key;
          if ($this->matchArray($condition, $data[$key], $needs_matching, $new_parents)) {
            return TRUE;
          }
129 130
        }
      }
131 132 133
      // Only try to match a scalar if there are no remaining keys in
      // $needs_matching as this indicates that we are looking for a specific
      // subkey and a scalar can never match that.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
      elseif ($this->match($condition, $data[$key])) {
        return TRUE;
      }
    }
    return FALSE;
  }

  /**
   * Perform the actual matching.
   *
   * @param array $condition
   *   The condition array as created by the condition() method.
   * @param string $value
   *   The value to match against.
   *
   * @return bool
   *   TRUE when matches else FALSE.
   */
  protected function match(array $condition, $value) {
    if (isset($value)) {
      switch ($condition['operator']) {
        case '=':
          return $value == $condition['value'];
        case '>':
          return $value > $condition['value'];
        case '<':
          return $value < $condition['value'];
        case '>=':
          return $value >= $condition['value'];
        case '<=':
          return $value <= $condition['value'];
165 166
        case '<>':
          return $value != $condition['value'];
167 168 169 170 171 172 173 174 175 176 177 178
        case 'IN':
          return array_search($value, $condition['value']) !== FALSE;
        case 'NOT IN':
          return array_search($value, $condition['value']) === FALSE;
        case 'STARTS_WITH':
          return strpos($value, $condition['value']) === 0;
        case 'CONTAINS':
          return strpos($value, $condition['value']) !== FALSE;
        case 'ENDS_WITH':
          return substr($value, -strlen($condition['value'])) === (string) $condition['value'];
        case 'IS NOT NULL':
          return TRUE;
179 180 181 182
        case 'IS NULL':
          return FALSE;
        default:
          throw new QueryException('Invalid condition operator.');
183 184 185 186 187 188
      }
    }
    return $condition['operator'] === 'IS NULL';
  }

}