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 namespace Sofa\Revisionable\Laravel; |
||
2 | |||
3 | use App; |
||
4 | use DateTime; |
||
5 | |||
6 | /** |
||
7 | * @property int revisionsCount |
||
8 | * @property array relations |
||
9 | * @property array original |
||
10 | * @property array attributes |
||
11 | * @property array attributes |
||
12 | * @property array revisionableConnection |
||
13 | * |
||
14 | * @method void created(\Closure|string $callback) |
||
15 | * @method void updated(\Closure|string $callback) |
||
16 | * @method void deleted(\Closure|string $callback) |
||
17 | * @method void restored(\Closure|string $callback) |
||
18 | * @method string getTable() |
||
19 | * @method void load() |
||
20 | * @method mixed getRelation($relation) |
||
21 | * @method \Illuminate\Database\Eloquent\Relations\HasOne hasOne($related, $foreignKey = null, $localKey = null) |
||
22 | * @method \Illuminate\Database\Eloquent\Relations\HasMany hasMany($related, $foreignKey = null, $localKey = null) |
||
23 | */ |
||
24 | trait RevisionableTrait |
||
25 | { |
||
26 | /** |
||
27 | * Revisionable Logger instance. |
||
28 | * |
||
29 | * @var \Sofa\Revisionable\Logger |
||
30 | */ |
||
31 | protected static $revisionableLogger; |
||
32 | |||
33 | /** |
||
34 | * Revisioning switch. |
||
35 | * |
||
36 | * @var boolean |
||
37 | */ |
||
38 | protected $revisioned = true; |
||
39 | |||
40 | /** |
||
41 | * Boot revisionable trait for the model. |
||
42 | * |
||
43 | * @return void |
||
44 | */ |
||
45 | public static function bootRevisionableTrait() |
||
46 | { |
||
47 | static::bootLogger(); |
||
48 | |||
49 | static::registerListeners(); |
||
50 | } |
||
51 | |||
52 | /** |
||
53 | * Register event listeners. |
||
54 | * |
||
55 | * @return void |
||
56 | */ |
||
57 | public static function registerListeners() |
||
58 | { |
||
59 | foreach (static::getRevisionableEvents() as $event) { |
||
60 | static::{"register{$event}Listener"}(); |
||
61 | } |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * Register listener for created event. |
||
66 | * |
||
67 | * @return void |
||
68 | */ |
||
69 | protected static function registerCreatedListener() |
||
70 | { |
||
71 | static::created('Sofa\Revisionable\Listener@onCreated'); |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Register listener for updated event. |
||
76 | * |
||
77 | * @return void |
||
78 | */ |
||
79 | protected static function registerUpdatedListener() |
||
80 | { |
||
81 | static::updated('Sofa\Revisionable\Listener@onUpdated'); |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * Register listener for deleted event. |
||
86 | * |
||
87 | * @return void |
||
88 | */ |
||
89 | protected static function registerDeletedListener() |
||
90 | { |
||
91 | static::deleted('Sofa\Revisionable\Listener@onDeleted'); |
||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Register listener for restored event. |
||
96 | * |
||
97 | * @return void |
||
98 | */ |
||
99 | protected static function registerRestoredListener() |
||
100 | { |
||
101 | if (method_exists(get_called_class(), 'restored')) { |
||
102 | static::restored('Sofa\Revisionable\Listener@onRestored'); |
||
103 | } |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Boot Revisionable Logger. |
||
108 | * |
||
109 | * @return void |
||
110 | */ |
||
111 | public static function bootLogger() |
||
112 | { |
||
113 | if (!static::$revisionableLogger) { |
||
114 | static::setRevisionableLogger(App::make('revisionable.logger')); |
||
115 | } |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Set logger instance. |
||
120 | * |
||
121 | * @param mixed $logger |
||
122 | */ |
||
123 | public static function setRevisionableLogger($logger) |
||
124 | { |
||
125 | static::$revisionableLogger = $logger; |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * Get logger instance. |
||
130 | * |
||
131 | * @return \Sofa\Revisionable\Logger |
||
132 | */ |
||
133 | public static function getRevisionableLogger() |
||
134 | { |
||
135 | return static::$revisionableLogger; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Get the connection for revision logs. |
||
140 | * |
||
141 | * @return \Illuminate\Database\ConnectionInterface |
||
142 | */ |
||
143 | public function getRevisionableConnection() |
||
144 | { |
||
145 | $connection = (isset($this->revisionableConnection)) ? $this->revisionableConnection : null; |
||
146 | |||
147 | return static::resolveConnection($connection); |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Get an array of updated revisionable attributes. |
||
152 | * |
||
153 | * @return array |
||
154 | */ |
||
155 | public function getDiff() |
||
156 | { |
||
157 | $old = $this->getOldAttributes(); |
||
158 | |||
159 | $new = $this->getNewAttributes(); |
||
160 | |||
161 | return array_diff_assoc($new, $old); |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Get an array of original revisionable attributes. |
||
166 | * |
||
167 | * @return array |
||
168 | */ |
||
169 | public function getOldAttributes() |
||
170 | { |
||
171 | $attributes = $this->getRevisionableItems($this->original); |
||
172 | |||
173 | return $this->prepareAttributes($attributes); |
||
174 | } |
||
175 | |||
176 | /** |
||
177 | * Get an array of current revisionable attributes. |
||
178 | * |
||
179 | * @return array |
||
180 | */ |
||
181 | public function getNewAttributes() |
||
182 | { |
||
183 | $attributes = $this->getRevisionableItems($this->attributes); |
||
184 | |||
185 | return $this->prepareAttributes($attributes); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Stringify revisionable attributes. |
||
190 | * |
||
191 | * @param array $attributes |
||
192 | * @return array |
||
193 | */ |
||
194 | protected function prepareAttributes(array $attributes) |
||
195 | { |
||
196 | return array_map(function ($attribute) { |
||
197 | return ($attribute instanceof DateTime) |
||
198 | ? $this->fromDateTime($attribute) |
||
0 ignored issues
–
show
|
|||
199 | : (string) $attribute; |
||
200 | }, $attributes); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * Get an array of revisionable attributes. |
||
205 | * |
||
206 | * @param array $values |
||
207 | * @return array |
||
208 | */ |
||
209 | public function getRevisionableItems(array $values) |
||
210 | { |
||
211 | if (count($this->getRevisionable()) > 0) { |
||
212 | return array_intersect_key($values, array_flip($this->getRevisionable())); |
||
213 | } |
||
214 | |||
215 | return array_diff_key($values, array_flip($this->getNonRevisionable())); |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * Events being tracked. |
||
220 | * |
||
221 | * @var array |
||
222 | */ |
||
223 | protected static function getRevisionableEvents() |
||
224 | { |
||
225 | return (isset(static::$revisionableEvents)) |
||
226 | ? (array) static::$revisionableEvents |
||
227 | : ['Created', 'Updated', 'Deleted', 'Restored']; |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Attributes being revisioned. |
||
232 | * |
||
233 | * @var array |
||
234 | */ |
||
235 | public function getRevisionable() |
||
236 | { |
||
237 | return (isset($this->revisionable)) |
||
238 | ? (array) $this->revisionable |
||
239 | : []; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Attributes hidden from revisioning if revisionable are not provided. |
||
244 | * |
||
245 | * @var array |
||
246 | */ |
||
247 | public function getNonRevisionable() |
||
248 | { |
||
249 | return (isset($this->nonRevisionable)) |
||
250 | ? (array) $this->nonRevisionable |
||
251 | : ['created_at', 'updated_at', 'deleted_at']; |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Determine if model should be revisioned. |
||
256 | * |
||
257 | * @return boolean |
||
258 | */ |
||
259 | public function isRevisioned() |
||
260 | { |
||
261 | return $this->revisioned; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Disable revisioning for current instance. |
||
266 | * |
||
267 | * @return void |
||
268 | */ |
||
269 | public function disableRevisioning() |
||
270 | { |
||
271 | $this->revisioned = false; |
||
272 | } |
||
273 | |||
274 | /** |
||
275 | * Enable revisioning for current instance. |
||
276 | * |
||
277 | * @return void |
||
278 | */ |
||
279 | public function enableRevisioning() |
||
280 | { |
||
281 | $this->revisioned = true; |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Model has many Revision |
||
286 | * |
||
287 | * @return \Illuminate\Database\Eloquent\Relations\HasMany |
||
288 | */ |
||
289 | public function revisions() |
||
290 | { |
||
291 | return $this->hasMany('Sofa\Revisionable\Laravel\Revision', 'row_id') |
||
292 | ->latest() |
||
293 | ->where('table_name', $this->getTable()); |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * Accessor for revisions property |
||
298 | * |
||
299 | * @return \Illuminate\Database\Eloquent\Collection |
||
300 | */ |
||
301 | public function getRevisionsAttribute() |
||
302 | { |
||
303 | $this->loadRelationIfNecessary('revisions'); |
||
304 | |||
305 | $collection = $this->getRelation('revisions'); |
||
306 | |||
307 | $presenter = $this->getRevisionPresenter(); |
||
308 | |||
309 | $default = $this->getDefaultRevisionPresenter(); |
||
310 | |||
311 | if (is_subclass_of($presenter, $default) || $presenter == $default) { |
||
312 | return $presenter::make($collection, $this); |
||
313 | } |
||
314 | |||
315 | return $collection; |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * Get record version at given timestamp. |
||
320 | * |
||
321 | * @param \Carbon\Carbon|string $timestamp |
||
322 | * |
||
323 | * @return \Sofa\Revisionable\Laravel\Revision|\Sofa\Revisionable\laravel\Presenter|null |
||
324 | */ |
||
325 | public function revisionSnapshot($timestamp) |
||
326 | { |
||
327 | $revision = $this->revisions()->where('created_at', '<=', $timestamp)->first(); |
||
328 | |||
329 | return ($revision) ? $this->wrapRevision($revision) : null; |
||
330 | } |
||
331 | |||
332 | /** |
||
333 | * Get record version at given step in history. |
||
334 | * |
||
335 | * @param integer $step |
||
336 | * |
||
337 | * @return \Sofa\Revisionable\Laravel\Revision|\Sofa\Revisionable\laravel\Presenter|null |
||
338 | */ |
||
339 | public function revisionStep($step) |
||
340 | { |
||
341 | $revision = $this->revisions()->skip($step)->first(); |
||
342 | |||
343 | return ($revision) ? $this->wrapRevision($revision) : null; |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * Wrap revision model with the presenter if provided. |
||
348 | * |
||
349 | * @param \Sofa\Revisionable\Laravel\Revision $revision |
||
350 | * @return \Sofa\Revisionable\Laravel\Presenter|\Sofa\Revisionable\Laravel\Revision |
||
351 | */ |
||
352 | public function wrapRevision(Revision $revision) |
||
353 | { |
||
354 | $presenter = $this->getRevisionPresenter(); |
||
355 | |||
356 | return (is_subclass_of($presenter, $this->getDefaultRevisionPresenter()) |
||
357 | || $presenter == $this->getDefaultRevisionPresenter()) |
||
358 | ? $presenter::make($revision, $this) |
||
359 | : $revision; |
||
360 | } |
||
361 | |||
362 | /** |
||
363 | * Load revisions relation if not loaded. |
||
364 | * |
||
365 | * @param string $relation |
||
366 | * |
||
367 | * @return void |
||
368 | */ |
||
369 | protected function loadRelationIfNecessary($relation) |
||
370 | { |
||
371 | if (!array_key_exists($relation, $this->relations)) { |
||
372 | $this->load($relation); |
||
0 ignored issues
–
show
The call to
RevisionableTrait::load() has too many arguments starting with $relation .
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. In this case you can add the ![]() |
|||
373 | } |
||
374 | } |
||
375 | |||
376 | /** |
||
377 | * Revisionable has one Revision count. |
||
378 | * |
||
379 | * @return \Illuminate\Database\Eloquent\Relations\HasOne |
||
380 | */ |
||
381 | public function revisionsCount() |
||
382 | { |
||
383 | return $this->hasOne('Sofa\Revisionable\Laravel\Revision', 'row_id') |
||
384 | ->where('table_name', $this->getTable()) |
||
385 | ->selectRaw('count(*) as aggregate, row_id') |
||
386 | ->groupBy('row_id'); |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Convenient accessor for revisionsCount relation. |
||
391 | * |
||
392 | * @return integer |
||
393 | */ |
||
394 | public function getRevisionsCountAttribute() |
||
395 | { |
||
396 | if (!array_key_exists('revisionsCount', $this->relations)) { |
||
397 | $this->load('revisionsCount'); |
||
0 ignored issues
–
show
The call to
RevisionableTrait::load() has too many arguments starting with 'revisionsCount' .
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. In this case you can add the ![]() |
|||
398 | } |
||
399 | |||
400 | $relation = $this->getRelation('revisionsCount'); |
||
401 | |||
402 | return ($relation) ? (int) $relation->aggregate : 0; |
||
403 | } |
||
404 | |||
405 | /** |
||
406 | * Determine if model has any revisions history. |
||
407 | * |
||
408 | * @return boolean |
||
409 | */ |
||
410 | public function hasRevisions() |
||
411 | { |
||
412 | return (bool) $this->revisionsCount; |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * Determine if model has any revisions history. |
||
417 | * |
||
418 | * @return boolean |
||
419 | */ |
||
420 | public function hasHistory() |
||
421 | { |
||
422 | return $this->hasRevisions(); |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * User has one oldestRevision |
||
427 | * |
||
428 | * @return \Illuminate\Database\Eloquent\Relations\HasOne |
||
429 | */ |
||
430 | public function oldestRevision() |
||
431 | { |
||
432 | return $this->hasOne('Sofa\Revisionable\Laravel\Revision', 'row_id') |
||
433 | ->where('table_name', $this->getTable()) |
||
434 | ->oldest(); |
||
435 | } |
||
436 | |||
437 | /** |
||
438 | * Accessor for oldestRevision property |
||
439 | * |
||
440 | * @return \Sofa\Revisionable\Laravel\Revision|\Sofa\Revisionable\Laravel\Presenter|null |
||
441 | */ |
||
442 | View Code Duplication | public function getOldestRevisionAttribute() |
|
0 ignored issues
–
show
This method seems to be duplicated in 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. ![]() |
|||
443 | { |
||
444 | $this->loadRelationIfNecessary('oldestRevision'); |
||
445 | |||
446 | $revision = $this->getRelation('oldestRevision'); |
||
447 | |||
448 | return ($revision) ? $this->wrapRevision($revision) : null; |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * User has one latestRevision |
||
453 | * |
||
454 | * @return \Illuminate\Database\Eloquent\Relations\HasOne |
||
455 | */ |
||
456 | public function latestRevision() |
||
457 | { |
||
458 | return $this->hasOne('Sofa\Revisionable\Laravel\Revision', 'row_id') |
||
459 | ->where('table_name', $this->getTable()) |
||
460 | ->latest(); |
||
461 | } |
||
462 | |||
463 | /** |
||
464 | * Accessor for latestRevision property |
||
465 | * |
||
466 | * @return \Sofa\Revisionable\Laravel\Revision|\Sofa\Revisionable\Laravel\Presenter|null |
||
467 | */ |
||
468 | View Code Duplication | public function getLatestRevisionAttribute() |
|
0 ignored issues
–
show
This method seems to be duplicated in 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. ![]() |
|||
469 | { |
||
470 | $this->loadRelationIfNecessary('latestRevision'); |
||
471 | |||
472 | $revision = $this->getRelation('latestRevision'); |
||
473 | |||
474 | return ($revision) ? $this->wrapRevision($revision) : null; |
||
475 | } |
||
476 | |||
477 | /** |
||
478 | * Set revision presenter class or true for default. |
||
479 | * |
||
480 | * @param string|true $class |
||
481 | */ |
||
482 | public function setRevisionPresenter($class) |
||
483 | { |
||
484 | $this->revisionPresenter = $class; |
||
0 ignored issues
–
show
The property
revisionPresenter 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;
![]() |
|||
485 | } |
||
486 | |||
487 | /** |
||
488 | * Get revision presenter class for the model. |
||
489 | * |
||
490 | * @return string|null |
||
491 | */ |
||
492 | public function getRevisionPresenter() |
||
493 | { |
||
494 | if (!isset($this->revisionPresenter)) { |
||
495 | return null; |
||
496 | } |
||
497 | |||
498 | // Use default or custom presenter class if provided. |
||
499 | return ($this->revisionPresenter === true) |
||
500 | ? $this->getDefaultRevisionPresenter() |
||
501 | : $this->revisionPresenter; |
||
502 | } |
||
503 | |||
504 | /** |
||
505 | * Get default revision presenter from the package. |
||
506 | * |
||
507 | * @return string |
||
508 | */ |
||
509 | public function getDefaultRevisionPresenter() |
||
510 | { |
||
511 | return 'Sofa\Revisionable\Laravel\Presenter'; |
||
512 | } |
||
513 | } |
||
514 |
This check looks for methods that are used by a trait but not required by it.
To illustrate, let’s look at the following code example
The trait
Idable
provides a methodequalsId
that in turn relies on the methodgetId()
. If this method does not exist on a class mixing in this trait, the method will fail.Adding the
getId()
as an abstract method to the trait will make sure it is available.