daikon-cqrs /
interop
| 1 | <?php declare(strict_types=1); |
||
| 2 | /** |
||
| 3 | * This file is part of the daikon-cqrs/interop project. |
||
| 4 | * |
||
| 5 | * For the full copyright and license information, please view the LICENSE |
||
| 6 | * file that was distributed with this source code. |
||
| 7 | */ |
||
| 8 | |||
| 9 | namespace Daikon\Interop; |
||
| 10 | |||
| 11 | use ReflectionClass; |
||
| 12 | |||
| 13 | trait FromToNativeTrait |
||
| 14 | { |
||
| 15 | use SupportsAnnotations; |
||
| 16 | |||
| 17 | /** |
||
| 18 | * @psalm-suppress MissingParamType |
||
| 19 | * @return static |
||
| 20 | */ |
||
| 21 | 11 | public static function fromNative($state): self |
|
| 22 | { |
||
| 23 | 11 | Assertion::isArray($state, 'This trait only works with array state.'); |
|
| 24 | |||
| 25 | 7 | list($valueFactories, $product) = static::construct($state); |
|
| 26 | 6 | foreach ($valueFactories as $propertyName => $factory) { |
|
| 27 | 4 | if (array_key_exists($propertyName, $state)) { |
|
| 28 | 3 | $product->$propertyName = $factory($state[$propertyName]); |
|
| 29 | 2 | } elseif (is_a($factory[0], MakeEmptyInterface::class, true)) { |
|
| 30 | 2 | $product->$propertyName = ([$factory[0], 'makeEmpty'])(); |
|
| 31 | } |
||
| 32 | } |
||
| 33 | |||
| 34 | 4 | return $product; |
|
| 35 | } |
||
| 36 | |||
| 37 | public function toNative(): array |
||
| 38 | { |
||
| 39 | $state = []; |
||
| 40 | $reflectionClass = new ReflectionClass($this); |
||
| 41 | foreach (static::getInheritance() as $currentClass) { |
||
| 42 | foreach ($currentClass->getProperties() as $property) { |
||
| 43 | $propertyName = $property->getName(); |
||
| 44 | if ($currentClass->isTrait()) { |
||
| 45 | $property = $reflectionClass->getProperty($propertyName); |
||
| 46 | } |
||
| 47 | $property->setAccessible(true); |
||
| 48 | $value = $property->getValue($this); |
||
| 49 | if (is_a($value, ToNativeInterface::class)) { |
||
| 50 | $state[$propertyName] = $value->toNative(); |
||
| 51 | } else { |
||
| 52 | $state[$propertyName] = $value; |
||
| 53 | } |
||
| 54 | } |
||
| 55 | } |
||
| 56 | |||
| 57 | return $state; |
||
| 58 | } |
||
| 59 | |||
| 60 | 7 | private static function construct(array $payload): array |
|
| 61 | { |
||
| 62 | 7 | $valueFactories = static::inferValueFactories(); |
|
| 63 | 7 | $constructor = (new ReflectionClass(static::class))->getConstructor(); |
|
| 64 | 7 | if (is_null($constructor) || $constructor->getNumberOfParameters() === 0) { |
|
| 65 | /** @psalm-suppress UnsafeInstantiation */ |
||
| 66 | 4 | return [$valueFactories, new static]; |
|
| 67 | } |
||
| 68 | |||
| 69 | 4 | $constructorArgs = []; |
|
| 70 | 4 | foreach ($constructor->getParameters() as $constructorParam) { |
|
| 71 | 4 | $paramName = $constructorParam->getName(); |
|
| 72 | 4 | if (isset($payload[$paramName])) { |
|
| 73 | 3 | if (isset($valueFactories[$paramName])) { |
|
| 74 | $constructorArgs[] = $valueFactories[$paramName]($payload[$paramName]); |
||
| 75 | unset($valueFactories[$paramName]); |
||
| 76 | } else { |
||
| 77 | 3 | $constructorArgs[] = $payload[$paramName]; |
|
| 78 | } |
||
| 79 | 3 | } elseif ($constructorParam->allowsNull()) { |
|
| 80 | 3 | $constructorArgs[] = null; |
|
| 81 | } else { |
||
| 82 | throw new InvalidArgumentException( |
||
| 83 | "Missing required value for key '$paramName' while constructing from native state." |
||
| 84 | ); |
||
| 85 | } |
||
| 86 | } |
||
| 87 | |||
| 88 | /** |
||
| 89 | * @psalm-suppress UnsafeInstantiation |
||
| 90 | * @psalm-suppress TooManyArguments |
||
| 91 | */ |
||
| 92 | 4 | return [$valueFactories, new static(...$constructorArgs)]; |
|
|
0 ignored issues
–
show
|
|||
| 93 | } |
||
| 94 | } |
||
| 95 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.