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 Neurony\Revisions\Traits; |
||
4 | |||
5 | use Closure; |
||
6 | use Exception; |
||
7 | use Illuminate\Database\Eloquent\Model; |
||
8 | use Illuminate\Database\Eloquent\SoftDeletes; |
||
9 | use Illuminate\Support\Arr; |
||
10 | use Illuminate\Support\Facades\DB; |
||
11 | use Neurony\Revisions\Contracts\RevisionModelContract; |
||
12 | use Neurony\Revisions\Helpers\RelationHelper; |
||
13 | use Neurony\Revisions\Models\Revision; |
||
14 | use Neurony\Revisions\Options\RevisionOptions; |
||
15 | |||
16 | trait HasRevisions |
||
17 | { |
||
18 | use SaveRevisionJsonRepresentation; |
||
19 | use RollbackRevisionJsonRepresentation; |
||
20 | |||
21 | /** |
||
22 | * The container for all the options necessary for this trait. |
||
23 | * Options can be viewed in the Neurony\Revisions\Options\RevisionOptions file. |
||
24 | * |
||
25 | * @var RevisionOptions |
||
26 | */ |
||
27 | protected $revisionOptions; |
||
28 | |||
29 | /** |
||
30 | * Set the options for the HasRevisions trait. |
||
31 | * |
||
32 | * @return RevisionOptions |
||
33 | */ |
||
34 | abstract public function getRevisionOptions(): RevisionOptions; |
||
35 | |||
36 | /** |
||
37 | * Boot the trait. |
||
38 | * Remove blocks on save and delete if one or many locations from model's instance have been changed/removed. |
||
39 | * |
||
40 | * @return void |
||
41 | */ |
||
42 | public static function bootHasRevisions() |
||
43 | { |
||
44 | static::created(function (Model $model) { |
||
45 | $model->createNewRevision(); |
||
46 | }); |
||
47 | |||
48 | static::updated(function (Model $model) { |
||
49 | $model->createNewRevision(); |
||
50 | }); |
||
51 | |||
52 | static::deleted(function (Model $model) { |
||
53 | if ($model->forceDeleting !== false) { |
||
54 | $model->deleteAllRevisions(); |
||
55 | } |
||
56 | }); |
||
57 | } |
||
58 | |||
59 | /** |
||
60 | * Register a revisioning model event with the dispatcher. |
||
61 | * |
||
62 | * @param Closure|string $callback |
||
63 | * @return void |
||
64 | */ |
||
65 | public static function revisioning($callback): void |
||
66 | { |
||
67 | static::registerModelEvent('revisioning', $callback); |
||
68 | } |
||
69 | |||
70 | /** |
||
71 | * Register a revisioned model event with the dispatcher. |
||
72 | * |
||
73 | * @param Closure|string $callback |
||
74 | * @return void |
||
75 | */ |
||
76 | public static function revisioned($callback): void |
||
77 | { |
||
78 | static::registerModelEvent('revisioned', $callback); |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Get all the revisions for a given model instance. |
||
83 | * |
||
84 | * @return \Illuminate\Database\Eloquent\Relations\MorphMany |
||
85 | */ |
||
86 | public function revisions() |
||
87 | { |
||
88 | $revision = config('revisions.revision_model', Revision::class); |
||
89 | |||
90 | return $this->morphMany($revision, 'revisionable'); |
||
0 ignored issues
–
show
|
|||
91 | } |
||
92 | |||
93 | /** |
||
94 | * Create a new revision record for the model instance. |
||
95 | * |
||
96 | * @return Revision|bool |
||
97 | * @throws Exception |
||
98 | */ |
||
99 | public function createNewRevision() |
||
100 | { |
||
101 | $this->initRevisionOptions(); |
||
102 | |||
103 | if ($this->wasRecentlyCreated && $this->revisionOptions->revisionOnCreate !== true) { |
||
0 ignored issues
–
show
The property
wasRecentlyCreated 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;
![]() |
|||
104 | return; |
||
105 | } |
||
106 | |||
107 | try { |
||
108 | if (! $this->shouldCreateRevision()) { |
||
109 | return false; |
||
110 | } |
||
111 | |||
112 | if ($this->fireModelEvent('revisioning') === false) { |
||
0 ignored issues
–
show
It seems like
fireModelEvent() must be provided by classes using this trait. How about adding it as abstract method to this trait?
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 trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
113 | return false; |
||
114 | } |
||
115 | |||
116 | $revision = $this->saveAsRevision(); |
||
117 | |||
118 | $this->fireModelEvent('revisioned', false); |
||
0 ignored issues
–
show
It seems like
fireModelEvent() must be provided by classes using this trait. How about adding it as abstract method to this trait?
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 trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
119 | |||
120 | return $revision; |
||
121 | } catch (Exception $e) { |
||
122 | throw $e; |
||
123 | } |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Manually save a new revision for a model instance. |
||
128 | * This method should be called manually only where and if needed. |
||
129 | * |
||
130 | * @return RevisionModelContract |
||
131 | * @throws Exception |
||
132 | */ |
||
133 | public function saveAsRevision(): RevisionModelContract |
||
134 | { |
||
135 | $this->initRevisionOptions(); |
||
136 | |||
137 | try { |
||
138 | return DB::transaction(function () { |
||
139 | $revision = $this->revisions()->create([ |
||
140 | 'user_id' => auth()->id() ?: null, |
||
0 ignored issues
–
show
The method
id does only exist in Illuminate\Contracts\Auth\Guard , but not in Illuminate\Contracts\Auth\Factory .
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
![]() |
|||
141 | 'metadata' => $this->buildRevisionData(), |
||
142 | ]); |
||
143 | |||
144 | $this->clearOldRevisions(); |
||
145 | |||
146 | return $revision; |
||
147 | }); |
||
148 | } catch (Exception $e) { |
||
149 | throw $e; |
||
150 | } |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * Rollback the model instance to the given revision instance. |
||
155 | * |
||
156 | * @param RevisionModelContract $revision |
||
157 | * @return bool |
||
158 | * @throws Exception |
||
159 | */ |
||
160 | public function rollbackToRevision(RevisionModelContract $revision): bool |
||
161 | { |
||
162 | $this->initRevisionOptions(); |
||
163 | |||
164 | try { |
||
165 | static::revisioning(function () { |
||
166 | return false; |
||
167 | }); |
||
168 | |||
169 | DB::transaction(function () use ($revision) { |
||
170 | if ($this->revisionOptions->createRevisionWhenRollingBack === true) { |
||
171 | $this->saveAsRevision(); |
||
172 | } |
||
173 | |||
174 | $this->rollbackModelToRevision($revision); |
||
175 | |||
176 | if ($revision instanceof RevisionModelContract && isset($revision->metadata['relations'])) { |
||
0 ignored issues
–
show
Accessing
metadata on the interface Neurony\Revisions\Contracts\RevisionModelContract suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
177 | foreach ($revision->metadata['relations'] as $relation => $attributes) { |
||
0 ignored issues
–
show
Accessing
metadata on the interface Neurony\Revisions\Contracts\RevisionModelContract suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
178 | if (RelationHelper::isDirect($attributes['type'])) { |
||
179 | $this->rollbackDirectRelationToRevision($relation, $attributes); |
||
180 | } |
||
181 | |||
182 | if (RelationHelper::isPivoted($attributes['type'])) { |
||
183 | $this->rollbackPivotedRelationToRevision($relation, $attributes); |
||
184 | } |
||
185 | } |
||
186 | } |
||
187 | }); |
||
188 | |||
189 | return true; |
||
190 | } catch (Exception $e) { |
||
191 | throw $e; |
||
192 | } |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * Remove all existing revisions from the database, belonging to a model instance. |
||
197 | * |
||
198 | * @return void |
||
199 | * @throws Exception |
||
200 | */ |
||
201 | public function deleteAllRevisions(): void |
||
202 | { |
||
203 | try { |
||
204 | $this->revisions()->delete(); |
||
205 | } catch (Exception $e) { |
||
206 | throw $e; |
||
207 | } |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * If a revision record limit is set on the model and that limit is exceeded. |
||
212 | * Remove the oldest revisions until the limit is met. |
||
213 | * |
||
214 | * @return void |
||
215 | */ |
||
216 | public function clearOldRevisions(): void |
||
217 | { |
||
218 | $this->initRevisionOptions(); |
||
219 | |||
220 | $limit = $this->revisionOptions->revisionLimit; |
||
221 | $count = $this->revisions()->count(); |
||
222 | |||
223 | if (is_numeric($limit) && $count > $limit) { |
||
224 | $this->revisions()->oldest()->take($count - $limit)->delete(); |
||
225 | } |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * Determine if a revision should be stored for a given model instance. |
||
230 | * |
||
231 | * Check the revisionable fields set on the model. |
||
232 | * If any of those fields have changed, then a new revisions should be stored. |
||
233 | * If no fields are specifically set on the model, this will return true. |
||
234 | * |
||
235 | * @return bool |
||
236 | */ |
||
237 | protected function shouldCreateRevision(): bool |
||
238 | { |
||
239 | $this->initRevisionOptions(); |
||
240 | |||
241 | $fieldsToRevision = $this->revisionOptions->revisionFields; |
||
242 | $fieldsToNotRevision = $this->revisionOptions->revisionNotFields; |
||
243 | |||
244 | if ( |
||
245 | array_key_exists(SoftDeletes::class, class_uses($this)) && |
||
246 | array_key_exists($this->getDeletedAtColumn(), $this->getDirty()) |
||
0 ignored issues
–
show
It seems like
getDeletedAtColumn() must be provided by classes using this trait. How about adding it as abstract method to this trait?
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 trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() It seems like
getDirty() must be provided by classes using this trait. How about adding it as abstract method to this trait?
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 trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
247 | ) { |
||
248 | return false; |
||
249 | } |
||
250 | |||
251 | if ($fieldsToRevision && is_array($fieldsToRevision) && ! empty($fieldsToRevision)) { |
||
252 | return $this->isDirty($fieldsToRevision); |
||
0 ignored issues
–
show
It seems like
isDirty() must be provided by classes using this trait. How about adding it as abstract method to this trait?
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 trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
253 | } |
||
254 | |||
255 | if ($fieldsToNotRevision && is_array($fieldsToNotRevision) && ! empty($fieldsToNotRevision)) { |
||
256 | return ! empty(Arr::except($this->getDirty(), $fieldsToNotRevision)); |
||
0 ignored issues
–
show
It seems like
getDirty() must be provided by classes using this trait. How about adding it as abstract method to this trait?
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 trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
257 | } |
||
258 | |||
259 | return true; |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Both instantiate the revision options as well as validate their contents. |
||
264 | * |
||
265 | * @return void |
||
266 | */ |
||
267 | protected function initRevisionOptions(): void |
||
268 | { |
||
269 | if ($this->revisionOptions === null) { |
||
270 | $this->revisionOptions = $this->getRevisionOptions(); |
||
271 | } |
||
272 | } |
||
273 | } |
||
274 |
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.