Passed
Push — master ( ab5b91...849dc0 )
by Fran
03:17
created

Dto::fromArray()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 7
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 9
ccs 8
cts 8
cp 1
crap 5
rs 9.6111
1
<?php
2
3
namespace PSFS\base\dto;
4
5
use PSFS\base\Logger;
6
use PSFS\base\Request;
7
use PSFS\base\Singleton;
8
use PSFS\base\types\helpers\InjectorHelper;
9
10
/**
11
 * Class Dto
12
 * @package PSFS\base\dto
13
 */
14
class Dto extends Singleton implements \JsonSerializable
15
{
16
    /**
17
     * @var array
18
     */
19
    protected array $__cache = [];
20
21 4
    public function __construct($hydrate = true)
22
    {
23 4
        parent::__construct();
24 4
        if ($hydrate) {
25 2
            $this->fromArray(Request::getInstance()->getData());
26
        }
27
    }
28
29
    /**
30
     * ToArray wrapper
31
     * @return array
32
     */
33 4
    public function toArray()
34
    {
35 4
        return $this->__toArray();
36
    }
37
38
    /**
39
     * Convert dto to array representation
40
     * @return array
41
     */
42 4
    public function __toArray()
43
    {
44 4
        $dto = [];
45
        try {
46 4
            $reflectionClass = new \ReflectionClass($this);
47 4
            $properties = $reflectionClass->getProperties(\ReflectionProperty::IS_PUBLIC);
48 4
            if (count($properties) > 0) {
49 4
                foreach ($properties as $property) {
50 4
                    $value = $property->getValue($this);
51 4
                    if (is_object($value) && method_exists($value, 'toArray')) {
52 1
                        $dto[$property->getName()] = $value->toArray();
53 4
                    } elseif (is_array($value)) {
54 4
                        foreach ($value as &$arrValue) {
55 2
                            if ($arrValue instanceof Dto) {
56 1
                                $arrValue = $arrValue->toArray();
57
                            }
58
                        }
59 4
                        $dto[$property->getName()] = $value;
60
                    } else {
61 4
                        $type = InjectorHelper::extractVarType($property->getDocComment());
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $type is correct as PSFS\base\types\helpers\...perty->getDocComment()) targeting PSFS\base\types\helpers\...elper::extractVarType() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
62 4
                        $dto[$property->getName()] = $this->checkCastedValue($property->getValue($this), $type);
0 ignored issues
show
Bug introduced by
$type of type null is incompatible with the type string expected by parameter $type of PSFS\base\dto\Dto::checkCastedValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
                        $dto[$property->getName()] = $this->checkCastedValue($property->getValue($this), /** @scrutinizer ignore-type */ $type);
Loading history...
63
                    }
64
                }
65
            }
66
        } catch (\Exception $e) {
67
            Logger::log(get_class($this) . ': ' . $e->getMessage(), LOG_ERR);
68
        }
69 4
        return $dto;
70
    }
71
72
    /**
73
     * Convert to string representation
74
     * @return string
75
     */
76 1
    public function __toString()
77
    {
78 1
        return get_class($this);
79
    }
80
81
    /**
82
     * @param array $properties
83
     * @param string $key
84
     * @param mixed|null $value
85
     */
86 2
    protected function parseDtoField(array $properties, string $key, mixed $value = null)
87
    {
88 2
        list($type, $isArray) = $this->extractTypes($properties, $key);
89 2
        $reflector = (class_exists($type)) ? new \ReflectionClass($type) : null;
90 2
        if (null !== $reflector && $reflector->isSubclassOf(Dto::class)) {
91 1
            if (is_array($value)) {
92 1
                if (!array_key_exists($type, $this->__cache)) {
93 1
                    $this->__cache[$type] = new $type(false);
94
                }
95 1
                if ($isArray) {
96 1
                    $this->$key = [];
97 1
                    foreach ($value as $data) {
98 1
                        if (is_array($data)) {
99 1
                            $dto = clone $this->__cache[$type];
100 1
                            $dto->fromArray($data);
101 1
                            $this->$key[] = $dto;
102
                        }
103
                    }
104
                } else {
105 1
                    $this->$key = clone $this->__cache[$type];
106 1
                    $this->$key->fromArray($value);
107
                }
108
            }
109
        } else {
110 2
            $this->castValue($key, $value, $type);
111
        }
112
    }
113
114
    /**
115
     * Hydrate object from array
116
     * @param array $object
117
     * @throws \ReflectionException
118
     */
119 3
    public function fromArray(array $object = [])
120
    {
121 3
        if (count($object) > 0) {
122 2
            $reflector = new \ReflectionClass($this);
123 2
            $properties = InjectorHelper::extractProperties($reflector, \ReflectionProperty::IS_PUBLIC, InjectorHelper::VAR_PATTERN);
124 2
            unset($reflector);
125 2
            foreach ($object as $key => $value) {
126 2
                if (property_exists($this, $key) && null !== $value) {
127 2
                    $this->parseDtoField($properties, $key, $value);
128
                }
129
            }
130
        }
131
    }
132
133
    /**
134
     * @return mixed
135
     */
136 1
    public function jsonSerialize(): array|string|null
137
    {
138 1
        return $this->toArray();
139
    }
140
141
    /**
142
     * @param array $properties
143
     * @param string $key
144
     * @return array
145
     */
146 2
    protected function extractTypes(array $properties, string $key): array
147
    {
148 2
        $type = 'string';
149 2
        $isArray = false;
150 2
        if (array_key_exists($key, $properties)) {
151 2
            $type = $properties[$key];
152 2
            if (preg_match('/(\[\]|Array)/i', $type)) {
153 1
                $type = preg_replace('/(\[\]|Array)/i', '', $type);
154 1
                $isArray = true;
155
            }
156
        }
157 2
        return array($type, $isArray);
158
    }
159
160
    /**
161
     * @param string $key
162
     * @param mixed $value
163
     * @param string $type
164
     */
165 2
    protected function castValue(string $key, mixed $value, string $type): void
166
    {
167 2
        $this->$key = $this->checkCastedValue($value, $type);
168
    }
169
170
    /**
171
     * @param mixed $rawValue
172
     * @param string $type
173
     */
174 4
    protected function checkCastedValue(mixed $rawValue, string $type)
175
    {
176 4
        if (null !== $rawValue) {
177
            switch ($type) {
178
                default:
179 4
                case 'string':
180 4
                    $value = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
181 4
                    break;
182 4
                case 'integer':
183 4
                case 'int':
184 2
                    $value = (integer)$rawValue;
185 2
                    break;
186 4
                case 'float':
187 4
                case 'double':
188 1
                    $value = (float)$rawValue;
189 1
                    break;
190 4
                case 'boolean':
191 3
                case 'bool':
192 4
                    $value = (bool)$rawValue;
193 4
                    break;
194
            }
195
        } else {
196 2
            $value = $rawValue;
197
        }
198 4
        return $value;
199
    }
200
}
201