Completed
Push — refactor ( 1ac315 )
by Akihito
05:56
created

Normalizer::__invoke()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.5386
c 0
b 0
f 0
cc 7
nc 6
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
        if (is_int($value)) {
50
            return new Scalar\LNumber($value);
51
        }
52
53
        if (is_float($value)) {
54
            return new Scalar\DNumber($value);
55
        }
56
57
        if (is_string($value)) {
58
            return new Scalar\String_($value);
59
        }
60
61
        return $this->noScalar($value);
62
    }
63
64
    /**
65
     * Return array or object node
66
     *
67
     * @param array<mixed>|mixed|object $value
68
     *
69
     * @return Expr\Array_|Expr\FuncCall
70
     */
71
    private function noScalar($value): Expr
72
    {
73
        if (is_array($value)) {
74
            return $this->arrayValue($value);
75
        }
76
77
        if (is_object($value)) {
78
            return $this->normalizeObject($value);
79
        }
80
81
        throw new InvalidInstance();
82
    }
83
84
    /**
85
     * Return "unserialize($object)" node
86
     *
87
     * @param object $object
88
     */
89
    private function normalizeObject($object): Expr\FuncCall
90
    {
91
        if ($object instanceof InjectorInterface) {
92
            return new Expr\FuncCall(new Expr\Variable('injector'));
93
        }
94
95
        $serialize = new Scalar\String_(serialize($object));
96
97
        return new Expr\FuncCall(new Node\Name('unserialize'), [new Arg($serialize)]);
98
    }
99
100
    /**
101
     * Return array value node
102
     *
103
     * @param array<int, mixed> $value
104
     */
105
    private function arrayValue($value): Expr\Array_
106
    {
107
        $items = [];
108
        $lastKey = -1;
109
        foreach ($value as $itemKey => $itemValue) {
110
            // for consecutive, numeric keys don't generate keys
111
            if ($lastKey !== null && ++$lastKey === $itemKey) {
112
                $items[] = new Expr\ArrayItem(
113
                    $this->__invoke($itemValue)
114
                );
115
            } else {
116
                $lastKey = null;
117
                $items[] = new Expr\ArrayItem(
118
                    $this->__invoke($itemValue),
119
                    $this->__invoke($itemKey)
120
                );
121
            }
122
        }
123
124
        return new Expr\Array_($items);
125
    }
126
}
127