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 Equip\Data\Traits; |
||
4 | |||
5 | use Equip\Data\ArraySerializableInterface; |
||
6 | use RuntimeException; |
||
7 | |||
8 | trait ImmutableValueObjectTrait |
||
9 | { |
||
10 | use ProtectedValueObjectTrait; |
||
11 | |||
12 | /** |
||
13 | * Hydrate the object with new values |
||
14 | * |
||
15 | * @param array $data |
||
16 | */ |
||
17 | 16 | public function __construct(array $data = []) |
|
18 | { |
||
19 | 16 | if ($data) { |
|
0 ignored issues
–
show
|
|||
20 | 14 | $this->apply($data); |
|
21 | 12 | } |
|
22 | 14 | } |
|
23 | |||
24 | /** |
||
25 | * Create a copy of the object with new values |
||
26 | * |
||
27 | * @param array $data |
||
28 | * |
||
29 | * @return static |
||
30 | */ |
||
31 | 2 | public function withData(array $data) |
|
32 | { |
||
33 | 2 | $copy = clone $this; |
|
34 | 2 | $copy->apply($data); |
|
35 | 2 | return $copy; |
|
36 | } |
||
37 | |||
38 | /** |
||
39 | * Type definitions for object properties |
||
40 | * |
||
41 | * return [ |
||
42 | * 'id' => 'int', |
||
43 | * 'email' => 'string', |
||
44 | * 'is_deleted' => 'bool', |
||
45 | * ]; |
||
46 | * |
||
47 | * Overload this method to enable type coercion! |
||
48 | * |
||
49 | * @return array |
||
50 | */ |
||
51 | 3 | private function types() |
|
52 | { |
||
53 | 3 | return []; |
|
54 | } |
||
55 | |||
56 | /** |
||
57 | * Expected types for object properties |
||
58 | * |
||
59 | * return [ |
||
60 | * 'user' => User::class, |
||
61 | * ]; |
||
62 | * |
||
63 | * @return array |
||
64 | */ |
||
65 | 7 | private function expects() |
|
66 | { |
||
67 | 7 | return []; |
|
68 | } |
||
69 | |||
70 | /** |
||
71 | * Validates the current object |
||
72 | * |
||
73 | * @return void |
||
74 | * |
||
75 | * @throws \DomainException If the object is not valid |
||
76 | */ |
||
77 | 8 | private function validate() |
|
78 | { |
||
79 | 8 | } |
|
80 | |||
81 | /** |
||
82 | * Update the current object with new values |
||
83 | * |
||
84 | * NOTE: Be careful not to violate immutability when using this method! |
||
85 | * |
||
86 | * @uses types() |
||
87 | * @uses expects() |
||
88 | * @uses validate() |
||
89 | * |
||
90 | * @param array $data |
||
91 | * |
||
92 | * @return void |
||
93 | * |
||
94 | * @throws \DomainException If a data value is not of the expected type |
||
95 | */ |
||
96 | 14 | private function apply(array $data) |
|
97 | { |
||
98 | // Discard any values that do not exist in this object |
||
99 | 14 | $data = array_intersect_key($data, get_object_vars($this)); |
|
100 | |||
101 | // Type coercion and class expectations are not run on null values |
||
102 | $values = array_filter($data, static function ($value) { |
||
103 | 14 | return null !== $value; |
|
104 | 14 | }); |
|
105 | |||
106 | 14 | $types = array_intersect_key($this->types(), $values); |
|
107 | 14 | $expects = array_intersect_key($this->expects(), $values); |
|
108 | |||
109 | 14 | foreach ($types as $key => $type) { |
|
110 | 12 | settype($data[$key], $type); |
|
111 | 14 | } |
|
112 | |||
113 | 14 | foreach ($expects as $key => $class) { |
|
114 | 3 | if (false === $data[$key] instanceof $class) { |
|
115 | 1 | throw new \DomainException(sprintf( |
|
116 | 1 | 'Expected value of `%s` to be an object of `%s` type', |
|
117 | 1 | $key, |
|
118 | $class |
||
119 | 1 | )); |
|
120 | } |
||
121 | 13 | } |
|
122 | |||
123 | 13 | foreach ($data as $key => $value) { |
|
124 | 13 | $this->$key = $value; |
|
125 | 13 | } |
|
126 | |||
127 | 13 | $this->validate(); |
|
0 ignored issues
–
show
The call to the method
Equip\Data\Traits\Immuta...ObjectTrait::validate() seems un-needed as the method has no side-effects.
PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left. Let’s take a look at an example: class User
{
private $email;
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
}
If we look at the $user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.
On the hand, if we look at the $user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
// instance variable).
Loading history...
|
|||
128 | 12 | } |
|
129 | |||
130 | /** |
||
131 | * Check if the current object has a property |
||
132 | * |
||
133 | * @param string $key |
||
134 | * |
||
135 | * @return boolean |
||
136 | */ |
||
137 | 2 | public function has($key) |
|
138 | { |
||
139 | 2 | return property_exists($this, $key); |
|
140 | } |
||
141 | |||
142 | /** |
||
143 | * Get the current object values as an array |
||
144 | * |
||
145 | * @return array |
||
146 | */ |
||
147 | public function toArray() |
||
148 | { |
||
149 | 8 | return array_map(static function ($value) { |
|
150 | 8 | if ($value instanceof ArraySerializableInterface) { |
|
151 | 1 | $value = $value->toArray(); |
|
152 | 1 | } |
|
153 | 8 | return $value; |
|
154 | 8 | }, get_object_vars($this)); |
|
155 | } |
||
156 | } |
||
157 |
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
empty(..)
or! empty(...)
instead.