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 | /* |
||
4 | * This file is part of the samshal/scripd package. |
||
5 | * |
||
6 | * (c) Samuel Adeshina <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | namespace Samshal\Scripd; |
||
13 | |||
14 | /** |
||
15 | * A robust SQL Generator. Parses database structures defined in json based on the |
||
16 | * jsyn file format and generates corresponding sql queries. |
||
17 | * |
||
18 | * @since 1.0 |
||
19 | * |
||
20 | * @author Samuel Adeshina <[email protected]> |
||
21 | */ |
||
22 | final class JsonDbStructure |
||
23 | { |
||
24 | /** |
||
25 | * @var array |
||
26 | * |
||
27 | * Names of database objects that can be manipulated |
||
28 | * using major DDL keywords such as 'create', 'alter' |
||
29 | * and 'drop' |
||
30 | */ |
||
31 | private $topLevelObjects = [ |
||
32 | ':database', |
||
33 | ':table', |
||
34 | ':table-group', |
||
35 | ':view', |
||
36 | ':index', |
||
37 | ':trigger', |
||
38 | ':function', |
||
39 | ':stored-procedure', |
||
40 | ':storage', |
||
41 | ':security', |
||
42 | ]; |
||
43 | |||
44 | /** |
||
45 | * @var array |
||
46 | * An array of object definers. |
||
47 | * |
||
48 | * Object Definers are Special keywords that accepts array values |
||
49 | * in a json structure file definition |
||
50 | */ |
||
51 | private $objectDefiners = [ |
||
52 | 'columns', |
||
53 | 'add-column', |
||
54 | 'foreign-key', |
||
55 | ]; |
||
56 | |||
57 | /** |
||
58 | * @var array |
||
59 | * Special Characters used in jsyn files. |
||
60 | * |
||
61 | * Characters which have a special meaning such as braces and |
||
62 | * square brackets are listed in this array |
||
63 | */ |
||
64 | private $specialCharacters = [ |
||
65 | 'left-curly-brace' => '{', |
||
66 | 'right-curly-brace' => '}', |
||
67 | 'left-square-bracket' => '[', |
||
68 | 'right-square-bracket' => ']', |
||
69 | 'left-bracket' => '(', |
||
70 | 'right-bracket' => ')', |
||
71 | ]; |
||
72 | |||
73 | /** |
||
74 | * @var string |
||
75 | */ |
||
76 | private $crudActionKeyword = ':crud-action'; |
||
77 | |||
78 | /** |
||
79 | * @var string |
||
80 | */ |
||
81 | private $objectGroupKeyword = '-group'; |
||
82 | |||
83 | /** |
||
84 | * @var string |
||
85 | */ |
||
86 | private $jsynExtension = '.jsyn'; |
||
87 | |||
88 | /** |
||
89 | * @var string |
||
90 | */ |
||
91 | private $jsynDirectory = __DIR__.'/bin/'; |
||
92 | |||
93 | /** |
||
94 | * @var null | array |
||
95 | */ |
||
96 | private $jsonStructure; |
||
97 | |||
98 | /** |
||
99 | * @var null | string |
||
100 | */ |
||
101 | private $sqlVendor; |
||
102 | |||
103 | /** |
||
104 | * @var array |
||
105 | */ |
||
106 | private $generatedSql = []; |
||
107 | |||
108 | /** |
||
109 | * @param $jsonStructureFile PathUtil | string | Array |
||
110 | * @param $sqlVendor string |
||
111 | */ |
||
112 | public function __construct($jsonStructureFile, $sqlVendor = 'default') |
||
113 | { |
||
114 | if (is_array($jsonStructureFile)) { |
||
115 | $this->jsonStructure = $jsonStructureFile; |
||
0 ignored issues
–
show
|
|||
116 | } else { |
||
117 | $this->jsonStructure = self::getObjectFromJsonFile($jsonStructureFile); |
||
0 ignored issues
–
show
It seems like
self::getObjectFromJsonFile($jsonStructureFile) of type array is incompatible with the declared type null of property $jsonStructure .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
118 | } |
||
119 | $this->sqlVendor = $sqlVendor; |
||
0 ignored issues
–
show
It seems like
$sqlVendor of type string is incompatible with the declared type null of property $sqlVendor .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
120 | } |
||
121 | |||
122 | /** |
||
123 | * @param $jsynDirectory string |
||
124 | * |
||
125 | * @return void |
||
126 | */ |
||
127 | public function setJsynDirectory($jsynDirectory) |
||
128 | { |
||
129 | $this->jsynDirectory = $jsynDirectory; |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * @param $sqlVendor string |
||
134 | * |
||
135 | * @return void |
||
136 | */ |
||
137 | public function setSqlVendor($sqlVendor) |
||
138 | { |
||
139 | $this->sqlVendor = $sqlVendor; |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * @param $topLevelObject string |
||
144 | * @param $crudAction string |
||
145 | * |
||
146 | * Based on the values provided in the $topLevelObject and $crudAction |
||
147 | * variables, this method tries to derive the name of the jsyn file to use |
||
148 | * for parsing. |
||
149 | * |
||
150 | * @return string | bool |
||
151 | */ |
||
152 | private function guessJsynFileName($topLevelObject, $crudAction) |
||
153 | { |
||
154 | if (in_array($topLevelObject, $this->topLevelObjects)) { |
||
155 | $this->crudAction = strtolower($crudAction); |
||
0 ignored issues
–
show
The property
crudAction does not seem to exist. Did you mean crudActionKeyword ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
156 | |||
157 | return $this->crudAction.'-'.self::objectIdentifierToString($topLevelObject).$this->jsynExtension; |
||
0 ignored issues
–
show
The property
crudAction does not seem to exist. Did you mean crudActionKeyword ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
158 | } |
||
159 | |||
160 | return false; |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * @param $jsonFile PathUtil | string |
||
165 | * |
||
166 | * Gets the content of a json file, decodes it and |
||
167 | * returns an array of the decoded json. |
||
168 | * |
||
169 | * @return array |
||
170 | */ |
||
171 | private function getObjectFromJsonFile($jsonFile) |
||
172 | { |
||
173 | $jsonStructure = file_get_contents($jsonFile); |
||
174 | |||
175 | return json_decode($jsonStructure, JSON_FORCE_OBJECT); |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * @param $jsonStructure array |
||
180 | * |
||
181 | * Tries to get the top level object from an array of |
||
182 | * a json structure, returns false if no top level object |
||
183 | * is found. |
||
184 | * |
||
185 | * @return string | bool |
||
186 | */ |
||
187 | private function getProvidedTopLevelObject($jsonStructure) |
||
188 | { |
||
189 | foreach ($this->topLevelObjects as $topLevelObject) { |
||
190 | if (isset($jsonStructure[$topLevelObject])) { |
||
191 | return $topLevelObject; |
||
192 | } |
||
193 | } |
||
194 | |||
195 | return false; |
||
0 ignored issues
–
show
The return type of
return false; (false ) is incompatible with the return type documented by Samshal\Scripd\JsonDbStr...tProvidedTopLevelObject of type string .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
196 | } |
||
197 | |||
198 | /** |
||
199 | * @param $jsonStructure array |
||
200 | * |
||
201 | * Determines if a top level object is a valid one by checking |
||
202 | * the $topLevelObjects array to see if its present. |
||
203 | * |
||
204 | * @return bool |
||
205 | */ |
||
206 | private function isValidTopLevelObject($jsonStructure) |
||
207 | { |
||
208 | foreach ($this->topLevelObjects as $topLevelObject) { |
||
209 | if (isset($jsonStructure[$topLevelObject])) { |
||
210 | return true; |
||
211 | } |
||
212 | } |
||
213 | |||
214 | return false; |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * @param $objectIdentifier string |
||
219 | * |
||
220 | * Strips a supplied $objectIdentifier string variable of |
||
221 | * special characters and returns a new string with only alphanumeric |
||
222 | * characters. |
||
223 | * |
||
224 | * @return string |
||
225 | */ |
||
226 | private function objectIdentifierToString($objectIdentifier) |
||
227 | { |
||
228 | return substr($objectIdentifier, 1, strlen($objectIdentifier) - 1); |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * @param $jsonStructure array |
||
233 | * |
||
234 | * Converts a $jsonStructure array into a string containing valid |
||
235 | * sql statements. |
||
236 | * |
||
237 | * @return string |
||
238 | */ |
||
239 | public function generateSqlFromStructure($jsonStructure) |
||
240 | { |
||
241 | $topLevelObject = self::getProvidedTopLevelObject($jsonStructure); |
||
242 | $crudAction = $jsonStructure[$topLevelObject][$this->crudActionKeyword]; |
||
243 | |||
244 | $jsynFileName = self::guessJsynFileName($topLevelObject, $crudAction); |
||
245 | |||
246 | $jsynExtractor = new JsynExtractor($this->jsynDirectory.$jsynFileName, $this->sqlVendor); |
||
247 | $jsynExtractor->formatJsyn(); |
||
248 | $jsyn = $jsynExtractor->getJsyn(); |
||
249 | |||
250 | $count = count($jsyn); |
||
251 | for ($i = 0; $i < $count; ++$i) { |
||
252 | $string = $jsyn[$i]; |
||
253 | $toSetValue = false; |
||
254 | $isConstant = false; |
||
255 | |||
256 | if (self::enclosed($this->specialCharacters['left-square-bracket'], $this->specialCharacters['right-square-bracket'], $string)) { |
||
257 | $string = str_replace($this->specialCharacters['left-square-bracket'], null, str_replace($this->specialCharacters['right-square-bracket'], null, $string)); |
||
258 | View Code Duplication | if (self::enclosed($this->specialCharacters['left-curly-brace'], $this->specialCharacters['right-curly-brace'], $string)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
259 | $string = str_replace($this->specialCharacters['left-curly-brace'], null, str_replace($this->specialCharacters['right-curly-brace'], null, $string)); |
||
260 | $toSetValue = true; |
||
261 | } |
||
262 | View Code Duplication | } elseif (self::enclosed($this->specialCharacters['left-curly-brace'], $this->specialCharacters['right-curly-brace'], $string)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
263 | $string = str_replace($this->specialCharacters['left-curly-brace'], null, str_replace($this->specialCharacters['right-curly-brace'], null, $string)); |
||
264 | $toSetValue = true; |
||
265 | } else { |
||
266 | $isConstant = true; |
||
267 | } |
||
268 | |||
269 | $_string = str_replace(' ', '-', $string); |
||
270 | if (isset($jsonStructure[$topLevelObject][$_string])) { |
||
271 | if ($toSetValue && !is_bool($jsonStructure[$topLevelObject][$_string])) { |
||
272 | if (in_array($_string, $this->objectDefiners)) { |
||
273 | $_str = []; |
||
274 | foreach ($jsonStructure[$topLevelObject][$_string] as $jsonStructures) { |
||
275 | $_str[] = self::generateSqlFromObjectDefiner([$_string => $jsonStructures], $_string); |
||
276 | } |
||
277 | $jsonStructure[$topLevelObject][$_string] = '('.implode(', ', $_str).')'; |
||
278 | } |
||
279 | $jsyn[$i] = $jsonStructure[$topLevelObject][$_string]; |
||
280 | } else { |
||
281 | $jsyn[$i] = (isset($jsonStructure[$topLevelObject][$_string]) && $jsonStructure[$topLevelObject][$_string] == true) ? strtoupper($string) : null; |
||
282 | } |
||
283 | View Code Duplication | } else { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
284 | if (!$isConstant) { |
||
285 | if (isset($jsyn[$i - 1]) && $jsyn[$i - 1] == '=') { |
||
286 | unset($jsyn[$i - 1]); |
||
287 | } |
||
288 | unset($jsyn[$i]); |
||
289 | } |
||
290 | } |
||
291 | } |
||
292 | |||
293 | return implode(' ', $jsyn); |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * @param $jsonStructures array |
||
298 | * @param $objectDefiner string |
||
299 | * |
||
300 | * While the {@link generateSqlFromStructure()} method above generates sql string |
||
301 | * from only valid top level objects, this method generates sql statements from valid |
||
302 | * object definers. Accepts an $objectDefiner and a $jsonStructure array as parameters. |
||
303 | * |
||
304 | * @return string |
||
305 | */ |
||
306 | public function generateSqlFromObjectDefiner($jsonStructures, $objectDefiner) |
||
307 | { |
||
308 | $topLevelObject = $objectDefiner; |
||
309 | $jsynFileName = $objectDefiner.'.jsyn'; |
||
310 | |||
311 | $jsynExtractor = new JsynExtractor($this->jsynDirectory.$jsynFileName, $this->sqlVendor); |
||
312 | $jsynExtractor->formatJsyn(); |
||
313 | $jsyn = $jsynExtractor->getJsyn(); |
||
314 | |||
315 | $count = count($jsyn); |
||
316 | foreach ($jsonStructures as $jsonStructure) { |
||
317 | $jsonStructure = [$topLevelObject => $jsonStructure]; |
||
318 | for ($i = 0; $i < $count; ++$i) { |
||
319 | $string = $jsyn[$i]; |
||
320 | $toSetValue = false; |
||
321 | $isConstant = false; |
||
322 | $replaceWithComma = false; |
||
323 | |||
324 | if (self::enclosed($this->specialCharacters['left-square-bracket'], $this->specialCharacters['right-square-bracket'], $string)) { |
||
325 | $string = str_replace($this->specialCharacters['left-square-bracket'], null, str_replace($this->specialCharacters['right-square-bracket'], null, $string)); |
||
326 | if (self::enclosed($this->specialCharacters['left-curly-brace'], $this->specialCharacters['right-curly-brace'], $string)) { |
||
327 | $string = str_replace($this->specialCharacters['left-curly-brace'], null, str_replace($this->specialCharacters['right-curly-brace'], null, $string)); |
||
328 | $toSetValue = true; |
||
329 | View Code Duplication | } elseif (self::enclosed($this->specialCharacters['left-bracket'], $this->specialCharacters['right-bracket'], $string)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
330 | $string = str_replace($this->specialCharacters['left-bracket'], null, str_replace($this->specialCharacters['right-bracket'], null, $string)); |
||
331 | $toSetValue = false; |
||
332 | $replaceWithComma = true; |
||
333 | } |
||
334 | } elseif (self::enclosed($this->specialCharacters['left-curly-brace'], $this->specialCharacters['right-curly-brace'], $string)) { |
||
335 | $string = str_replace($this->specialCharacters['left-curly-brace'], null, str_replace($this->specialCharacters['right-curly-brace'], null, $string)); |
||
336 | $toSetValue = true; |
||
337 | } else { |
||
338 | $isConstant = true; |
||
339 | } |
||
340 | |||
341 | $_string = str_replace(' ', '-', $string); |
||
342 | if (isset($jsonStructure[$topLevelObject][$_string])) { |
||
343 | if ($toSetValue && !is_bool($jsonStructure[$topLevelObject][$_string])) { |
||
344 | $jsyn[$i] = $jsonStructure[$topLevelObject][$_string]; |
||
345 | } else { |
||
346 | if ($replaceWithComma) { |
||
347 | $string = ", $string"; |
||
348 | } |
||
349 | $jsyn[$i] = (isset($jsonStructure[$topLevelObject][$_string]) && $jsonStructure[$topLevelObject][$_string] == true) ? strtoupper($string) : null; |
||
350 | } |
||
351 | View Code Duplication | } else { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
352 | if (!$isConstant) { |
||
353 | if (isset($jsyn[$i - 1]) && $jsyn[$i - 1] == '=') { |
||
354 | unset($jsyn[$i - 1]); |
||
355 | } |
||
356 | unset($jsyn[$i]); |
||
357 | } |
||
358 | } |
||
359 | } |
||
360 | } |
||
361 | |||
362 | return implode(' ', $jsyn); |
||
363 | } |
||
364 | |||
365 | /** |
||
366 | * @param $encloserPre string |
||
367 | * @param $encloserPost string |
||
368 | * @param $enclosee string |
||
369 | * |
||
370 | * Checks to see if a string ($enclosee) is enclosed by special characters |
||
371 | * such as '{' and '}' and '[' and ']'. |
||
372 | * |
||
373 | * @return bool |
||
374 | */ |
||
375 | private function enclosed($encloserPre, $encloserPost, $enclosee) |
||
376 | { |
||
377 | if (substr($enclosee, 0, 1) == $encloserPre && substr($enclosee, strlen($enclosee) - 1) == $encloserPost) { |
||
378 | return true; |
||
379 | } else { |
||
380 | return false; |
||
381 | } |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * Parses a jsonStructure in global scope and assigns |
||
386 | * a generated array to either of the sql string generator methods |
||
387 | * depending on the top level objects or object definers. |
||
388 | * |
||
389 | * @return bool |
||
390 | */ |
||
391 | public function parseStructure() |
||
392 | { |
||
393 | foreach ($this->jsonStructure as $object => $jsonStructure) { |
||
0 ignored issues
–
show
The expression
$this->jsonStructure of type array<string,?>|null is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
394 | if (!strpos($object, $this->objectGroupKeyword)) { |
||
395 | $jsonStructure = [$object => $jsonStructure]; |
||
396 | if (self::isValidTopLevelObject($jsonStructure)) { |
||
397 | $this->generatedSql[] = self::generateSqlFromStructure($jsonStructure); |
||
398 | } |
||
399 | |||
400 | $topLevelObject = self::isAnotherObjectPresent($jsonStructure[$object]); |
||
401 | while ($topLevelObject) { |
||
402 | if (strtolower($object) == ':database') { |
||
403 | $dbname = ($jsonStructure[$object]['name']); |
||
404 | $this->generatedSql[] = "USE $dbname"; |
||
405 | } |
||
406 | $this->jsonStructure = [$topLevelObject => $jsonStructure[$object][$topLevelObject]]; |
||
0 ignored issues
–
show
It seems like
array($topLevelObject =>...ject][$topLevelObject]) of type array<string,?> is incompatible with the declared type null of property $jsonStructure .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
407 | $topLevelObject = self::isAnotherObjectPresent($jsonStructure[$object][$topLevelObject]); |
||
408 | self::parseStructure(); |
||
409 | } |
||
410 | } else { |
||
411 | foreach ($jsonStructure as $_jsonStructure) { |
||
412 | $object = substr($object, 0, strlen($object) - strpos($object, $this->objectGroupKeyword)); |
||
413 | $_jsonStructure = [$object => $_jsonStructure]; |
||
414 | if (self::isValidTopLevelObject($_jsonStructure)) { |
||
415 | $this->generatedSql[] = self::generateSqlFromStructure($_jsonStructure); |
||
416 | } |
||
417 | |||
418 | $topLevelObject = self::isAnotherObjectPresent($_jsonStructure[$object]); |
||
419 | while ($topLevelObject) { |
||
420 | $this->jsonStructure = [$topLevelObject => $_jsonStructure[$object][$topLevelObject]]; |
||
0 ignored issues
–
show
It seems like
array($topLevelObject =>...ject][$topLevelObject]) of type array<string,?> is incompatible with the declared type null of property $jsonStructure .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
421 | $topLevelObject = self::isAnotherObjectPresent($_jsonStructure[$object][$topLevelObject]); |
||
422 | self::parseStructure(); |
||
423 | } |
||
424 | } |
||
425 | } |
||
426 | } |
||
427 | |||
428 | return true; |
||
429 | } |
||
430 | |||
431 | /** |
||
432 | * @param $jsonStructure array |
||
433 | * |
||
434 | * Determines if another top level object or object definer is |
||
435 | * present within the supplied json structure. |
||
436 | * Returns the name of the object if found and false if not found. |
||
437 | * |
||
438 | * @return string |
||
439 | */ |
||
440 | public function isAnotherObjectPresent($jsonStructure) |
||
441 | { |
||
442 | foreach ($this->topLevelObjects as $topLevelObject) { |
||
443 | if (isset($jsonStructure[$topLevelObject])) { |
||
444 | return $topLevelObject; |
||
445 | } |
||
446 | } |
||
447 | } |
||
448 | |||
449 | /** |
||
450 | * @param $delimiter string |
||
451 | * |
||
452 | * Returns the parsed and generated string containing the sql |
||
453 | * statement delimited by a value supplied in the $delimiter |
||
454 | * parameter. |
||
455 | * |
||
456 | * @return string |
||
457 | */ |
||
458 | public function getGeneratedSql($delimiter = ";\n") |
||
459 | { |
||
460 | return implode($delimiter, $this->generatedSql); |
||
461 | } |
||
462 | } |
||
463 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..