This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Sofa\Eloquence; |
||
4 | |||
5 | use Sofa\Eloquence\Metable\Hooks; |
||
6 | use Sofa\Eloquence\Metable\Attribute; |
||
7 | use Sofa\Eloquence\Metable\AttributeBag; |
||
8 | use Sofa\Hookable\Contracts\ArgumentBag; |
||
9 | |||
10 | /** |
||
11 | * @property array $allowedMeta |
||
12 | */ |
||
13 | trait Metable |
||
14 | { |
||
15 | /** |
||
16 | * Query methods customizable by this trait. |
||
17 | * |
||
18 | * @var array |
||
19 | */ |
||
20 | protected $metaQueryable = [ |
||
21 | 'where', 'whereBetween', 'whereIn', 'whereNull', |
||
22 | 'whereDate', 'whereYear', 'whereMonth', 'whereDay', |
||
23 | 'orderBy', 'pluck', 'value', 'aggregate', |
||
24 | ]; |
||
25 | |||
26 | /** |
||
27 | * Register hooks for the trait. |
||
28 | * |
||
29 | * @codeCoverageIgnore |
||
30 | * |
||
31 | * @return void |
||
32 | */ |
||
33 | public static function bootMetable() |
||
34 | { |
||
35 | $hooks = new Hooks; |
||
36 | |||
37 | foreach ([ |
||
38 | 'setAttribute', |
||
39 | 'getAttribute', |
||
40 | 'toArray', |
||
41 | 'replicate', |
||
42 | 'save', |
||
43 | '__isset', |
||
44 | '__unset', |
||
45 | 'queryHook', |
||
46 | ] as $method) { |
||
47 | static::hook($method, $hooks->{$method}()); |
||
48 | } |
||
49 | } |
||
50 | |||
51 | /** |
||
52 | * Determine wheter method called on the query is customizable by this trait. |
||
53 | * |
||
54 | * @param string $method |
||
55 | * @return bool |
||
56 | */ |
||
57 | protected function isMetaQueryable($method) |
||
58 | { |
||
59 | return in_array($method, $this->metaQueryable); |
||
60 | } |
||
61 | |||
62 | /** |
||
63 | * Custom query handler for querying meta attributes. |
||
64 | * |
||
65 | * @param \Sofa\Eloquence\Builder $query |
||
66 | * @param string $method |
||
67 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
68 | * @return mixed |
||
69 | */ |
||
70 | protected function metaQuery(Builder $query, $method, ArgumentBag $args) |
||
71 | { |
||
72 | if (in_array($method, ['pluck', 'value', 'aggregate', 'orderBy', 'lists'])) { |
||
73 | return $this->metaJoinQuery($query, $method, $args); |
||
74 | } |
||
75 | |||
76 | return $this->metaHasQuery($query, $method, $args); |
||
77 | } |
||
78 | |||
79 | /** |
||
80 | * Adjust meta columns for select statement. |
||
81 | * |
||
82 | * @param \Sofa\Eloquence\Builder $query |
||
83 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
84 | * @return void |
||
85 | */ |
||
86 | protected function metaSelect(Builder $query, ArgumentBag $args) |
||
87 | { |
||
88 | $columns = $args->get('columns'); |
||
89 | |||
90 | foreach ($columns as $key => $column) { |
||
91 | list($column, $alias) = $this->extractColumnAlias($column); |
||
92 | |||
93 | if ($this->hasColumn($column)) { |
||
94 | $select = "{$this->getTable()}.{$column}"; |
||
95 | |||
96 | if ($column !== $alias) { |
||
97 | $select .= " as {$alias}"; |
||
98 | } |
||
99 | |||
100 | $columns[$key] = $select; |
||
101 | } elseif (is_string($column) && $column != '*' && strpos($column, '.') === false) { |
||
102 | $table = $this->joinMeta($query, $column); |
||
103 | |||
104 | $columns[$key] = "{$table}.meta_value as {$alias}"; |
||
105 | } |
||
106 | } |
||
107 | |||
108 | $args->set('columns', $columns); |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Join meta attributes table in order to call provided method. |
||
113 | * |
||
114 | * @param \Sofa\Eloquence\Builder $query |
||
115 | * @param string $method |
||
116 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
117 | * @return mixed |
||
118 | */ |
||
119 | protected function metaJoinQuery(Builder $query, $method, ArgumentBag $args) |
||
120 | { |
||
121 | $alias = $this->joinMeta($query, $args->get('column')); |
||
122 | |||
123 | // For aggregates we need the actual function name |
||
124 | // so it can be called directly on the builder. |
||
125 | $method = $args->get('function') ?: $method; |
||
126 | |||
127 | return (in_array($method, ['orderBy', 'lists', 'pluck'])) |
||
128 | ? $this->{"{$method}Meta"}($query, $args, $alias) |
||
129 | : $this->metaSingleResult($query, $method, $alias); |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Order query by meta attribute. |
||
134 | * |
||
135 | * @param \Sofa\Eloquence\Builder $query |
||
136 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
137 | * @param string $alias |
||
138 | * @return \Sofa\Eloquence\Builder |
||
139 | */ |
||
140 | protected function orderByMeta(Builder $query, $args, $alias) |
||
141 | { |
||
142 | $query->with('metaAttributes')->getQuery()->orderBy("{$alias}.meta_value", $args->get('direction')); |
||
143 | |||
144 | return $query; |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Get an array with the values of given meta attribute. |
||
149 | * |
||
150 | * @param \Sofa\Eloquence\Builder $query |
||
151 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
152 | * @param string $alias |
||
153 | * @return array |
||
154 | */ |
||
155 | protected function pluckMeta(Builder $query, ArgumentBag $args, $alias) |
||
156 | { |
||
157 | list($column, $key) = [$args->get('column'), $args->get('key')]; |
||
158 | |||
159 | $query->select("{$alias}.meta_value as {$column}"); |
||
160 | |||
161 | if (!is_null($key)) { |
||
162 | $this->metaSelectListsKey($query, $key); |
||
163 | } |
||
164 | |||
165 | return $query->callParent('pluck', $args->all()); |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Add select clause for key of the list array. |
||
170 | * |
||
171 | * @param \Sofa\Eloquence\Builder $query |
||
172 | * @param string $key |
||
173 | * @return \Sofa\Eloquence\Builder |
||
174 | */ |
||
175 | protected function metaSelectListsKey(Builder $query, $key) |
||
176 | { |
||
177 | if (strpos($key, '.') !== false) { |
||
178 | return $query->addSelect($key); |
||
0 ignored issues
–
show
|
|||
179 | } elseif ($this->hasColumn($key)) { |
||
180 | return $query->addSelect($this->getTable() . '.' . $key); |
||
0 ignored issues
–
show
|
|||
181 | } |
||
182 | |||
183 | $alias = $this->joinMeta($query, $key); |
||
184 | |||
185 | return $query->addSelect("{$alias}.meta_value as {$key}"); |
||
0 ignored issues
–
show
|
|||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Get single value result from the meta attribute. |
||
190 | * |
||
191 | * @param \Sofa\Eloquence\Builder $query |
||
192 | * @param string $method |
||
193 | * @param string $alias |
||
194 | * @return mixed |
||
195 | */ |
||
196 | protected function metaSingleResult(Builder $query, $method, $alias) |
||
197 | { |
||
198 | return $query->getQuery()->select("{$alias}.meta_value")->{$method}("{$alias}.meta_value"); |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Join meta attributes table. |
||
203 | * |
||
204 | * @param \Sofa\Eloquence\Builder $query |
||
205 | * @param string $column |
||
206 | * @return string |
||
207 | */ |
||
208 | protected function joinMeta(Builder $query, $column) |
||
209 | { |
||
210 | $query->prefixColumnsForJoin(); |
||
211 | |||
212 | $alias = $this->generateMetaAlias(); |
||
213 | |||
214 | $table = (new Attribute)->getTable(); |
||
215 | |||
216 | $query->leftJoin("{$table} as {$alias}", function ($join) use ($alias, $column) { |
||
0 ignored issues
–
show
|
|||
217 | $join->on("{$alias}.metable_id", '=', $this->getQualifiedKeyName()) |
||
218 | ->where("{$alias}.metable_type", '=', $this->getMorphClass()) |
||
219 | ->where("{$alias}.meta_key", '=', $column); |
||
220 | }); |
||
221 | |||
222 | return $alias; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Generate unique alias for meta attributes table. |
||
227 | * |
||
228 | * @return string |
||
229 | */ |
||
230 | protected function generateMetaAlias() |
||
231 | { |
||
232 | return md5(microtime(true)) . '_meta_alias'; |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Add whereHas subquery on the meta attributes relation. |
||
237 | * |
||
238 | * @param \Sofa\Eloquence\Builder $query |
||
239 | * @param string $method |
||
240 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
241 | * @return \Sofa\Eloquence\Builder |
||
242 | */ |
||
243 | protected function metaHasQuery(Builder $query, $method, ArgumentBag $args) |
||
244 | { |
||
245 | $boolean = $this->getMetaBoolean($args); |
||
246 | |||
247 | $operator = $this->getMetaOperator($method, $args); |
||
248 | |||
249 | if (in_array($method, ['whereBetween', 'where'])) { |
||
250 | $this->unbindNumerics($args); |
||
251 | } |
||
252 | |||
253 | return $query |
||
0 ignored issues
–
show
The method
with does only exist in Illuminate\Database\Eloquent\Builder , but not in Illuminate\Database\Eloq...ns\QueriesRelationships .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
![]() |
|||
254 | ->has('metaAttributes', $operator, 1, $boolean, $this->getMetaWhereConstraint($method, $args)) |
||
255 | ->with('metaAttributes'); |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * Get boolean called on the original method and set it to default. |
||
260 | * |
||
261 | * @param \Sofa\EloquenceArgumentBag $args |
||
262 | * @return string |
||
263 | */ |
||
264 | protected function getMetaBoolean(ArgumentBag $args) |
||
265 | { |
||
266 | $boolean = $args->get('boolean'); |
||
267 | |||
268 | $args->set('boolean', 'and'); |
||
269 | |||
270 | return $boolean; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Determine the operator for count relation query. |
||
275 | * |
||
276 | * @param string $method |
||
277 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
278 | * @return string |
||
279 | */ |
||
280 | protected function getMetaOperator($method, ArgumentBag $args) |
||
281 | { |
||
282 | if ($not = $args->get('not')) { |
||
283 | $args->set('not', false); |
||
284 | } |
||
285 | |||
286 | return ($not ^ $this->isWhereNull($method, $args)) ? '<' : '>='; |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * Integers and floats must be passed in raw form in order to avoid string |
||
291 | * comparison, due to the fact that all meta values are stored as strings. |
||
292 | * |
||
293 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
294 | * @return void |
||
295 | */ |
||
296 | protected function unbindNumerics(ArgumentBag $args) |
||
297 | { |
||
298 | if (($value = $args->get('value')) && (is_int($value) || is_float($value))) { |
||
299 | $args->set('value', $this->raw($value)); |
||
300 | } elseif ($values = $args->get('values')) { |
||
301 | foreach ($values as $key => $value) { |
||
302 | if (is_int($value) || is_float($value)) { |
||
303 | $values[$key] = $this->raw($value); |
||
304 | } |
||
305 | } |
||
306 | |||
307 | $args->set('values', $values); |
||
308 | } |
||
309 | } |
||
310 | |||
311 | /** |
||
312 | * Get the relation constraint closure. |
||
313 | * |
||
314 | * @param string $method |
||
315 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
316 | * @return \Closure |
||
317 | */ |
||
318 | protected function getMetaWhereConstraint($method, ArgumentBag $args) |
||
319 | { |
||
320 | $column = $args->get('column'); |
||
321 | |||
322 | $args->set('column', 'meta_value'); |
||
323 | |||
324 | if ($method === 'whereBetween') { |
||
325 | return $this->getMetaBetweenConstraint($column, $args->get('values')); |
||
326 | } |
||
327 | |||
328 | return function ($query) use ($column, $method, $args) { |
||
329 | $query->where('meta_key', $column); |
||
330 | |||
331 | if ($args->get('value') || $args->get('values')) { |
||
332 | call_user_func_array([$query, $method], $args->all()); |
||
333 | } |
||
334 | }; |
||
335 | } |
||
336 | |||
337 | /** |
||
338 | * Query Builder whereBetween override required to pass raw numeric values. |
||
339 | * |
||
340 | * @param string $column |
||
341 | * @param array $values |
||
342 | * @return \Closure |
||
343 | */ |
||
344 | protected function getMetaBetweenConstraint($column, array $values) |
||
345 | { |
||
346 | $min = $values[0]; |
||
347 | $max = $values[1]; |
||
348 | |||
349 | return function ($query) use ($column, $min, $max) { |
||
350 | $query->where('meta_key', $column) |
||
351 | ->where('meta_value', '>=', $min) |
||
352 | ->where('meta_value', '<=', $max); |
||
353 | }; |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * Save new or updated meta attributes and delete the ones that were unset. |
||
358 | * |
||
359 | * @return void |
||
360 | */ |
||
361 | protected function saveMeta() |
||
362 | { |
||
363 | foreach ($this->getMetaAttributes() as $attribute) { |
||
364 | if (is_null($attribute->getValue())) { |
||
365 | $attribute->delete(); |
||
366 | } else { |
||
367 | $this->metaAttributes()->save($attribute); |
||
368 | } |
||
369 | } |
||
370 | } |
||
371 | |||
372 | /** |
||
373 | * Determine whether meta attribute is allowed for the model. |
||
374 | * |
||
375 | * @param string $key |
||
376 | * @return bool |
||
377 | */ |
||
378 | public function allowsMeta($key) |
||
379 | { |
||
380 | $allowed = $this->getAllowedMeta(); |
||
381 | |||
382 | return empty($allowed) || in_array($key, $allowed); |
||
383 | } |
||
384 | |||
385 | /** |
||
386 | * Determine whether meta attribute exists on the model. |
||
387 | * |
||
388 | * @param string $key |
||
389 | * @return bool |
||
390 | */ |
||
391 | public function hasMeta($key) |
||
392 | { |
||
393 | return array_key_exists($key, $this->getMetaAttributesArray()); |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * Get meta attribute value. |
||
398 | * |
||
399 | * @param string $key |
||
400 | * @return mixed |
||
401 | */ |
||
402 | public function getMeta($key) |
||
403 | { |
||
404 | return $this->getMetaAttributes()->getValue($key); |
||
405 | } |
||
406 | |||
407 | /** |
||
408 | * Get meta attribute values by group. |
||
409 | * |
||
410 | * @param string $key |
||
411 | * @return mixed |
||
412 | */ |
||
413 | public function getMetaByGroup($group) |
||
414 | { |
||
415 | return $this->getMetaAttributes()->getMetaByGroup($group); |
||
416 | } |
||
417 | |||
418 | /** |
||
419 | * Set meta attribute. |
||
420 | * |
||
421 | * @param string $key |
||
422 | * @param mixed $value |
||
423 | * @return void |
||
424 | */ |
||
425 | public function setMeta($key, $value, $group = null) |
||
426 | { |
||
427 | $this->getMetaAttributes()->set($key, $value, $group); |
||
428 | } |
||
429 | |||
430 | /** |
||
431 | * Meta attributes relation. |
||
432 | * |
||
433 | * @codeCoverageIgnore |
||
434 | * |
||
435 | * @return \Illuminate\Database\Eloquent\Relations\MorphMany |
||
436 | */ |
||
437 | public function metaAttributes() |
||
438 | { |
||
439 | return $this->morphMany('Sofa\Eloquence\Metable\Attribute', 'metable'); |
||
440 | } |
||
441 | |||
442 | /** |
||
443 | * Get meta attributes as collection. |
||
444 | * |
||
445 | * @return \Sofa\Eloquence\Metable\AttributeBag |
||
446 | */ |
||
447 | public function getMetaAttributes() |
||
448 | { |
||
449 | $this->loadMetaAttributes(); |
||
450 | |||
451 | return $this->getRelation('metaAttributes'); |
||
452 | } |
||
453 | |||
454 | /** |
||
455 | * Accessor for metaAttributes property |
||
456 | * |
||
457 | * @return \Sofa\Eloquence\Metable\AttributeBag |
||
458 | */ |
||
459 | public function getMetaAttributesAttribute() |
||
460 | { |
||
461 | return $this->getMetaAttributes(); |
||
462 | } |
||
463 | |||
464 | /** |
||
465 | * Get meta attributes as associative array. |
||
466 | * |
||
467 | * @return array |
||
468 | */ |
||
469 | public function getMetaAttributesArray() |
||
470 | { |
||
471 | return $this->getMetaAttributes()->toArray(); |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * Load meta attributes relation. |
||
476 | * |
||
477 | * @return void |
||
478 | */ |
||
479 | protected function loadMetaAttributes() |
||
480 | { |
||
481 | if (!array_key_exists('metaAttributes', $this->relations)) { |
||
0 ignored issues
–
show
The property
relations does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
482 | $this->reloadMetaAttributes(); |
||
483 | } |
||
484 | |||
485 | $attributes = $this->getRelation('metaAttributes'); |
||
486 | |||
487 | if (!$attributes instanceof AttributeBag) { |
||
488 | $this->setRelation('metaAttributes', (new Attribute)->newBag($attributes->all())); |
||
489 | } |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * Reload meta attributes from db or set empty bag for newly created model. |
||
494 | * |
||
495 | * @return $this |
||
496 | */ |
||
497 | protected function reloadMetaAttributes() |
||
498 | { |
||
499 | return ($this->exists) |
||
0 ignored issues
–
show
The property
exists does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
500 | ? $this->load('metaAttributes') |
||
501 | : $this->setRelation('metaAttributes', (new Attribute)->newBag()); |
||
502 | } |
||
503 | |||
504 | /** |
||
505 | * Get allowed meta attributes array. |
||
506 | * |
||
507 | * @return array |
||
508 | */ |
||
509 | public function getAllowedMeta() |
||
510 | { |
||
511 | return (property_exists($this, 'allowedMeta')) ? $this->allowedMeta : []; |
||
512 | } |
||
513 | } |
||
514 |
This check marks calls to methods that do not seem to exist on an object.
This is most likely the result of a method being renamed without all references to it being renamed likewise.