bd2facd18fe8070c3b6c918ccd52d27a19ecb81f
[yaffs-website] / web / core / lib / Drupal / Core / Config / Entity / Query / Condition.php
1 <?php
2
3 namespace Drupal\Core\Config\Entity\Query;
4
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\Core\Entity\Query\ConditionBase;
7 use Drupal\Core\Entity\Query\ConditionInterface;
8 use Drupal\Core\Entity\Query\QueryException;
9
10 /**
11  * Defines the condition class for the config entity query.
12  *
13  * @see \Drupal\Core\Config\Entity\Query\Query
14  */
15 class Condition extends ConditionBase {
16
17   /**
18    * {@inheritdoc}
19    */
20   public function compile($configs) {
21     $and = strtoupper($this->conjunction) == 'AND';
22     $single_conditions = [];
23     $condition_groups = [];
24     foreach ($this->conditions as $condition) {
25       if ($condition['field'] instanceof ConditionInterface) {
26         $condition_groups[] = $condition;
27       }
28       else {
29         if (!isset($condition['operator'])) {
30           $condition['operator'] = is_array($condition['value']) ? 'IN' : '=';
31         }
32
33         // Lowercase condition value(s) for case-insensitive matches.
34         if (is_array($condition['value'])) {
35           $condition['value'] = array_map('Drupal\Component\Utility\Unicode::strtolower', $condition['value']);
36         }
37         elseif (!is_bool($condition['value'])) {
38           $condition['value'] = Unicode::strtolower($condition['value']);
39         }
40
41         $single_conditions[] = $condition;
42       }
43     }
44     $return = [];
45     if ($single_conditions) {
46       foreach ($configs as $config_name => $config) {
47         foreach ($single_conditions as $condition) {
48           $match = $this->matchArray($condition, $config, explode('.', $condition['field']));
49           // If AND and it's not matching, then the rest of conditions do not
50           // matter and this config object does not match.
51           // If OR and it is matching, then the rest of conditions do not
52           // matter and this config object does match.
53           if ($and != $match) {
54             break;
55           }
56         }
57         if ($match) {
58           $return[$config_name] = $config;
59         }
60       }
61     }
62     elseif (!$condition_groups || $and) {
63       // If there were no single conditions then either:
64       // - Complex conditions, OR: need to start from no entities.
65       // - Complex conditions, AND: need to start from all entities.
66       // - No complex conditions (AND/OR doesn't matter): need to return all
67       //   entities.
68       $return = $configs;
69     }
70     foreach ($condition_groups as $condition) {
71       $group_entities = $condition['field']->compile($configs);
72       if ($and) {
73         $return = array_intersect_key($return, $group_entities);
74       }
75       else {
76         $return = $return + $group_entities;
77       }
78     }
79
80     return $return;
81   }
82
83   /**
84    * {@inheritdoc}
85    */
86   public function exists($field, $langcode = NULL) {
87     return $this->condition($field, NULL, 'IS NOT NULL', $langcode);
88   }
89
90   /**
91    * {@inheritdoc}
92    */
93   public function notExists($field, $langcode = NULL) {
94     return $this->condition($field, NULL, 'IS NULL', $langcode);
95   }
96
97   /**
98    * Matches for an array representing one or more config paths.
99    *
100    * @param array $condition
101    *   The condition array as created by the condition() method.
102    * @param array $data
103    *   The config array or part of it.
104    * @param array $needs_matching
105    *   The list of config array keys needing a match. Can contain config keys
106    *   and the * wildcard.
107    * @param array $parents
108    *   The current list of parents.
109    *
110    * @return bool
111    *   TRUE when the condition matched to the data else FALSE.
112    */
113   protected function matchArray(array $condition, array $data, array $needs_matching, array $parents = []) {
114     $parent = array_shift($needs_matching);
115     if ($parent === '*') {
116       $candidates = array_keys($data);
117     }
118     else {
119       // Avoid a notice when calling match() later.
120       if (!isset($data[$parent])) {
121         $data[$parent] = NULL;
122       }
123       $candidates = [$parent];
124     }
125     foreach ($candidates as $key) {
126       if ($needs_matching) {
127         if (is_array($data[$key])) {
128           $new_parents = $parents;
129           $new_parents[] = $key;
130           if ($this->matchArray($condition, $data[$key], $needs_matching, $new_parents)) {
131             return TRUE;
132           }
133         }
134       }
135       // Only try to match a scalar if there are no remaining keys in
136       // $needs_matching as this indicates that we are looking for a specific
137       // subkey and a scalar can never match that.
138       elseif ($this->match($condition, $data[$key])) {
139         return TRUE;
140       }
141     }
142     return FALSE;
143   }
144
145   /**
146    * Perform the actual matching.
147    *
148    * @param array $condition
149    *   The condition array as created by the condition() method.
150    * @param string $value
151    *   The value to match against.
152    *
153    * @return bool
154    *   TRUE when matches else FALSE.
155    */
156   protected function match(array $condition, $value) {
157     // "IS NULL" and "IS NOT NULL" conditions can also deal with array values,
158     // so we return early for them to avoid problems.
159     if (in_array($condition['operator'], ['IS NULL', 'IS NOT NULL'], TRUE)) {
160       $should_be_set = $condition['operator'] === 'IS NOT NULL';
161       return $should_be_set === isset($value);
162     }
163
164     if (isset($value)) {
165       // We always want a case-insensitive match.
166       if (!is_bool($value)) {
167         $value = Unicode::strtolower($value);
168       }
169
170       switch ($condition['operator']) {
171         case '=':
172           return $value == $condition['value'];
173         case '>':
174           return $value > $condition['value'];
175         case '<':
176           return $value < $condition['value'];
177         case '>=':
178           return $value >= $condition['value'];
179         case '<=':
180           return $value <= $condition['value'];
181         case '<>':
182           return $value != $condition['value'];
183         case 'IN':
184           return array_search($value, $condition['value']) !== FALSE;
185         case 'NOT IN':
186           return array_search($value, $condition['value']) === FALSE;
187         case 'STARTS_WITH':
188           return strpos($value, $condition['value']) === 0;
189         case 'CONTAINS':
190           return strpos($value, $condition['value']) !== FALSE;
191         case 'ENDS_WITH':
192           return substr($value, -strlen($condition['value'])) === (string) $condition['value'];
193         default:
194           throw new QueryException('Invalid condition operator.');
195       }
196     }
197     return FALSE;
198   }
199
200 }