Passed
Push — master ( 893d1e...31005e )
by Kirill
03:35
created

ReflectionArgument   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 156
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 23
eloc 63
c 0
b 0
f 0
dl 0
loc 156
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A createArgument() 0 21 6
A getType() 0 3 1
A getValue() 0 3 1
A __construct() 0 4 1
C locateArguments() 0 57 12
A stringValue() 0 10 2
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Tokenizer\Reflection;
13
14
use Spiral\Tokenizer\Exception\ReflectionException;
15
16
/**
17
 * Represent argument using in method or function invocation with it's type and value.
18
 */
19
final class ReflectionArgument
20
{
21
    /**
22
     * Argument types.
23
     */
24
    public const CONSTANT   = 'constant';   //Scalar constant and not variable.
25
    public const VARIABLE   = 'variable';   //PHP variable
26
    public const EXPRESSION = 'expression'; //PHP code (expression).
27
    public const STRING     = 'string';     //Simple scalar string, can be fetched using stringValue().
28
29
    /** @var string */
30
    private $type;
31
32
    /** @var string */
33
    private $value;
34
35
    /**
36
     * New instance of ReflectionArgument.
37
     *
38
     * @param string $type  Argument type (see top constants).
39
     * @param string $value Value in a form of php code.
40
     */
41
    public function __construct($type, string $value)
42
    {
43
        $this->type = $type;
44
        $this->value = $value;
45
    }
46
47
    /**
48
     * @return string
49
     */
50
    public function getType(): string
51
    {
52
        return $this->type;
53
    }
54
55
    /**
56
     * @return string
57
     */
58
    public function getValue(): string
59
    {
60
        return $this->value;
61
    }
62
63
    /**
64
     * Convert argument value into valid string. Can be applied only for STRING type arguments.
65
     *
66
     * @return string
67
     *
68
     * @throws ReflectionException When value can not be converted into string.
69
     */
70
    public function stringValue(): string
71
    {
72
        if ($this->type != self::STRING) {
73
            throw new ReflectionException(
74
                "Unable to represent value as string, value type is '{$this->type}'"
75
            );
76
        }
77
78
        //The most reliable way
79
        return eval("return {$this->value};");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
80
    }
81
82
    /**
83
     * Create Argument reflections based on provided set of tokens (fetched from invoke).
84
     *
85
     * @param array $tokens
86
     * @return self[]
87
     */
88
    public static function locateArguments(array $tokens): array
89
    {
90
        $definition = null;
91
        $level = 0;
92
93
        $result = [];
94
        foreach ($tokens as $token) {
95
            if ($token[ReflectionFile::TOKEN_TYPE] == T_WHITESPACE) {
96
                continue;
97
            }
98
99
            if (empty($definition)) {
100
                $definition = ['type' => self::EXPRESSION, 'value' => '', 'tokens' => []];
101
            }
102
103
            if (
104
                $token[ReflectionFile::TOKEN_TYPE] == '('
105
                || $token[ReflectionFile::TOKEN_TYPE] == '['
106
            ) {
107
                ++$level;
108
                $definition['value'] .= $token[ReflectionFile::TOKEN_CODE];
109
                continue;
110
            }
111
112
            if (
113
                $token[ReflectionFile::TOKEN_TYPE] == ')'
114
                || $token[ReflectionFile::TOKEN_TYPE] == ']'
115
            ) {
116
                --$level;
117
                $definition['value'] .= $token[ReflectionFile::TOKEN_CODE];
118
                continue;
119
            }
120
121
            if ($level) {
122
                $definition['value'] .= $token[ReflectionFile::TOKEN_CODE];
123
                continue;
124
            }
125
126
            if ($token[ReflectionFile::TOKEN_TYPE] == ',') {
127
                $result[] = self::createArgument($definition);
128
                $definition = null;
129
                continue;
130
            }
131
132
            $definition['tokens'][] = $token;
133
            $definition['value'] .= $token[ReflectionFile::TOKEN_CODE];
134
        }
135
136
        //Last argument
137
        if (is_array($definition)) {
138
            $definition = self::createArgument($definition);
139
            if (!empty($definition->getType())) {
140
                $result[] = $definition;
141
            }
142
        }
143
144
        return $result;
145
    }
146
147
    /**
148
     * Create Argument reflection using token definition. Internal method.
149
     *
150
     * @see locateArguments
151
     * @param array $definition
152
     * @return self
153
     */
154
    private static function createArgument(array $definition): ReflectionArgument
155
    {
156
        $result = new static(self::EXPRESSION, $definition['value']);
157
158
        if (count($definition['tokens']) == 1) {
159
            //If argument represent by one token we can try to resolve it's type more precisely
160
            switch ($definition['tokens'][0][0]) {
161
                case T_VARIABLE:
162
                    $result->type = self::VARIABLE;
163
                    break;
164
                case T_LNUMBER:
165
                case T_DNUMBER:
166
                    $result->type = self::CONSTANT;
167
                    break;
168
                case T_CONSTANT_ENCAPSED_STRING:
169
                    $result->type = self::STRING;
170
                    break;
171
            }
172
        }
173
174
        return $result;
175
    }
176
}
177