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 Victorlap\Approvable; |
||
4 | |||
5 | use Illuminate\Database\Eloquent\Relations\MorphMany; |
||
6 | use Illuminate\Support\Collection; |
||
7 | use Illuminate\Support\Facades\Auth; |
||
8 | use Illuminate\Support\Facades\DB; |
||
9 | |||
10 | trait Approvable |
||
11 | { |
||
12 | |||
13 | /** @var array */ |
||
14 | public $approveOf = array(); |
||
15 | |||
16 | /** @var array */ |
||
17 | public $dontApproveOf = array(); |
||
18 | |||
19 | /** @var bool */ |
||
20 | protected $withoutApproval = false; |
||
21 | |||
22 | /** |
||
23 | * Create the event listeners for the saving event |
||
24 | * This lets us save approvals whenever a save is made, no matter the |
||
25 | * http method |
||
26 | */ |
||
27 | 15 | public static function bootApprovable(): void |
|
28 | { |
||
29 | static::saving(function ($model) { |
||
30 | 15 | return $model->preSave(); |
|
31 | 15 | }); |
|
32 | 15 | } |
|
33 | |||
34 | 5 | public function approvals(): MorphMany |
|
35 | { |
||
36 | 5 | return $this->morphMany(Approval::class, 'approvable'); |
|
0 ignored issues
–
show
|
|||
37 | } |
||
38 | |||
39 | /** |
||
40 | * Check if this model has pending changes, |
||
41 | * If an attribute is provided, check if the attribute has pending changes. |
||
42 | * |
||
43 | * @param null $attribute |
||
44 | * @return bool |
||
45 | */ |
||
46 | 2 | public function isPendingApproval($attribute = null): bool |
|
47 | { |
||
48 | 2 | return $this->approvals() |
|
49 | ->when($attribute !== null, function ($query) use ($attribute) { |
||
50 | 1 | $query->where('key', $attribute); |
|
51 | 2 | }) |
|
52 | 2 | ->where('approved', null) |
|
53 | 2 | ->exists(); |
|
54 | } |
||
55 | |||
56 | /** |
||
57 | * List all the attributes, that currently have pending changes. |
||
58 | * |
||
59 | * @return \Illuminate\Support\Collection |
||
60 | */ |
||
61 | 1 | public function getPendingApprovalAttributes(): Collection |
|
62 | { |
||
63 | 1 | return $this->approvals() |
|
64 | 1 | ->where('approved', null) |
|
65 | 1 | ->groupBy('key') |
|
66 | 1 | ->pluck('key'); |
|
67 | } |
||
68 | |||
69 | /** |
||
70 | * Disable the approval process for this model instance. |
||
71 | * |
||
72 | * @param bool $withoutApproval Deprecated, see withoApproval() |
||
73 | * Will be removed in 2.0.0 |
||
74 | * |
||
75 | * @return self |
||
76 | */ |
||
77 | 1 | public function withoutApproval(bool $withoutApproval = true): self |
|
78 | { |
||
79 | 1 | $this->withoutApproval = $withoutApproval; |
|
80 | |||
81 | 1 | return $this; |
|
82 | } |
||
83 | |||
84 | /** |
||
85 | * Enable the approval process for this model instance |
||
86 | * |
||
87 | * @return self |
||
88 | */ |
||
89 | 1 | public function withApproval(): self |
|
90 | { |
||
91 | 1 | $this->withoutApproval = false; |
|
92 | |||
93 | 1 | return $this; |
|
94 | } |
||
95 | |||
96 | /** |
||
97 | * Invoked before a model is saved. Return false to abort the operation. |
||
98 | * |
||
99 | * @return bool |
||
100 | */ |
||
101 | 15 | protected function preSave(): bool |
|
102 | { |
||
103 | 15 | if ($this->withoutApproval) { |
|
104 | 1 | return true; |
|
105 | } |
||
106 | |||
107 | 15 | if ($this->currentUserCanApprove()) { |
|
108 | // If the user is able to approve edits, do nothing. |
||
109 | 3 | return true; |
|
110 | } |
||
111 | |||
112 | 13 | if (!$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;
![]() |
|||
113 | // There is currently no way (implemented) to enable this for new models. |
||
114 | 13 | return true; |
|
115 | } |
||
116 | |||
117 | 11 | $changes_to_record = $this->changedApprovableFields(); |
|
118 | |||
119 | 11 | $approvals = array(); |
|
120 | 11 | foreach ($changes_to_record as $key => $change) { |
|
121 | 11 | $approvals[] = array( |
|
122 | 11 | 'approvable_type' => $this->getMorphClass(), |
|
0 ignored issues
–
show
It seems like
getMorphClass() 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 ![]() |
|||
123 | 11 | 'approvable_id' => $this->getKey(), |
|
0 ignored issues
–
show
It seems like
getKey() 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 ![]() |
|||
124 | 11 | 'key' => $key, |
|
125 | 11 | 'value' => $change, |
|
126 | 11 | 'user_id' => $this->getSystemUserId(), |
|
127 | 11 | 'created_at' => new \DateTime(), |
|
128 | 11 | 'updated_at' => new \DateTime(), |
|
129 | ); |
||
130 | } |
||
131 | |||
132 | 11 | if (count($approvals) > 0) { |
|
133 | 11 | $approval = new Approval(); |
|
134 | 11 | DB::table($approval->getTable())->insert($approvals); |
|
135 | } |
||
136 | |||
137 | 11 | return true; |
|
138 | } |
||
139 | |||
140 | /** |
||
141 | * Get all of the changes that have been made, that are also supposed |
||
142 | * to be approved. |
||
143 | * |
||
144 | * @return array fields with new data, that should be recorded |
||
145 | */ |
||
146 | 11 | private function changedApprovableFields(): array |
|
147 | { |
||
148 | 11 | $dirty = $this->getDirty(); |
|
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 ![]() |
|||
149 | 11 | $changes_to_record = array(); |
|
150 | |||
151 | 11 | foreach ($dirty as $key => $value) { |
|
152 | 11 | if ($this->isApprovable($key)) { |
|
153 | 11 | if (!isset($this->original[$key]) || $this->original[$key] != $this->attributes[$key]) { |
|
154 | 11 | $changes_to_record[$key] = $value; |
|
155 | |||
156 | // Reset changes that we want to approve |
||
157 | 11 | if (!isset($this->original[$key])) { |
|
158 | 2 | unset($this->attributes[$key]); |
|
159 | } else { |
||
160 | 9 | $this->attributes[$key] = $this->original[$key]; |
|
0 ignored issues
–
show
The property
attributes 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;
![]() The property
original 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;
![]() |
|||
161 | } |
||
162 | } |
||
163 | } |
||
164 | } |
||
165 | |||
166 | 11 | return $changes_to_record; |
|
167 | } |
||
168 | |||
169 | /** |
||
170 | * Return whether an attribute of this model should be approvable. |
||
171 | * |
||
172 | * @param string $key |
||
173 | * @return bool |
||
174 | */ |
||
175 | 11 | private function isApprovable(string $key): bool |
|
176 | { |
||
177 | 11 | if (isset($this->approveOf) && in_array($key, $this->approveOf)) { |
|
178 | 1 | return true; |
|
179 | } |
||
180 | 11 | if (isset($this->dontApproveOf) && in_array($key, $this->dontApproveOf)) { |
|
181 | 1 | return false; |
|
182 | } |
||
183 | |||
184 | 11 | return empty($this->approveOf); |
|
185 | } |
||
186 | |||
187 | /** |
||
188 | * Get the user id that should be stored as the requester for the approval. |
||
189 | * |
||
190 | * @return int|null |
||
191 | */ |
||
192 | 11 | protected function getSystemUserId(): ?int |
|
193 | { |
||
194 | 11 | return Auth::id() ?? null; |
|
195 | } |
||
196 | |||
197 | /** |
||
198 | * Check if the approval process needs to happen for the currently logged in user. |
||
199 | * |
||
200 | * @return bool |
||
201 | */ |
||
202 | 2 | protected function currentUserCanApprove(): bool |
|
203 | { |
||
204 | 2 | return Auth::check() && Auth::user()->can('approve', $this) ?? false; |
|
0 ignored issues
–
show
It seems like you code against a concrete implementation and not the interface
Illuminate\Contracts\Auth\Authenticatable as the method can() does only exist in the following implementations of said interface: Illuminate\Foundation\Auth\User .
Let’s take a look at an example: interface User
{
/** @return string */
public function getPassword();
}
class MyUser implements User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
![]() |
|||
205 | } |
||
206 | } |
||
207 |
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.