1 | <?php |
||||
2 | |||||
3 | namespace Sanderdekroon\Parlant\Builder; |
||||
4 | |||||
5 | use Closure; |
||||
6 | use InvalidArgumentException; |
||||
7 | |||||
8 | class WhereTaxonomyClause |
||||
9 | { |
||||
10 | |||||
11 | protected $grammar; |
||||
12 | protected $relation; |
||||
13 | |||||
14 | |||||
15 | 4 | public function __construct($grammar) |
|||
16 | { |
||||
17 | 4 | $this->grammar = $grammar; |
|||
18 | 4 | } |
|||
19 | |||||
20 | |||||
21 | 4 | public function build($taxonomy, $field = null, $operator = null, $value = null, $includeChildren = true, $relation = null, $level = 1) |
|||
22 | { |
||||
23 | /** @todo Rewrite */ |
||||
24 | 4 | $this->relation = [$level => empty($relation) ? 'AND' : $relation]; |
|||
25 | |||||
26 | // If the taxonomy is an array, we will assume it is an array of key-value pairs |
||||
27 | // and can add them each as a where clause. We will maintain the boolean we |
||||
28 | // received when the method was called and pass it into the nested where. |
||||
29 | 4 | if (is_array($taxonomy)) { |
|||
30 | return $this->addArrayOfWhereTaxonomies($taxonomy); |
||||
31 | } |
||||
32 | |||||
33 | // If the taxonomy parameter is a close we'll start a nested meta query. |
||||
34 | 4 | if ($taxonomy instanceof Closure) { |
|||
35 | 1 | $nestedTaxonomy = $this->extractNestedTaxonomyClosures($taxonomy); |
|||
36 | |||||
37 | 1 | return [$this->whereNestedTaxonomy($nestedTaxonomy, $relation)]; |
|||
0 ignored issues
–
show
|
|||||
38 | } |
||||
39 | |||||
40 | // If the field variable is a closure, we'll start a nested taxonomy query |
||||
41 | // and use the supplied taxonomy as default taxonomy in the query. |
||||
42 | 4 | if ($field instanceof Closure) { |
|||
43 | $nestedTaxonomy = $this->extractNestedTaxonomyClosures($field, $taxonomy); |
||||
44 | |||||
45 | return [$this->whereNestedTaxonomy($nestedTaxonomy, $relation)]; |
||||
46 | } |
||||
47 | |||||
48 | // Here we will make some assumptions about the operator. If only 3 values are |
||||
49 | // passed to the method, we will assume that the operator is an equals sign |
||||
50 | // and keep going. Otherwise, we'll require the operator to be passed in. |
||||
51 | 4 | list($value, $operator) = $this->prepareValueAndOperator( |
|||
52 | 4 | $value, |
|||
53 | 4 | $operator, |
|||
54 | 4 | (func_num_args() == 3 || is_null($value)), |
|||
55 | 4 | true // This is needed since tax_query has different default operator. |
|||
56 | ); |
||||
57 | |||||
58 | // If the given operator is not found in the list of valid operators we will |
||||
59 | // assume that the developer is just short-cutting the '=' operators and |
||||
60 | // we will set the operators to '=' and set the values appropriately. |
||||
61 | 4 | if ($this->invalidOperator($operator)) { |
|||
62 | list($value, $operator) = [$operator, 'IN']; |
||||
63 | } |
||||
64 | |||||
65 | // Validate the supplied field against the known fields of an taxonomy |
||||
66 | // query within WP_Query. If no valid field is found, we'll default |
||||
67 | // back to the WordPress default which is term_id. |
||||
68 | 4 | $field = $this->getValidTermField($field); |
|||
69 | |||||
70 | 4 | return [compact( |
|||
71 | 4 | 'taxonomy', // Taxonomy name |
|||
72 | 4 | 'field', // Taxonomy term field |
|||
73 | 4 | 'operator', // The term operator |
|||
74 | 4 | 'value', // The value of the term |
|||
75 | 4 | 'includeChildren', // Include/exclude children |
|||
76 | 4 | 'level' // Unimplemented, undocumented. |
|||
77 | )]; |
||||
78 | |||||
79 | return $this; |
||||
0 ignored issues
–
show
return $this is not reachable.
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed. Unreachable code is most often the result of function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last ![]() |
|||||
80 | } |
||||
81 | |||||
82 | |||||
83 | 4 | public function getRelation() |
|||
84 | { |
||||
85 | 4 | return $this->relation; |
|||
86 | } |
||||
87 | |||||
88 | /** |
||||
89 | * Resolve the closures and replace them with NestedTaxonomy classes. |
||||
90 | * @param Closure $closure |
||||
91 | * @return NestedTaxonomy |
||||
92 | */ |
||||
93 | 1 | protected function extractNestedTaxonomyClosures($closure, $taxonomyName = null) |
|||
94 | { |
||||
95 | 1 | $nestedTaxonomy = call_user_func($closure->bindTo(new NestedTaxonomy($taxonomyName))); |
|||
96 | |||||
97 | 1 | $query = $nestedTaxonomy->getQuery(); |
|||
98 | 1 | foreach ($query as $key => $taxonomy) { |
|||
99 | 1 | if ($taxonomy['taxonomy'] instanceof Closure) { |
|||
100 | 1 | $query[$key] = $this->extractNestedTaxonomyClosures($taxonomy['taxonomy']); |
|||
101 | } |
||||
102 | } |
||||
103 | |||||
104 | 1 | $nestedTaxonomy->replaceQuery($query); |
|||
105 | 1 | return $nestedTaxonomy; |
|||
106 | } |
||||
107 | |||||
108 | /** |
||||
109 | * Nest multiple taxonomy queries by supplying a query. If the closure contains |
||||
110 | * another closure, it is resolved recursivly. |
||||
111 | * @param Closure $closure |
||||
112 | * @return NestedTaxonomy Returns a NestedTaxonomy instance which is further processed by the compiler. |
||||
113 | */ |
||||
114 | 1 | protected function whereNestedTaxonomy($nestedTaxonomy) |
|||
115 | { |
||||
116 | 1 | if (!$nestedTaxonomy instanceof NestedTaxonomy) { |
|||
117 | throw new InvalidArgumentException('Invalid class supplied for nested taxonomy query'); |
||||
118 | } |
||||
119 | |||||
120 | 1 | $query = $nestedTaxonomy->getQuery(); |
|||
121 | |||||
122 | 1 | foreach ($query as $key => $taxonomy) { |
|||
123 | 1 | if ($taxonomy instanceof NestedTaxonomy) { |
|||
124 | $query[$key] = $this->whereNestedTaxonomy($taxonomy); |
||||
125 | continue; |
||||
126 | } |
||||
127 | |||||
128 | 1 | $query[$key] = $this->parseNestedTaxonomy($taxonomy); |
|||
129 | } |
||||
130 | |||||
131 | 1 | $nestedTaxonomy->replaceQuery($query); |
|||
132 | |||||
133 | 1 | return $nestedTaxonomy; |
|||
134 | } |
||||
135 | |||||
136 | /** |
||||
137 | * Parse the nested taxonomy fields by validating them. If the taxonomy fields is |
||||
138 | * an instance of NestedTaxonomy, we'll resolve that recursively. |
||||
139 | * @param array|NestedTaxonomy $taxonomy |
||||
140 | * @return array |
||||
141 | */ |
||||
142 | 1 | protected function parseNestedTaxonomy($taxonomy) |
|||
143 | { |
||||
144 | 1 | if ($taxonomy instanceof NestedTaxonomy) { |
|||
145 | $taxonomy = $this->whereNestedTaxonomy($taxonomy); |
||||
146 | } |
||||
147 | |||||
148 | 1 | return $this->validateTaxonomyFields($taxonomy); |
|||
0 ignored issues
–
show
It seems like
$taxonomy can also be of type Sanderdekroon\Parlant\Builder\NestedTaxonomy ; however, parameter $fields of Sanderdekroon\Parlant\Bu...alidateTaxonomyFields() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
149 | } |
||||
150 | |||||
151 | /** |
||||
152 | * Do some basic validating of the meta fields. Checks if the operator is valid and |
||||
153 | * if the meta type (if supplied any) is valid with the current grammar. |
||||
154 | * @param array $fields |
||||
155 | * @return array |
||||
156 | */ |
||||
157 | 1 | protected function validateTaxonomyFields($fields) |
|||
158 | { |
||||
159 | 1 | extract($fields); |
|||
160 | |||||
161 | // If the given operator is not found in the list of valid operators we will |
||||
162 | // assume that the developer is just short-cutting the '=' operators and |
||||
163 | // we will set the operators to '=' and set the values appropriately. |
||||
164 | 1 | if ($this->invalidOperator($operator)) { |
|||
165 | list($value, $operator) = [$operator, '=']; |
||||
166 | } |
||||
167 | |||||
168 | // If no field is given or if it's an invalid one, we'll default back |
||||
169 | // to the term_id field. The $field is checked against the |
||||
170 | // values in the supplied posttype grammar. |
||||
171 | 1 | $field = $this->getValidTermField($field); |
|||
172 | |||||
173 | 1 | return compact('taxonomy', 'field', 'operator', 'value', 'includeChildren', 'level'); |
|||
174 | } |
||||
175 | |||||
176 | /** |
||||
177 | * Adds arrays of where metas to the query. |
||||
178 | * @param array $taxonomyArray |
||||
179 | */ |
||||
180 | protected function addArrayOfWhereTaxonomies($taxonomyArray) |
||||
181 | { |
||||
182 | $build = []; |
||||
183 | foreach ($taxonomyArray as $array) { |
||||
184 | $build[] = $this->build(...$array); |
||||
0 ignored issues
–
show
$array is expanded, but the parameter $taxonomy of Sanderdekroon\Parlant\Bu...TaxonomyClause::build() does not expect variable arguments.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
185 | } |
||||
186 | |||||
187 | return $build; |
||||
188 | } |
||||
189 | |||||
190 | /** |
||||
191 | * Return a valid taxonomy term field, like term_id or term_slug. |
||||
192 | * @param string $type |
||||
193 | * @return string Returns 'term_id' if none is supplied or if it's invalid |
||||
194 | */ |
||||
195 | 4 | protected function getValidTermField($field = null) |
|||
196 | { |
||||
197 | 4 | if (is_null($field) || !in_array($field, $this->grammar->getTaxonomyFields())) { |
|||
198 | 1 | return 'term_id'; |
|||
199 | } |
||||
200 | |||||
201 | 3 | return strtolower($field); |
|||
202 | } |
||||
203 | |||||
204 | // protected abstract function setBinding($key, $data); |
||||
205 | |||||
206 | // protected abstract function getBinding($key); |
||||
207 | |||||
208 | // protected abstract function getGrammar(); |
||||
209 | |||||
210 | // protected abstract function appendBinding($key, $data); |
||||
211 | |||||
212 | 4 | protected function invalidOperator($operator) |
|||
213 | { |
||||
214 | 4 | return !in_array($operator, $this->grammar->getTaxonomyOperators()); |
|||
215 | } |
||||
216 | |||||
217 | 4 | protected function prepareValueAndOperator($value, $operator, $useDefault = false, $termDefault = false) |
|||
218 | { |
||||
219 | 4 | if ($useDefault) { |
|||
220 | 3 | return [$operator, $termDefault ? 'IN' : '=']; |
|||
221 | } |
||||
222 | |||||
223 | 1 | if ($this->invalidOperator($operator) && !is_null($value)) { |
|||
224 | throw new InvalidArgumentException('Illegal operator and value combination.'); |
||||
225 | } |
||||
226 | |||||
227 | 1 | return [$value, $operator]; |
|||
228 | } |
||||
229 | } |
||||
230 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.