Passed
Push — latest ( 5fe1f6...9a2102 )
by Mark
02:40
created

Property::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 10
ccs 9
cts 9
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace UnicornFail\Emoji\Util;
6
7
class Property
8
{
9
    public const REGEX = '/^(?:(?P<emptyNullable>[!?]{2})|(?P<nullable>[!?]))??(?P<customType>[^[<]+)?(?P<type>array|bool|boolean|double|float|int|integer|null|object|string)??(?P<iterable>\[\])?(?:<(?P<callback>[^[]+)>)??$/Ui';
10
11
    public const TYPES = ['array', 'bool', 'boolean', 'double', 'float', 'int', 'integer', 'null', 'object', 'string'];
12
13
    /** @var ?string */
14
    public $callback;
15
16
    /** @var ?string */
17
    public $customType;
18
19
    /** @var bool */
20
    public $emptyNullable;
21
22
    /** @var bool */
23
    public $nullable;
24
25
    /** @var bool */
26
    public $iterable;
27
28
    /** @var bool */
29
    public $isPhpType;
30
31
    /** @var string */
32
    public $type;
33
34 120
    public function __construct(string $type)
35
    {
36 120
        \preg_match(self::REGEX, $type, $matches, PREG_UNMATCHED_AS_NULL);
37 120
        $this->callback      = $matches['callback'] ?? null;
38 120
        $this->customType    = $matches['customType'] ?? null;
39 120
        $this->emptyNullable = ! ! ($matches['emptyNullable'] ?? false);
40 120
        $this->iterable      = ! ! ($matches['iterable'] ?? false);
41 120
        $this->nullable      = ! ! ($this->emptyNullable || ($matches['nullable'] ?? false));
42 120
        $this->type          = $matches['type'] ?? 'mixed';
43 120
        $this->isPhpType     = \in_array($this->type, self::TYPES, true);
44 120
    }
45
46
    /**
47
     * @param mixed $value
48
     *
49
     * @return bool|float|int|mixed[]|object|string|null
50
     */
51 120
    public static function cast(string $type, $value)
52
    {
53 120
        return (new self($type))->castValue($value);
54
    }
55
56
    /**
57
     * @param mixed $value
58
     *
59
     * @return mixed
60
     */
61 36
    protected function castCallback($value)
62
    {
63 36
        $callback = $this->callback;
64 36
        if (\is_callable($callback)) {
65 36
            $value = $callback($value);
66
        }
67
68 36
        return $value;
69
    }
70
71
    /**
72
     * @param mixed $value
73
     *
74
     * @return mixed
75
     */
76 36
    protected function castCustomType($value)
77
    {
78 36
        $type = $this->customType;
79 36
        if (\is_string($type) && \class_exists($type)) {
80 36
            $value = new $type($value);
81
        }
82
83 36
        return $value;
84
    }
85
86
    /**
87
     * @param mixed $value
88
     *
89
     * @return mixed[]|bool|float|int|object|string|null
90
     */
91 90
    protected function castPhpType($value)
92
    {
93 90
        switch ($this->type) {
94 90
            case 'array':
95 3
                $value = (array) ($value ?? []);
96 3
                break;
97
98 87
            case 'bool':
99 81
            case 'boolean':
100 12
                $value = (bool) ($value ?? false);
101 12
                break;
102
103 75
            case 'double':
104 69
            case 'float':
105 45
                $value = (float) ($value ?? 0.0);
106 45
                break;
107
108 63
            case 'int':
109 54
            case 'integer':
110 48
                $value = (int) ($value ?? 0);
111 48
                break;
112
113 48
            case 'null':
114 6
                $value = null;
115 6
                break;
116
117 42
            case 'object':
118 3
                $value = (object) ($value ?? new \stdClass());
119 3
                break;
120
121 39
            case 'string':
122 39
                $value = (string) ($value ?? '');
123 39
                break;
124
        }
125
126 90
        return $this->emptyNullable && ! $value ? null : $value;
127
    }
128
129
    /**
130
     * @param mixed $value
131
     *
132
     * @return mixed[]|bool|float|int|object|string|null
133
     */
134 120
    public function castValue($value)
135
    {
136
        // Immediately return if nullable or empty.
137 120
        if (($this->nullable && $value === null) || ($this->emptyNullable && ! $value)) {
138 60
            return null;
139
        }
140
141 93
        if ($this->callback) {
142 36
            $value = $this->castCallback($value);
143
        }
144
145 93
        if ($this->iterable) {
146 39
            $value = (array) $value;
147 39
            $type  = $this->nullable ? \sprintf('?%s', $this->type) : $this->type;
148
149
            /** @var string[] $types */
150 39
            $types = \array_fill_keys(\array_keys($value), $type);
151
152 39
            return Normalize::properties($value, $types);
153
        }
154
155 93
        if ($this->customType) {
156 36
            $value = $this->castCustomType($value);
157
        } else {
158 90
            $value = $this->castPhpType($value);
159
        }
160
161 93
        return $value;
162
    }
163
}
164