Completed
Push — coverage ( 88355f...343a8c )
by Akihito
08:29
created

Normalizer::getValueNode()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 4
nc 4
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Compiler;
6
7
use PhpParser\Node;
8
use PhpParser\Node\Arg;
9
use PhpParser\Node\Expr;
10
use PhpParser\Node\Scalar;
11
use Ray\Compiler\Exception\InvalidInstance;
12
use Ray\Di\InjectorInterface;
13
14
use function is_array;
15
use function is_bool;
16
use function is_float;
17
use function is_int;
18
use function is_object;
19
use function is_string;
20
use function serialize;
21
22
/**
23
 * Value to code(Node) converter
24
 */
25
final class Normalizer
26
{
27
    /**
28
     * Normalizes a value: Converts nulls, booleans, integers,
29
     * floats, strings and arrays into their respective nodes
30
     *
31
     * @param mixed $value The value to normalize
32
     *
33
     * @return Expr The normalized value
34
     */
35
    public function __invoke($value): Expr
36
    {
37
        if ($value === null) {
38
            return new Expr\ConstFetch(
39
                new Node\Name('null')
40
            );
41
        }
42
43
        if (is_bool($value)) {
44
            return new Expr\ConstFetch(
45
                new Node\Name($value ? 'true' : 'false')
46
            );
47
        }
48
49
        return $this->getValueNode($value);
50
    }
51
52
    /**
53
     * Return array or object node
54
     *
55
     * @param array<mixed>|mixed|object $value
56
     *
57
     * @return Scalar\String_|Scalar\LNumber|Scalar\DNumber|Expr\Array_|Expr\FuncCall
58
     */
59
    private function getValueNode($value): Expr
60
    {
61
        if (is_string($value)) {
62
            return new Scalar\String_($value);
63
        }
64
65
        if (is_int($value)) {
66
            return new Scalar\LNumber($value);
67
        }
68
69
        if (is_float($value)) {
70
            return new Scalar\DNumber($value);
71
        }
72
73
        return $this->getValueNodeNoScalar($value);
74
    }
75
76
    /**
77
     * Return array or object node
78
     *
79
     * @param array<mixed>|mixed|object $value
80
     *
81
     * @return Expr\Array_|Expr\FuncCall
82
     */
83
    private function getValueNodeNoScalar($value): Expr
84
    {
85
        if (is_array($value)) {
86
            return $this->arrayValue($value);
87
        }
88
89
        if (is_object($value)) {
90
            return $this->normalizeObject($value);
91
        }
92
93
        throw new InvalidInstance();
94
    }
95
96
    /**
97
     * Return "unserialize($object)" node
98
     *
99
     * @param object $object
100
     */
101
    private function normalizeObject($object): Expr\FuncCall
102
    {
103
        if ($object instanceof InjectorInterface) {
104
            return new Expr\FuncCall(new Expr\Variable('injector'));
105
        }
106
107
        $serialize = new Scalar\String_(serialize($object));
108
109
        return new Expr\FuncCall(new Node\Name('unserialize'), [new Arg($serialize)]);
110
    }
111
112
    /**
113
     * Return array value node
114
     *
115
     * @param array<int, mixed> $value
116
     */
117
    private function arrayValue($value): Expr\Array_
118
    {
119
        $items = [];
120
        $lastKey = -1;
121
        foreach ($value as $itemKey => $itemValue) {
122
            // for consecutive, numeric keys don't generate keys
123
            if ($lastKey !== null && ++$lastKey === $itemKey) {
124
                $items[] = new Expr\ArrayItem(
125
                    $this->__invoke($itemValue)
126
                );
127
            } else {
128
                $lastKey = null;
129
                $items[] = new Expr\ArrayItem(
130
                    $this->__invoke($itemValue),
131
                    $this->__invoke($itemKey)
132
                );
133
            }
134
        }
135
136
        return new Expr\Array_($items);
137
    }
138
}
139