Passed
Push — latest ( 03f783...d7ba96 )
by Mark
02:37
created

Property::castValueCallback()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
cc 3
nc 2
nop 1
crap 3
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 39
    protected function castIterable($value): array
62
    {
63 39
        $value = (array) $value;
64 39
        $type  = $this->nullable ? \sprintf('?%s', $this->type) : $this->type;
65
66
        /** @var string[] $types */
67 39
        $types = \array_fill_keys(\array_keys($value), $type);
68
69 39
        return Normalize::properties($value, $types);
70
    }
71
72
    /**
73
     * @param mixed $value
74
     *
75
     * @return mixed[]|bool|float|int|object|string|null
76
     */
77 120
    public function castValue($value)
78
    {
79
        // Immediately return if nullable or empty.
80 120
        if (($this->nullable && $value === null) || ($this->emptyNullable && ! $value)) {
81 60
            return null;
82
        }
83
84 93
        $value = $this->castValueCallback($value);
85
86 93
        if ($this->iterable) {
87 39
            return $this->castIterable($value);
88
        }
89
90 93
        return $this->castValueType($value);
91
    }
92
93
    /**
94
     * @param mixed $value
95
     *
96
     * @return mixed
97
     */
98 93
    protected function castValueCallback($value)
99
    {
100 93
        if (($callback = $this->callback) && \is_callable($callback)) {
101 36
            $value = $callback($value);
102
        }
103
104 93
        return $value;
105
    }
106
107
    /**
108
     * @param mixed $value
109
     *
110
     * @return mixed
111
     */
112 93
    protected function castValueType($value)
113
    {
114 93
        if (($type = $this->customType) && \class_exists($type)) {
115 36
            $value = new $type($value);
116
        } else {
117 90
            Normalize::setType($value, $this->type);
118
        }
119
120 93
        return $this->emptyNullable && ! $value ? null : $value;
121
    }
122
}
123