1 | <?php |
||
2 | |||
3 | namespace Charcoal\Presenter; |
||
4 | |||
5 | use ArrayAccess; |
||
6 | use Charcoal\Model\CollectionInterface; |
||
0 ignored issues
–
show
|
|||
7 | use InvalidArgumentException; |
||
8 | use Traversable; |
||
9 | |||
10 | /** |
||
11 | * Presenter provides a presentation and transformation layer for a "model". |
||
12 | * |
||
13 | * It transforms (serializes) any data model (objects or array) into a presentation array, according to a **transformer**. |
||
14 | * |
||
15 | * A **transformer** defines the morph rules |
||
16 | * |
||
17 | * - A simple array or Traversable object, contain |
||
18 | */ |
||
19 | class Presenter |
||
20 | { |
||
21 | /** |
||
22 | * @var callable $transformer |
||
23 | */ |
||
24 | private $transformer; |
||
25 | |||
26 | /** |
||
27 | * @var string $getterPattern |
||
28 | */ |
||
29 | private $getterPattern; |
||
30 | |||
31 | /** |
||
32 | * @param array|Traversable|callable $transformer The data-view transformation array (or Traversable) object. |
||
33 | * @param string $getterPattern The string pattern to match string with. Must have a single catch-block. |
||
34 | */ |
||
35 | public function __construct($transformer, $getterPattern = '~{{(\w*?)}}~') |
||
36 | { |
||
37 | $this->setTransformer($transformer); |
||
38 | $this->getterPattern = $getterPattern; |
||
39 | } |
||
40 | |||
41 | /** |
||
42 | * @param array|Traversable|callable $transformer The data-view transformation array (or Traversable) object. |
||
43 | * @throws InvalidArgumentException If the provided transformer is not valid. |
||
44 | * @return void |
||
45 | */ |
||
46 | private function setTransformer($transformer) |
||
47 | { |
||
48 | if (is_callable($transformer)) { |
||
49 | $this->transformer = $transformer; |
||
0 ignored issues
–
show
It seems like
$transformer can also be of type Traversable . However, the property $transformer is declared as type callable . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
50 | } elseif (is_array($transformer) || $transformer instanceof Traversable) { |
||
51 | $this->transformer = function($model) use ($transformer) { |
||
52 | return $transformer; |
||
53 | }; |
||
54 | } else { |
||
55 | throw new InvalidArgumentException( |
||
56 | 'Transformer must be an array or a Traversable object' |
||
57 | ); |
||
58 | } |
||
59 | } |
||
60 | |||
61 | /** |
||
62 | * TheT Transformer class is callable. Its purpose is to transform a model (object) into view data. |
||
63 | * |
||
64 | * The transformer is set from the constructor. |
||
65 | * |
||
66 | * @param mixed $obj The original data (object / model) to transform into view-data. |
||
67 | * @return array Normalized data, suitable as presentation (view) layer |
||
68 | */ |
||
69 | public function transform($obj) |
||
70 | { |
||
71 | $transformer = $this->transformer; |
||
72 | return $this->transmogrify($obj, $transformer($obj)); |
||
0 ignored issues
–
show
|
|||
73 | } |
||
74 | |||
75 | /** |
||
76 | * @param mixed $collection A collection or array to transform. |
||
77 | * @return array |
||
78 | */ |
||
79 | public function transformCollection($collection) |
||
80 | { |
||
81 | $array = []; |
||
82 | |||
83 | if ($collection instanceof CollectionInterface) { |
||
84 | $array = $collection->values(); |
||
85 | } elseif (is_array($collection)) { |
||
86 | $array = $collection; |
||
87 | } |
||
88 | |||
89 | return array_map([$this, 'transform'], $array); |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * Transmogrify an object into an other structure. |
||
94 | * |
||
95 | * @param mixed $obj Source object. |
||
96 | * @param mixed $val Modifier. |
||
97 | * @throws InvalidArgumentException If the modifier is not callable, traversable (array) or string. |
||
98 | * @return mixed The transformed data (type depends on modifier). |
||
99 | */ |
||
100 | private function transmogrify($obj, $val) |
||
101 | { |
||
102 | // Callbacks (lambda or callable) are supported. They must accept the source object as argument. |
||
103 | if (!is_string($val) && is_callable($val)) { |
||
104 | return $val($obj); |
||
105 | } |
||
106 | |||
107 | // Arrays or traversables are handled recursively. |
||
108 | // This also converts / casts any Traversable into a simple array. |
||
109 | if (is_array($val) || $val instanceof Traversable) { |
||
110 | $data = []; |
||
111 | foreach ($val as $k => $v) { |
||
112 | if (!is_string($k)) { |
||
113 | if (is_string($v)) { |
||
114 | $data[$v] = $this->objectGet($obj, $v); |
||
115 | } else { |
||
116 | $data[] = $v; |
||
117 | } |
||
118 | } else { |
||
119 | $data[$k] = $this->transmogrify($obj, $v); |
||
120 | } |
||
121 | } |
||
122 | return $data; |
||
123 | } |
||
124 | |||
125 | // Strings are handled by rendering {{property}} with dynamic object getter pattern. |
||
126 | if (is_string($val)) { |
||
127 | return preg_replace_callback($this->getterPattern, function(array $matches) use ($obj) { |
||
128 | return $this->objectGet($obj, $matches[1]); |
||
129 | }, $val); |
||
130 | } |
||
131 | |||
132 | if (is_numeric($val)) { |
||
133 | return $val; |
||
134 | } |
||
135 | |||
136 | if (is_bool($val)) { |
||
137 | return !!$val; |
||
138 | } |
||
139 | |||
140 | if ($val === null) { |
||
141 | return null; |
||
142 | } |
||
143 | |||
144 | // Any other |
||
145 | throw new InvalidArgumentException( |
||
146 | sprintf( |
||
147 | 'Presenter\'s transmogrify val needs to be callable, traversable (array) or a string. "%s" given.', |
||
148 | gettype($val) |
||
149 | ) |
||
150 | ); |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * General-purpose dynamic object "getter". |
||
155 | * |
||
156 | * This method tries to fetch a "property" from any type of object (or array), |
||
157 | * trying to figure out the best possible way: |
||
158 | * |
||
159 | * - Method call (`$obj->property()`) |
||
160 | * - Public property get (`$obj->property`) |
||
161 | * - Array access, if available (`$obj[property]`) |
||
162 | * - Returns the property unchanged, otherwise |
||
163 | * |
||
164 | * @param mixed $obj The model (object or array) to retrieve the property's value from. |
||
165 | * @param string $propertyName The property name (key) to retrieve from model. |
||
166 | * @throws InvalidArgumentException If the property name is not a string. |
||
167 | * @return mixed The object property, if available. The property name, unchanged, if it's not available. |
||
168 | */ |
||
169 | private function objectGet($obj, $propertyName) |
||
170 | { |
||
171 | if (is_callable([$obj, $propertyName])) { |
||
172 | return $obj->{$propertyName}(); |
||
173 | } |
||
174 | |||
175 | if (isset($obj->{$propertyName})) { |
||
176 | return $obj->{$propertyName}; |
||
177 | } |
||
178 | |||
179 | if (is_string($propertyName) && (is_array($obj) || $obj instanceof ArrayAccess) && (isset($obj[$propertyName]))) { |
||
180 | return $obj[$propertyName]; |
||
181 | } |
||
182 | |||
183 | return null; |
||
184 | } |
||
185 | } |
||
186 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths