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