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 | class Ajde_Collection extends Ajde_Object_Standard implements Iterator, Countable |
||
4 | { |
||
5 | /** |
||
6 | * @var string |
||
7 | */ |
||
8 | protected $_modelName; |
||
9 | |||
10 | /** |
||
11 | * @var PDO |
||
12 | */ |
||
13 | protected $_connection; |
||
14 | |||
15 | /** |
||
16 | * @var PDOStatement |
||
17 | */ |
||
18 | protected $_statement; |
||
19 | |||
20 | /** |
||
21 | * @var Ajde_Query |
||
22 | */ |
||
23 | protected $_query; |
||
24 | |||
25 | protected $_link = []; |
||
26 | |||
27 | /** |
||
28 | * @var Ajde_Db_Table |
||
29 | */ |
||
30 | protected $_table; |
||
31 | |||
32 | protected $_filters = []; |
||
33 | public $_filterValues = []; |
||
34 | |||
35 | /** |
||
36 | * @var Ajde_Collection_View |
||
37 | */ |
||
38 | protected $_view; |
||
39 | |||
40 | // For Iterator |
||
41 | protected $_items = null; |
||
42 | protected $_position = 0; |
||
43 | |||
44 | private $_sqlInitialized = false; |
||
45 | private $_queryCount; |
||
46 | |||
47 | public static function extendController(Ajde_Controller $controller, $method, $arguments) |
||
0 ignored issues
–
show
|
|||
48 | { |
||
49 | // Register getCollection($name) function on Ajde_Controller |
||
50 | if ($method === 'getCollection') { |
||
51 | return self::getCollection($arguments[0]); |
||
52 | } |
||
53 | // TODO: if last triggered in event cueue, throw exception |
||
54 | // throw new Ajde_Exception("Call to undefined method ".get_class($controller)."::$method()", 90006); |
||
55 | // Now, we give other callbacks in event cueue chance to return |
||
56 | } |
||
57 | |||
58 | public static function getCollection($name) |
||
59 | { |
||
60 | $collectionName = ucfirst($name).'Collection'; |
||
61 | |||
62 | return new $collectionName(); |
||
63 | } |
||
64 | |||
65 | public function __construct() |
||
66 | { |
||
67 | $this->_modelName = str_replace('Collection', '', get_class($this)).'Model'; |
||
68 | $this->_connection = Ajde_Db::getInstance()->getConnection(); |
||
69 | |||
70 | $tableNameCC = str_replace('Collection', '', get_class($this)); |
||
71 | $tableName = $this->fromCamelCase($tableNameCC); |
||
72 | |||
73 | $this->_table = Ajde_Db::getInstance()->getTable($tableName); |
||
74 | $this->_query = new Ajde_Query(); |
||
75 | } |
||
76 | |||
77 | public function reset() |
||
78 | { |
||
79 | parent::reset(); |
||
80 | $this->_query = new Ajde_Query(); |
||
81 | $this->_filters = []; |
||
82 | $this->_filterValues = []; |
||
83 | $this->_items = null; |
||
84 | $this->_position = 0; |
||
85 | $this->_queryCount = null; |
||
86 | $this->_sqlInitialized = false; |
||
87 | } |
||
88 | |||
89 | public function __sleep() |
||
90 | { |
||
91 | return ['_modelName', '_items']; |
||
92 | } |
||
93 | |||
94 | public function __wakeup() |
||
95 | { |
||
96 | } |
||
97 | |||
98 | public function rewind() |
||
99 | { |
||
100 | if (!isset($this->_items)) { |
||
101 | $this->load(); |
||
102 | } |
||
103 | $this->_position = 0; |
||
104 | } |
||
105 | |||
106 | public function current() |
||
107 | { |
||
108 | return $this->_items[$this->_position]; |
||
109 | } |
||
110 | |||
111 | public function key() |
||
112 | { |
||
113 | return $this->_position; |
||
114 | } |
||
115 | |||
116 | public function next() |
||
117 | { |
||
118 | $this->_position++; |
||
119 | } |
||
120 | |||
121 | public function count($query = false) |
||
122 | { |
||
123 | if ($query == true) { |
||
0 ignored issues
–
show
|
|||
124 | if (!isset($this->_queryCount)) { |
||
125 | $this->_statement = $this->getConnection()->prepare($this->getCountSql()); |
||
126 | View Code Duplication | foreach ($this->getFilterValues() as $key => $value) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
127 | if (is_null($value)) { |
||
128 | $this->_statement->bindValue(":$key", null, PDO::PARAM_NULL); |
||
129 | } else { |
||
130 | $this->_statement->bindValue(":$key", $value, PDO::PARAM_STR); |
||
131 | } |
||
132 | } |
||
133 | $this->_statement->execute(); |
||
134 | $result = $this->_statement->fetch(PDO::FETCH_ASSOC); |
||
135 | $this->_queryCount = $result['count']; |
||
136 | } |
||
137 | |||
138 | return $this->_queryCount; |
||
139 | } else { |
||
140 | if (!isset($this->_items)) { |
||
141 | $this->load(); |
||
142 | } |
||
143 | |||
144 | return count($this->_items); |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * @param string $field |
||
150 | * @param mixed $value |
||
151 | * |
||
152 | * @return Ajde_Model | boolean |
||
153 | */ |
||
154 | public function find($field, $value) |
||
155 | { |
||
156 | foreach ($this as $item) { |
||
157 | if ($item->{$field} == $value) { |
||
158 | return $item; |
||
159 | } |
||
160 | } |
||
161 | |||
162 | return false; |
||
0 ignored issues
–
show
The return type of
return false; (false ) is incompatible with the return type documented by Ajde_Collection::find of type Ajde_Model .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
163 | } |
||
164 | |||
165 | public function valid() |
||
166 | { |
||
167 | return isset($this->_items[$this->_position]); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * @return Ajde_Db_PDO |
||
172 | */ |
||
173 | public function getConnection() |
||
174 | { |
||
175 | return $this->_connection; |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * @return Ajde_Db_Table |
||
180 | */ |
||
181 | public function getTable() |
||
182 | { |
||
183 | return $this->_table; |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * @return PDOStatement |
||
188 | */ |
||
189 | public function getStatement() |
||
190 | { |
||
191 | return $this->_statement; |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * @return Ajde_Query |
||
196 | */ |
||
197 | public function getQuery() |
||
198 | { |
||
199 | return $this->_query; |
||
200 | } |
||
201 | |||
202 | public function populate($array) |
||
203 | { |
||
204 | $this->reset(); |
||
205 | $this->_data = $array; |
||
206 | } |
||
207 | |||
208 | public function getLink($modelName, $value) |
||
209 | { |
||
210 | if (!array_key_exists($modelName, $this->_link)) { |
||
211 | // TODO: |
||
212 | throw new Ajde_Exception('Link not defined...'); |
||
213 | } |
||
214 | |||
215 | return new Ajde_Filter_Link($this, $modelName, $this->_link[$modelName], $value); |
||
216 | } |
||
217 | |||
218 | // Chainable collection methods |
||
219 | |||
220 | public function addFilter(Ajde_Filter $filter) |
||
221 | { |
||
222 | $this->_filters[] = $filter; |
||
223 | |||
224 | return $this; |
||
225 | } |
||
226 | |||
227 | public function orderBy($field, $direction = Ajde_Query::ORDER_ASC) |
||
228 | { |
||
229 | $this->getQuery()->addOrderBy($field, $direction); |
||
230 | |||
231 | return $this; |
||
232 | } |
||
233 | |||
234 | public function limit($count, $start = 0) |
||
235 | { |
||
236 | $this->getQuery()->limit((int) $count, (int) $start); |
||
237 | |||
238 | return $this; |
||
239 | } |
||
240 | |||
241 | public function filter($field, $value, $comparison = Ajde_Filter::FILTER_EQUALS, $operator = Ajde_Query::OP_AND) |
||
242 | { |
||
243 | $this->addFilter(new Ajde_Filter_Where($field, $comparison, $value, $operator)); |
||
244 | |||
245 | return $this; |
||
246 | } |
||
247 | |||
248 | // View functions |
||
249 | |||
250 | public function setView(Ajde_Collection_View $view) |
||
251 | { |
||
252 | $this->_view = $view; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * @return Ajde_Collection_View |
||
257 | */ |
||
258 | public function getView() |
||
259 | { |
||
260 | return $this->_view; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * @return bool |
||
265 | */ |
||
266 | public function hasView() |
||
267 | { |
||
268 | return isset($this->_view) && $this->_view instanceof Ajde_Collection_View; |
||
269 | } |
||
270 | |||
271 | public function applyView(Ajde_Collection_View $view = null) |
||
272 | { |
||
273 | if (!$this->hasView() && !isset($view)) { |
||
274 | // TODO: |
||
275 | throw new Ajde_Exception('No view set'); |
||
276 | } |
||
277 | |||
278 | if (isset($view)) { |
||
279 | $this->setView($view); |
||
280 | } else { |
||
281 | $view = $this->getView(); |
||
282 | } |
||
283 | |||
284 | // LIMIT |
||
285 | $this->limit($view->getPageSize(), $view->getRowStart()); |
||
286 | |||
287 | // ORDER BY |
||
288 | if (!$view->isEmpty('orderBy')) { |
||
289 | $oldOrderBy = $this->getQuery()->orderBy; |
||
290 | $this->getQuery()->orderBy = []; |
||
291 | if (in_array($view->getOrderBy(), $this->getTable()->getFieldNames())) { |
||
292 | $this->orderBy((string) $this->getTable().'.'.$view->getOrderBy(), $view->getOrderDir()); |
||
293 | } else { |
||
294 | // custom column, make sure to add it to the query first! |
||
295 | $this->orderBy($view->getOrderBy(), $view->getOrderDir()); |
||
296 | } |
||
297 | foreach ($oldOrderBy as $orderBy) { |
||
298 | $this->orderBy($orderBy['field'], $orderBy['direction']); |
||
299 | } |
||
300 | } |
||
301 | |||
302 | // FILTER |
||
303 | if (!$view->isEmpty('filter')) { |
||
304 | foreach ($view->getFilter() as $fieldName => $filterValue) { |
||
305 | if ($filterValue != '') { |
||
306 | $fieldType = $this->getTable()->getFieldProperties($fieldName, 'type'); |
||
307 | if ($fieldType == Ajde_Db::FIELD_TYPE_DATE) { |
||
308 | // date fields |
||
309 | $start = $filterValue['start'] ? date('Y-m-d H:i:s', |
||
310 | strtotime($filterValue['start'].' 00:00:00')) : false; |
||
311 | $end = $filterValue['end'] ? date('Y-m-d H:i:s', |
||
312 | strtotime($filterValue['end'].' 23:59:59')) : false; |
||
313 | View Code Duplication | if ($start) { |
|
0 ignored issues
–
show
The expression
$start of type string|false is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
314 | $this->addFilter(new Ajde_Filter_Where((string) $this->getTable().'.'.$fieldName, |
||
315 | Ajde_Filter::FILTER_GREATEROREQUAL, $start)); |
||
316 | } |
||
317 | View Code Duplication | if ($end) { |
|
0 ignored issues
–
show
The expression
$end of type string|false is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
318 | $this->addFilter(new Ajde_Filter_Where((string) $this->getTable().'.'.$fieldName, |
||
319 | Ajde_Filter::FILTER_LESSOREQUAL, $end)); |
||
320 | } |
||
321 | } else { |
||
322 | if ($fieldType == Ajde_Db::FIELD_TYPE_TEXT) { |
||
323 | // text fields (fuzzy) |
||
324 | $this->addFilter(new Ajde_Filter_Where((string) $this->getTable().'.'.$fieldName, |
||
325 | Ajde_Filter::FILTER_LIKE, '%'.$filterValue.'%')); |
||
326 | } else { |
||
327 | // non-date fields (exact match) |
||
328 | $this->addFilter(new Ajde_Filter_Where((string) $this->getTable().'.'.$fieldName, |
||
329 | Ajde_Filter::FILTER_EQUALS, $filterValue)); |
||
330 | } |
||
331 | } |
||
332 | } |
||
333 | } |
||
334 | } |
||
335 | |||
336 | // SEARCH |
||
337 | if (!$view->isEmpty('search')) { |
||
338 | $this->addTextFilter($view->getSearch()); |
||
339 | } |
||
340 | } |
||
341 | |||
342 | public function addTextFilter($text, $operator = Ajde_Query::OP_AND, $condition = Ajde_Filter::CONDITION_WHERE) |
||
343 | { |
||
344 | $searchFilter = $this->getTextFilterGroup($text, $operator, $condition); |
||
345 | if ($searchFilter !== false) { |
||
346 | $this->addFilter($searchFilter); |
||
347 | } else { |
||
348 | $this->addFilter(new Ajde_Filter_Where('true', '=', 'false')); |
||
349 | } |
||
350 | } |
||
351 | |||
352 | public function getTextFilterGroup($text, $operator = Ajde_Query::OP_AND, $condition = Ajde_Filter::CONDITION_WHERE) |
||
353 | { |
||
354 | $groupClass = 'Ajde_Filter_'.ucfirst($condition).'Group'; |
||
355 | $filterClass = 'Ajde_Filter_'.ucfirst($condition); |
||
356 | |||
357 | $searchFilter = new $groupClass($operator); |
||
358 | $fieldOptions = $this->getTable()->getFieldProperties(); |
||
359 | foreach ($fieldOptions as $fieldName => $fieldProperties) { |
||
360 | switch ($fieldProperties['type']) { |
||
361 | case Ajde_Db::FIELD_TYPE_TEXT: |
||
362 | View Code Duplication | case Ajde_Db::FIELD_TYPE_ENUM: |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
363 | $searchFilter->addFilter(new $filterClass((string) $this->getTable().'.'.$fieldName, |
||
364 | Ajde_Filter::FILTER_LIKE, '%'.$text.'%', Ajde_Query::OP_OR)); |
||
365 | break; |
||
366 | View Code Duplication | case Ajde_Db::FIELD_TYPE_NUMERIC: |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
367 | $searchFilter->addFilter(new $filterClass('CAST('.(string) $this->getTable().'.'.$fieldName.' AS CHAR)', |
||
368 | Ajde_Filter::FILTER_LIKE, '%'.$text.'%', Ajde_Query::OP_OR)); |
||
369 | break; |
||
370 | default: |
||
371 | break; |
||
372 | } |
||
373 | } |
||
374 | |||
375 | return $searchFilter->hasFilters() ? $searchFilter : false; |
||
376 | } |
||
377 | |||
378 | public function getSql() |
||
379 | { |
||
380 | if (!$this->_sqlInitialized) { |
||
381 | foreach ($this->getTable()->getFieldNames() as $field) { |
||
382 | $this->getQuery()->addSelect((string) $this->getTable().'.'.$field); |
||
383 | } |
||
384 | if (!empty($this->_filters)) { |
||
385 | foreach ($this->getFilter('select') as $select) { |
||
386 | call_user_func_array([$this->getQuery(), 'addSelect'], $select); |
||
387 | } |
||
388 | } |
||
389 | $this->getQuery()->addFrom($this->_table); |
||
390 | if (!empty($this->_filters)) { |
||
391 | foreach ($this->getFilter('join') as $join) { |
||
392 | call_user_func_array([$this->getQuery(), 'addJoin'], $join); |
||
393 | } |
||
394 | foreach ($this->getFilter('where') as $where) { |
||
395 | call_user_func_array([$this->getQuery(), 'addWhere'], $where); |
||
396 | } |
||
397 | foreach ($this->getFilter('having') as $having) { |
||
398 | call_user_func_array([$this->getQuery(), 'addHaving'], $having); |
||
399 | } |
||
400 | } |
||
401 | } |
||
402 | $this->_sqlInitialized = true; |
||
403 | |||
404 | return $this->getQuery()->getSql(); |
||
405 | } |
||
406 | |||
407 | public function getCountSql() |
||
408 | { |
||
409 | // Make sure to load the filters |
||
410 | $this->getSql(); |
||
411 | $query = clone $this->getQuery(); |
||
412 | /* @var $query Ajde_Query */ |
||
413 | $query->select = []; |
||
414 | $query->orderBy = []; |
||
415 | $query->limit = ['start' => null, 'count' => null]; |
||
416 | $query->addSelect('COUNT(*) AS count'); |
||
417 | |||
418 | return $query->getSql(); |
||
419 | } |
||
420 | |||
421 | public function getEmulatedSql() |
||
422 | { |
||
423 | return Ajde_Db_PDOStatement::getEmulatedSql($this->getSql(), $this->getFilterValues()); |
||
424 | } |
||
425 | |||
426 | public function getFilter($queryPart) |
||
427 | { |
||
428 | $arguments = []; |
||
429 | foreach ($this->_filters as $filter) { |
||
430 | $prepared = $filter->prepare($this->getTable()); |
||
431 | if (isset($prepared[$queryPart])) { |
||
432 | if (isset($prepared[$queryPart]['values'])) { |
||
433 | $this->_filterValues = array_merge($this->_filterValues, $prepared[$queryPart]['values']); |
||
434 | } |
||
435 | $arguments[] = $prepared[$queryPart]['arguments']; |
||
436 | } |
||
437 | } |
||
438 | if (empty($arguments)) { |
||
439 | return []; |
||
440 | } else { |
||
441 | return $arguments; |
||
442 | } |
||
443 | } |
||
444 | |||
445 | public function getFilterValues() |
||
446 | { |
||
447 | return $this->_filterValues; |
||
448 | } |
||
449 | |||
450 | // Load the collection |
||
451 | public function load() |
||
452 | { |
||
453 | if (!$this->getConnection() instanceof Ajde_Db_PDO) { |
||
454 | // return false; |
||
455 | } |
||
456 | $this->_statement = $this->getConnection()->prepare($this->getSql()); |
||
457 | View Code Duplication | foreach ($this->getFilterValues() as $key => $value) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
458 | if (is_null($value)) { |
||
459 | $this->_statement->bindValue(":$key", null, PDO::PARAM_NULL); |
||
460 | } else { |
||
461 | $this->_statement->bindValue(":$key", $value, PDO::PARAM_STR); |
||
462 | } |
||
463 | } |
||
464 | $this->_statement->execute(); |
||
465 | |||
466 | return $this->_items = $this->_statement->fetchAll(PDO::FETCH_CLASS, $this->_modelName); |
||
467 | } |
||
468 | |||
469 | public function first() |
||
470 | { |
||
471 | $this->limit(1); |
||
472 | $items = $this->load(); |
||
473 | |||
474 | if (count($items)) { |
||
475 | return $items[0]; |
||
476 | } |
||
477 | } |
||
478 | |||
479 | public function loadParents() |
||
480 | { |
||
481 | if (count($this) > 0) { |
||
482 | foreach ($this as $model) { |
||
483 | $model->loadParents(); |
||
484 | } |
||
485 | } |
||
486 | } |
||
487 | |||
488 | public function length() |
||
489 | { |
||
490 | if (!isset($this->_items)) { |
||
491 | $this->load(); |
||
492 | } |
||
493 | |||
494 | return count($this->_items); |
||
495 | } |
||
496 | |||
497 | public function hash() |
||
498 | { |
||
499 | $str = ''; |
||
500 | /** @var $item Ajde_Model */ |
||
501 | foreach ($this as $item) { |
||
502 | $str .= implode('', $item->valuesAsSingleDimensionArray()); |
||
503 | } |
||
504 | |||
505 | return md5($str); |
||
506 | } |
||
507 | |||
508 | public function toArray() |
||
509 | { |
||
510 | $array = []; |
||
511 | foreach ($this as $item) { |
||
512 | $array[] = $item->values(); |
||
513 | } |
||
514 | |||
515 | return $array; |
||
516 | } |
||
517 | |||
518 | public function items() |
||
519 | { |
||
520 | if (!isset($this->_items)) { |
||
521 | $this->load(); |
||
522 | } |
||
523 | |||
524 | return $this->_items; |
||
525 | } |
||
526 | |||
527 | public function add($item) |
||
528 | { |
||
529 | $this->_items[] = $item; |
||
530 | } |
||
531 | |||
532 | public function combine(Ajde_Collection $collection) |
||
533 | { |
||
534 | foreach ($collection as $item) { |
||
535 | $this->add($item); |
||
536 | } |
||
537 | |||
538 | return $this; |
||
539 | } |
||
540 | |||
541 | public function deleteAll() |
||
542 | { |
||
543 | foreach ($this as $item) { |
||
544 | $item->delete(); |
||
545 | } |
||
546 | } |
||
547 | } |
||
548 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.