Completed
Push — master ( f07a36...bb0ee0 )
by Nico
01:44
created

AST.php$0 ➔ call()   A

Complexity

Conditions 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 4
cts 5
cp 0.8
rs 9.9332
c 0
b 0
f 0
cc 2
crap 2.032
1
<?php declare(strict_types=1);
2
3
/**
4
 * @license     http://opensource.org/licenses/mit-license.php MIT
5
 * @link        https://github.com/nicoSWD
6
 * @author      Nicolas Oelgart <[email protected]>
7
 */
8
namespace nicoSWD\Rule\TokenStream;
9
10
use Closure;
11
use InvalidArgumentException;
12
use nicoSWD\Rule\Grammar\CallableUserFunctionInterface;
13
use nicoSWD\Rule\TokenStream\Exception\UndefinedVariableException;
14
use nicoSWD\Rule\TokenStream\Token\BaseToken;
15
use nicoSWD\Rule\TokenStream\Token\TokenFactory;
16
use nicoSWD\Rule\Tokenizer\TokenizerInterface;
17
use nicoSWD\Rule\TokenStream\Token\TokenObject;
18
19
class AST
20
{
21
    /** @var TokenizerInterface */
22
    private $tokenizer;
23
    /** @var TokenFactory */
24
    private $tokenFactory;
25
    /** @var TokenStreamFactory */
26
    private $tokenStreamFactory;
27
    /** @var Closure[] */
28
    private $functions = [];
29
    /** @var string[] */
30
    private $methods = [];
31
    /** @var mixed[] */
32
    private $variables = [];
33
34 226
    public function __construct(
35
        TokenizerInterface $tokenizer,
36
        TokenFactory $tokenFactory,
37
        TokenStreamFactory $tokenStreamFactory
38
    ) {
39 226
        $this->tokenizer = $tokenizer;
40 226
        $this->tokenFactory = $tokenFactory;
41 226
        $this->tokenStreamFactory = $tokenStreamFactory;
42 226
    }
43
44 220
    public function getStream(string $rule): TokenStream
45
    {
46 220
        return $this->tokenStreamFactory->create($this->tokenizer->tokenize($rule), $this);
47
    }
48
49 110
    public function getMethod(string $methodName, BaseToken $token): CallableUserFunctionInterface
50
    {
51 110
        if ($token instanceof TokenObject) {
52 2
            return $this->getUserObjectCallable($token, $methodName);
53
        }
54
55 108
        if (empty($this->methods)) {
56 108
            $this->registerMethods();
57
        }
58
59 108
        if (!isset($this->methods[$methodName])) {
60 4
            throw new Exception\UndefinedMethodException($methodName);
61
        }
62
63 104
        return new $this->methods[$methodName]($token);
64
    }
65
66 108
    private function registerMethods()
67
    {
68 108
        $this->methods = $this->tokenizer->getGrammar()->getInternalMethods();
69 108
    }
70
71 220
    public function setVariables(array $variables)
72
    {
73 220
        $this->variables = $variables;
74 220
    }
75
76 92
    public function getVariable(string $variableName): BaseToken
77
    {
78 92
        if (!$this->variableExists($variableName)) {
79 4
            throw new UndefinedVariableException($variableName);
80
        }
81
82 88
        return $this->tokenFactory->createFromPHPType($this->variables[$variableName]);
83
    }
84
85 92
    public function variableExists(string $variableName): bool
86
    {
87 92
        return array_key_exists($variableName, $this->variables);
88
    }
89
90 32
    public function getFunction(string $functionName): Closure
91
    {
92 32
        if (empty($this->functions)) {
93 32
            $this->registerFunctions();
94
        }
95
96 32
        if (!isset($this->functions[$functionName])) {
97 6
            throw new Exception\UndefinedFunctionException($functionName);
98
        }
99
100 26
        return $this->functions[$functionName];
101
    }
102
103
    private function registerFunctionClass(string $functionName, string $className)
104
    {
105 26
        $this->functions[$functionName] = function (BaseToken ...$args) use ($className): BaseToken {
106 26
            $function = new $className();
107
108 26
            if (!$function instanceof CallableUserFunctionInterface) {
109 2
                throw new InvalidArgumentException(
110 2
                    sprintf(
111 2
                        "%s must be an instance of %s",
112 2
                        $className,
113 2
                        CallableUserFunctionInterface::class
114
                    )
115
                );
116
            }
117
118 24
            return $function->call(...$args);
0 ignored issues
show
Documentation introduced by
$args is of type array<integer,object<nic...tream\Token\BaseToken>>, but the function expects a null|object<nicoSWD\Rule...Stream\Token\BaseToken>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
119
        };
120 30
    }
121
122 32
    private function registerFunctions()
123
    {
124 32
        foreach ($this->tokenizer->getGrammar()->getInternalFunctions() as $functionName => $className) {
125 30
            $this->registerFunctionClass($functionName, $className);
126
        }
127 32
    }
128
129
    private function getUserObjectCallable(BaseToken $token, string $methodName): CallableUserFunctionInterface
130
    {
131
        return new class ($token, $this->tokenFactory, $methodName) implements CallableUserFunctionInterface
132
        {
133
            /** @var BaseToken */
134
            private $token;
135
            /** @var TokenFactory */
136
            private $tokenFactory;
137
            /** @var string */
138
            private $methodName;
139
140 2
            public function __construct(BaseToken $token, TokenFactory $tokenFactory, string $methodName)
141
            {
142 2
                $this->token = $token;
143 2
                $this->tokenFactory = $tokenFactory;
144 2
                $this->methodName = $methodName;
145 2
            }
146
147 2
            public function call(BaseToken $param = null): BaseToken
148
            {
149 2
                $object = [$this->token->getValue(), $this->methodName];
150
151 2
                if (!is_callable($object)) {
152
                    throw new \Exception();
153
                }
154
155 2
                return $this->tokenFactory->createFromPHPType($object());
156
            }
157
        };
158
    }
159
}
160