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 defined('SYSPATH') OR die('No direct script access.'); |
||
2 | |||
3 | /** |
||
4 | * Represents an array of models. Lazy loaded from a Jam_Query_Builder_Collection, and can be changed, checked and saved at once. |
||
5 | * |
||
6 | * @package Jam |
||
7 | * @category Associations |
||
8 | * @author Ivan Kerin |
||
9 | * @copyright (c) 2011-2012 Despark Ltd. |
||
10 | * @license http://www.opensource.org/licenses/isc-license.txt |
||
11 | */ |
||
12 | abstract class Kohana_Jam_Array_Model extends Jam_Array { |
||
13 | |||
14 | public static function factory() |
||
15 | { |
||
16 | return new Jam_Array_Model(); |
||
17 | } |
||
18 | |||
19 | /** |
||
20 | * Convert a collection to an array, keep an array or make a Jam_Model to an array(Jam_Model) |
||
21 | * @param mixed $collection |
||
22 | * @return array |
||
23 | */ |
||
24 | 20 | public static function convert_collection_to_array($collection) |
|
25 | { |
||
26 | 20 | if ($collection instanceof Jam_Query_Builder_Collection OR $collection instanceof Jam_Array_Model) |
|
27 | { |
||
28 | 6 | $array = $collection->as_array(); |
|
29 | } |
||
30 | 19 | elseif ( ! is_array($collection)) |
|
31 | { |
||
32 | 12 | $array = array($collection); |
|
33 | } |
||
34 | else |
||
35 | { |
||
36 | 9 | $array = $collection; |
|
37 | } |
||
38 | 20 | return array_values(array_filter($array)); |
|
39 | } |
||
40 | |||
41 | /** |
||
42 | * A collection used to load the content |
||
43 | * @var Jam_Query_Builder_Collection |
||
44 | */ |
||
45 | protected $_collection; |
||
46 | |||
47 | /** |
||
48 | * The name of the models in this iterator |
||
49 | * @var string |
||
50 | */ |
||
51 | protected $_model; |
||
52 | |||
53 | /** |
||
54 | * The original content, loaded from the database |
||
55 | * @var array |
||
56 | */ |
||
57 | protected $_original; |
||
58 | |||
59 | /** |
||
60 | * This is set if the whole collection has been replaced. |
||
61 | * @var boolean |
||
62 | */ |
||
63 | protected $_replace = FALSE; |
||
64 | |||
65 | /** |
||
66 | * Getter / Setter of the model name |
||
67 | * @param string $model |
||
68 | * @return string |
||
69 | */ |
||
70 | 754 | public function model($model = NULL) |
|
71 | { |
||
72 | 754 | if ($model !== NULL) |
|
73 | { |
||
74 | 754 | $this->_model = $model; |
|
75 | 754 | return $this; |
|
76 | } |
||
77 | 24 | return $this->_model; |
|
78 | } |
||
79 | |||
80 | /** |
||
81 | * Getter of the meta object for this iterator (based on $_model) |
||
82 | * @return Jam_Model |
||
83 | * @throws Kohana_Exception If $_model not present |
||
84 | */ |
||
85 | 18 | public function meta() |
|
86 | { |
||
87 | 18 | if ( ! $this->model()) |
|
88 | throw new Kohana_Exception('Model not set'); |
||
89 | |||
90 | 18 | return Jam::meta($this->model()); |
|
0 ignored issues
–
show
|
|||
91 | } |
||
92 | |||
93 | /** |
||
94 | * Getter / Setter of the collection, used to lazy load the data for this iterator |
||
95 | * @param Jam_Query_Builder_Collection $collection |
||
96 | * @return Jam_Query_Builder_Collection |
||
97 | */ |
||
98 | 18 | public function collection(Jam_Query_Builder_Collection $collection = NULL) |
|
99 | { |
||
100 | 18 | if ($collection !== NULL) |
|
101 | { |
||
102 | 18 | $this->_collection = $collection; |
|
103 | 18 | return $this; |
|
0 ignored issues
–
show
The return type of
return $this; (Kohana_Jam_Array_Model ) is incompatible with the return type documented by Kohana_Jam_Array_Model::collection of type Jam_Query_Builder_Collection .
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
Loading history...
|
|||
104 | } |
||
105 | |||
106 | 16 | return $this->_collection; |
|
107 | } |
||
108 | |||
109 | /** |
||
110 | * Load the content from the database, using $_collection. |
||
111 | * If some items have been added to the iterator before it has been loaded, merge the results |
||
112 | * @throws Kohana_Exception If the $_collection has not been loaded |
||
113 | */ |
||
114 | 27 | protected function _load_content() |
|
115 | { |
||
116 | 27 | if ($this->_original === NULL) |
|
117 | { |
||
118 | 27 | if ( ! $this->collection()) |
|
119 | throw new Kohana_Exception('Cannot load content because collection not loaded for Jam_Array(:model)', array(':model' => $this->model())); |
||
120 | |||
121 | 27 | $collection = clone $this->collection(); |
|
122 | |||
123 | 27 | $this->_original = $collection->result()->as_array(); |
|
124 | |||
125 | 27 | if ( ! $this->_replace) |
|
126 | { |
||
127 | 22 | if ($this->_content !== NULL) |
|
128 | { |
||
129 | 1 | $this->_content = array_merge($this->_content, $this->_original); |
|
130 | } |
||
131 | else |
||
132 | { |
||
133 | 21 | $this->_content = $this->_original; |
|
134 | } |
||
135 | } |
||
136 | } |
||
137 | 27 | } |
|
138 | |||
139 | public function reload() |
||
140 | { |
||
141 | $this->_original = NULL; |
||
0 ignored issues
–
show
It seems like
NULL of type null is incompatible with the declared type array of property $_original .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
142 | $this->_replace = FALSE; |
||
143 | return parent::reload(); |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * Load an item from the database, based on a unique key |
||
148 | * @param string $key |
||
149 | * @return Jam_Model |
||
150 | */ |
||
151 | protected function _find_item($key) |
||
152 | { |
||
153 | return Jam::find($this->model(), $key); |
||
0 ignored issues
–
show
It seems like
$this->model() targeting Kohana_Jam_Array_Model::model() can also be of type this<Kohana_Jam_Array_Model> ; however, Kohana_Jam::find() does only seem to accept string , maybe add an additional type check?
This check looks at variables that are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble.
Loading history...
|
|||
154 | } |
||
155 | |||
156 | /** |
||
157 | * Convert an item from the $_content to a Jam_Model |
||
158 | * @param mixed $value |
||
159 | * @param boolean $is_changed |
||
160 | * @param int $offset |
||
161 | * @return Jam_Model |
||
162 | */ |
||
163 | 19 | protected function _load_item($value, $is_changed, $offset) |
|
164 | { |
||
165 | 19 | if ($value instanceof Jam_Model OR ! $value) |
|
166 | { |
||
167 | 12 | $item = $value; |
|
168 | } |
||
169 | 12 | elseif ( ! is_array($value)) |
|
170 | { |
||
171 | $item = $this->_find_item($value); |
||
172 | } |
||
173 | 12 | elseif (is_array($value) AND $is_changed) |
|
174 | { |
||
175 | $key = $this->meta()->primary_key(); |
||
176 | if (isset($value[$key]) AND $value[$key]) |
||
177 | { |
||
178 | $item_key = is_array($value[$key]) ? reset($value[$key]) : $value[$key]; |
||
179 | |||
180 | $model = $this->_find_item($item_key); |
||
181 | if ( ! $model) |
||
182 | { |
||
183 | $model = clone Jam::build_template($this->model(), $value); |
||
184 | } |
||
185 | else |
||
186 | { |
||
187 | unset($value[$key]); |
||
188 | } |
||
189 | } |
||
190 | else |
||
191 | { |
||
192 | $model = clone Jam::build_template($this->model(), $value); |
||
193 | } |
||
194 | |||
195 | $item = $model->set($value); |
||
196 | } |
||
197 | else |
||
198 | { |
||
199 | 12 | $item = clone Jam::build_template($this->model(), $value); |
|
200 | 12 | $item = $item->load_fields($value); |
|
201 | } |
||
202 | |||
203 | 19 | $this->_content[$offset] = $item; |
|
204 | |||
205 | 19 | return $item; |
|
206 | } |
||
207 | |||
208 | /** |
||
209 | * Find out the primary_key of an item of the $_content |
||
210 | * @param mixed $value |
||
211 | * @return int |
||
212 | */ |
||
213 | 21 | protected function _id($value) |
|
214 | { |
||
215 | 21 | if ($value instanceof Jam_Model) |
|
216 | 14 | return $value->id(); |
|
217 | |||
218 | 15 | if (is_numeric($value)) |
|
219 | 6 | return (int) $value; |
|
220 | |||
221 | 13 | if (is_string($value) AND $item = $this->_find_item($value)) |
|
222 | return $item->id(); |
||
223 | |||
224 | 13 | if (is_array($value) AND isset($value[$this->meta()->primary_key()])) |
|
225 | 13 | return (int) $value[$this->meta()->primary_key()]; |
|
226 | } |
||
227 | |||
228 | 15 | public function search($item) |
|
229 | { |
||
230 | 15 | $search_id = $this->_id($item); |
|
231 | |||
232 | 15 | if ( ! $search_id) |
|
233 | 6 | return NULL; |
|
234 | |||
235 | 10 | $this->_load_content(); |
|
236 | |||
237 | 10 | if ($this->_content) |
|
0 ignored issues
–
show
The expression
$this->_content of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using
Loading history...
|
|||
238 | { |
||
239 | 10 | foreach ($this->_content as $offset => $current) |
|
240 | { |
||
241 | 10 | if ($this->_id($current) === $search_id) |
|
242 | { |
||
243 | 10 | return (int) $offset; |
|
244 | } |
||
245 | } |
||
246 | } |
||
247 | |||
248 | 5 | return NULL; |
|
249 | } |
||
250 | |||
251 | 12 | public function as_array($key = NULL, $value = NULL) |
|
252 | { |
||
253 | 12 | $results = array(); |
|
254 | 12 | $key = Jam_Query_Builder::resolve_meta_attribute($key, $this->meta()); |
|
255 | 12 | $value = Jam_Query_Builder::resolve_meta_attribute($value, $this->meta()); |
|
256 | |||
257 | 12 | foreach ($this as $i => $item) |
|
258 | { |
||
259 | 9 | $results[$key ? $item->$key : $i] = $value ? $item->$value : $item; |
|
260 | } |
||
261 | |||
262 | 12 | return $results; |
|
263 | } |
||
264 | |||
265 | 6 | public function ids() |
|
266 | { |
||
267 | 6 | $this->_load_content(); |
|
268 | |||
269 | 6 | return $this->_content ? array_filter(array_map(array($this, '_id'), $this->_content)) : array(); |
|
270 | } |
||
271 | |||
272 | 7 | public function load_fields(array $data) |
|
273 | { |
||
274 | 7 | $this->_content = $this->_original = $data; |
|
275 | 7 | return $this; |
|
276 | } |
||
277 | |||
278 | public function original() |
||
279 | { |
||
280 | return $this->_original; |
||
281 | } |
||
282 | |||
283 | 1 | public function original_ids() |
|
284 | { |
||
285 | 1 | $this->_load_content(); |
|
286 | |||
287 | 1 | return $this->_original ? array_filter(array_map(array($this, '_id'), $this->_original)) : array(); |
|
288 | } |
||
289 | |||
290 | 3 | public function has($item) |
|
291 | { |
||
292 | 3 | return $this->search($item) !== NULL; |
|
293 | } |
||
294 | |||
295 | 8 | public function set($items) |
|
296 | { |
||
297 | 8 | $this->_content = Jam_Array_Model::convert_collection_to_array($items); |
|
298 | 8 | $this->_changed = count($this->_content) ? array_fill(0, count($this->_content), TRUE) : array(); |
|
299 | 8 | $this->_replace = TRUE; |
|
300 | 8 | $this->_removed = TRUE; |
|
301 | |||
302 | 8 | return $this; |
|
303 | } |
||
304 | |||
305 | 8 | public function add($items) |
|
306 | { |
||
307 | 8 | $items = Jam_Array_Model::convert_collection_to_array($items); |
|
308 | |||
309 | 8 | foreach ($items as $item) |
|
310 | { |
||
311 | 8 | $this->offsetSet($this->search($item), $item); |
|
312 | } |
||
313 | |||
314 | 8 | return $this; |
|
315 | } |
||
316 | |||
317 | 6 | public function remove($items) |
|
318 | { |
||
319 | 6 | $items = Jam_Array_Model::convert_collection_to_array($items); |
|
320 | |||
321 | 6 | foreach ($items as $item) |
|
322 | { |
||
323 | 6 | if (($offset = $this->search($item)) !== NULL) |
|
324 | { |
||
325 | 6 | $this->offsetUnset($offset); |
|
326 | } |
||
327 | } |
||
328 | |||
329 | 6 | return $this; |
|
330 | } |
||
331 | |||
332 | /** |
||
333 | * Build a new Jam Model, add it to the collection and return the newly built model |
||
334 | * @param array $values set values on the new model |
||
335 | * @return Jam_Model |
||
336 | */ |
||
337 | 1 | public function build(array $values = NULL) |
|
338 | { |
||
339 | 1 | $item = clone Jam::build_template($this->model(), $values); |
|
340 | 1 | if ($values) |
|
341 | { |
||
342 | 1 | $item->set($values); |
|
343 | } |
||
344 | 1 | $this->add($item); |
|
345 | |||
346 | 1 | return $item; |
|
347 | } |
||
348 | |||
349 | /** |
||
350 | * The same as build but saves the model in the database |
||
351 | * @param array $values |
||
352 | * @return Jam_Model |
||
353 | */ |
||
354 | public function create(array $values = NULL) |
||
355 | { |
||
356 | return $this->build($values)->save(); |
||
357 | } |
||
358 | |||
359 | 1 | public function check_changed() |
|
360 | { |
||
361 | 1 | $check = TRUE; |
|
362 | |||
363 | 1 | foreach ($this->_changed as $offset => $is_changed) |
|
364 | { |
||
365 | 1 | $item = $this->offsetGet($offset); |
|
366 | 1 | if ($is_changed AND $item AND ! $item->is_validating() AND ! $item->check()) |
|
367 | { |
||
368 | 1 | $check = FALSE; |
|
369 | } |
||
370 | } |
||
371 | |||
372 | 1 | return $check; |
|
373 | } |
||
374 | |||
375 | 1 | public function save_changed() |
|
376 | { |
||
377 | 1 | foreach ($this->_changed as $offset => $is_changed) |
|
378 | { |
||
379 | 1 | $item = $this->offsetGet($offset); |
|
380 | 1 | if ($is_changed AND $item AND ! $item->is_saving()) |
|
381 | { |
||
382 | 1 | $item->save(); |
|
383 | } |
||
384 | } |
||
385 | |||
386 | 1 | return $this; |
|
387 | } |
||
388 | |||
389 | 1 | public function clear() |
|
390 | { |
||
391 | 1 | $this->_changed = |
|
392 | 1 | $this->_original = |
|
393 | 1 | $this->_content = array(); |
|
394 | |||
395 | 1 | return $this; |
|
396 | } |
||
397 | |||
398 | public function clear_changed() |
||
399 | { |
||
400 | $this->_changed = array(); |
||
401 | $this->_original = $this->_content; |
||
402 | |||
403 | return $this; |
||
404 | } |
||
405 | |||
406 | 16 | public function __toString() |
|
407 | { |
||
408 | 16 | if ( ! $this->collection()) |
|
409 | return 'Jam_Array ('.$this->model().')[NOT LOADED COLLECTION]'; |
||
410 | |||
411 | 16 | return $this->collection()->__toString(); |
|
412 | } |
||
413 | |||
414 | 2 | public function __call($method, $args) |
|
415 | { |
||
416 | 2 | if ( ! $this->collection()) |
|
417 | throw new Kohana_Exception('Cannot call method :method on collection because collection not loaded for Jam_Array(:model)', array(':method' => $method, ':model' => $this->model())); |
||
418 | |||
419 | 2 | return call_user_func_array(array(clone $this->collection(), $method), $args); |
|
420 | } |
||
421 | |||
422 | /** |
||
423 | * Getter for the changed array - check if any or a particular item has been changed |
||
424 | * @param int $offset |
||
425 | * @return bool |
||
426 | */ |
||
427 | 6 | public function changed($offset = NULL) |
|
428 | { |
||
429 | 6 | if ($this->_content) |
|
0 ignored issues
–
show
The expression
$this->_content of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using
Loading history...
|
|||
430 | { |
||
431 | 6 | foreach ($this->_content as $key => $value) |
|
432 | { |
||
433 | 6 | if ( ! isset($this->_changed[$key]) AND $value instanceof Jam_Model AND $value->changed()) |
|
434 | { |
||
435 | 6 | $this->_changed[$key] = TRUE; |
|
436 | } |
||
437 | } |
||
438 | } |
||
439 | |||
440 | 6 | return parent::changed($offset); |
|
441 | } |
||
442 | |||
443 | 754 | public function serialize() |
|
444 | { |
||
445 | 754 | $this->_load_content(); |
|
446 | |||
447 | |||
448 | 754 | return serialize(array( |
|
449 | 754 | 'original' => $this->_original, |
|
450 | 754 | 'model' => $this->_model, |
|
451 | 754 | 'replace' => $this->_replace, |
|
452 | 754 | 'changed' => $this->_changed, |
|
453 | 754 | 'content' => $this->_content, |
|
454 | 754 | 'removed' => $this->_removed, |
|
455 | 754 | 'current' => $this->_current, |
|
456 | )); |
||
457 | } |
||
458 | |||
459 | 754 | public function unserialize($data) |
|
460 | { |
||
461 | 754 | $data = unserialize($data); |
|
462 | |||
463 | 754 | $this->_original = $data['original']; |
|
464 | 754 | $this->_model = $data['model']; |
|
465 | 754 | $this->_replace = $data['replace']; |
|
466 | 754 | $this->_changed = $data['changed']; |
|
467 | 754 | $this->_content = $data['content']; |
|
468 | 754 | $this->_removed = $data['removed']; |
|
469 | 754 | $this->_current = $data['current']; |
|
470 | 754 | } |
|
471 | } |
||
472 |
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.