Passed
Push — master ( 09fa4e...8aa2ec )
by Fran
02:59
created

Dto::checkCastedValue()   C

Complexity

Conditions 12
Paths 13

Size

Total Lines 35
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 12.5898

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 12
eloc 28
c 6
b 0
f 0
nc 13
nop 2
dl 0
loc 35
ccs 21
cts 25
cp 0.84
crap 12.5898
rs 6.9666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\I18nHelper;
9
use PSFS\base\types\helpers\InjectorHelper;
10
11
/**
12
 * Class Dto
13
 * @package PSFS\base\dto
14
 */
15
class Dto extends Singleton implements \JsonSerializable
16
{
17
    /**
18
     * @var array
19
     */
20
    protected array $__cache = [];
21
22 4
    public function __construct($hydrate = true)
23
    {
24 4
        parent::__construct();
25 4
        if ($hydrate) {
26 2
            $this->fromArray(Request::getInstance()->getData());
27
        }
28
    }
29
30
    /**
31
     * ToArray wrapper
32
     * @return array
33
     */
34 4
    public function toArray()
35
    {
36 4
        return $this->__toArray();
37
    }
38
39
    /**
40
     * Convert dto to array representation
41
     * @return array
42
     */
43 4
    public function __toArray()
44
    {
45 4
        $dto = [];
46
        try {
47 4
            $reflectionClass = new \ReflectionClass($this);
48 4
            $properties = $reflectionClass->getProperties(\ReflectionProperty::IS_PUBLIC);
49 4
            if (count($properties) > 0) {
50 4
                foreach ($properties as $property) {
51 4
                    $value = $property->getValue($this);
52 4
                    if (is_object($value) && method_exists($value, 'toArray')) {
53 1
                        $dto[$property->getName()] = $value->toArray();
54 4
                    } elseif (is_array($value)) {
55 4
                        foreach ($value as &$arrValue) {
56 2
                            if ($arrValue instanceof Dto) {
57 1
                                $arrValue = $arrValue->toArray();
58
                            }
59
                        }
60 4
                        $dto[$property->getName()] = $value;
61
                    } else {
62 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...
63 4
                        $dto[$property->getName()] = $this->checkCastedValue($property->getValue($this), $type ?: 'string');
64
                    }
65
                }
66
            }
67
        } catch (\Exception $e) {
68
            Logger::log(get_class($this) . ': ' . $e->getMessage(), LOG_ERR);
69
        }
70 4
        return $dto;
71
    }
72
73
    /**
74
     * Convert to string representation
75
     * @return string
76
     */
77 1
    public function __toString()
78
    {
79 1
        return get_class($this);
80
    }
81
82
    /**
83
     * @param array $properties
84
     * @param string $key
85
     * @param mixed|null $value
86
     */
87 2
    protected function parseDtoField(array $properties, string $key, $value = null)
88
    {
89 2
        list($type, $isArray) = $this->extractTypes($properties, $key);
90 2
        $reflector = (class_exists($type)) ? new \ReflectionClass($type) : null;
91 2
        if (null !== $reflector && $reflector->isSubclassOf(Dto::class)) {
92 1
            if (is_array($value)) {
93 1
                if (!array_key_exists($type, $this->__cache)) {
94 1
                    $this->__cache[$type] = new $type(false);
95
                }
96 1
                if ($isArray) {
97 1
                    $this->$key = [];
98 1
                    foreach ($value as $data) {
99 1
                        if (is_array($data)) {
100 1
                            $dto = clone $this->__cache[$type];
101 1
                            $dto->fromArray($data);
102 1
                            $this->$key[] = $dto;
103
                        }
104
                    }
105
                } else {
106 1
                    $this->$key = clone $this->__cache[$type];
107 1
                    $this->$key->fromArray($value);
108
                }
109
            }
110
        } else {
111 2
            $this->castValue($key, $value, $type);
112
        }
113
    }
114
115
    /**
116
     * Hydrate object from array
117
     * @param array $object
118
     * @throws \ReflectionException
119
     */
120 3
    public function fromArray(array $object = [])
121
    {
122 3
        if (count($object) > 0) {
123 2
            $reflector = new \ReflectionClass($this);
124 2
            $properties = InjectorHelper::extractProperties($reflector, \ReflectionProperty::IS_PUBLIC, InjectorHelper::VAR_PATTERN);
125 2
            unset($reflector);
126 2
            foreach ($object as $key => $value) {
127 2
                if (property_exists($this, $key) && null !== $value) {
128 2
                    $this->parseDtoField($properties, $key, $value);
129
                }
130
            }
131
        }
132
    }
133
134
    /**
135
     * @return mixed
136
     */
137 1
    public function jsonSerialize(): array|string|null
138
    {
139 1
        return $this->toArray();
140
    }
141
142
    /**
143
     * @param array $properties
144
     * @param string $key
145
     * @return array
146
     */
147 2
    protected function extractTypes(array $properties, string $key): array
148
    {
149 2
        $type = 'string';
150 2
        $isArray = false;
151 2
        if (array_key_exists($key, $properties)) {
152 2
            $type = $properties[$key];
153 2
            if (preg_match('/(\[\]|Array)/i', $type)) {
154 1
                $type = preg_replace('/(\[\]|Array)/i', '', $type);
155 1
                $isArray = true;
156
            }
157
        }
158 2
        return array($type, $isArray);
159
    }
160
161
    /**
162
     * @param string $key
163
     * @param mixed $value
164
     * @param string $type
165
     */
166 2
    protected function castValue(string $key, mixed $value, string $type): void
167
    {
168 2
        $this->$key = $this->checkCastedValue($value, $type);
169
    }
170
171
    /**
172
     * @param mixed $rawValue
173
     * @param string $type
174
     * @return bool|float|int|string|null
175
     */
176 4
    protected function checkCastedValue(mixed $rawValue, string $type)
177
    {
178 4
        if (null !== $rawValue) {
179
            switch ($type) {
180
                default:
181 4
                case 'string':
182
                    // Cleaning HTML dangerous tags
183 4
                    if(is_array($rawValue)) {
184
                        foreach($rawValue as &$item) {
185
                            $item = $this->checkCastedValue($item, $type);
186
                        }
187
                        $value = $rawValue;
188 4
                    } elseif(is_string($rawValue)) {
189 4
                        $value = I18nHelper::cleanHtmlAttacks($rawValue);
190
                    } else {
191
                        $value = $rawValue;
192
                    }
193 4
                    break;
194 4
                case 'integer':
195 4
                case 'int':
196 2
                    $value = (integer)$rawValue;
197 2
                    break;
198 4
                case 'float':
199 4
                case 'double':
200 1
                    $value = (float)$rawValue;
201 1
                    break;
202 4
                case 'boolean':
203 3
                case 'bool':
204 4
                    $value = (bool)$rawValue;
205 4
                    break;
206
            }
207
        } else {
208 2
            $value = $rawValue;
209
        }
210 4
        return $value;
211
    }
212
}
213