Passed
Pull Request — master (#22)
by Nico
14:21
created

CallableUserMethod   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 82
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 30
dl 0
loc 82
ccs 30
cts 30
cp 1
rs 10
c 2
b 0
f 0
wmc 11
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 nicoSWD\Rule\Grammar\CallableUserFunctionInterface;
12
use nicoSWD\Rule\TokenStream\Token\BaseToken;
13
use nicoSWD\Rule\TokenStream\Token\TokenFactory;
14
15
final class CallableUserMethod implements CallableUserFunctionInterface
16
{
17
    private const MAGIC_METHOD_PREFIX = '__';
18
19
    private readonly TokenFactory $tokenFactory;
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting T_VARIABLE on line 19 at column 21
Loading history...
20
    private readonly Closure $callable;
21
22
    private array $methodPrefixes = ['', 'get', 'is', 'get_', 'is_'];
23
24
    /**
25
     * @throws Exception\UndefinedMethodException
26
     * @throws Exception\ForbiddenMethodException
27
     */
28
    public function __construct(BaseToken $token, TokenFactory $tokenFactory, string $methodName)
29 52
    {
30
        $this->tokenFactory = $tokenFactory;
31 52
        $this->callable = $this->getCallable($token, $methodName);
32 52
    }
33 18
34
    public function call(?BaseToken ...$param): BaseToken
35 18
    {
36
        $callable = $this->callable;
37 18
38
        return $this->tokenFactory->createFromPHPType(
39 18
            $callable(...$param)
40 18
        );
41
    }
42
43
    /**
44
     * @throws Exception\UndefinedMethodException
45
     * @throws Exception\ForbiddenMethodException
46
     */
47
    private function getCallable(BaseToken $token, string $methodName): Closure
48 52
    {
49
        $object = $token->getValue();
50 52
51
        if (property_exists($object, $methodName)) {
52 52
            return static fn (): mixed => $object->{$methodName};
53
        }
54 4
55 4
        $method = $this->findCallableMethod($object, $methodName);
56
57
        return fn (?BaseToken ...$params): mixed => $method(
58 48
            ...$this->getTokenValues($params)
59
        );
60
    }
61 14
62 14
    /**
63
     * @throws Exception\UndefinedMethodException
64
     * @throws Exception\ForbiddenMethodException
65
     */
66
    private function findCallableMethod(object $object, string $methodName): callable
67
    {
68
        $this->assertNonMagicMethod($methodName);
69 48
        $index = 0;
70
71 48
        do {
72
            if (!isset($this->methodPrefixes[$index])) {
73 18
                throw new Exception\UndefinedMethodException();
74 18
            }
75
76
            $callableMethod = $this->methodPrefixes[$index++] . $methodName;
77 18
        } while (!is_callable([$object, $callableMethod]));
78 4
79
        return [$object, $callableMethod];
80
    }
81 18
82 18
    private function getTokenValues(array $params): array
83
    {
84 14
        return array_map(static fn (BaseToken $token): mixed => $token->getValue(), $params);
85
    }
86
87 14
    /** @throws Exception\ForbiddenMethodException */
88
    private function assertNonMagicMethod(string $methodName): void
89 14
    {
90
        if (str_starts_with($methodName, self::MAGIC_METHOD_PREFIX)) {
91 14
            throw new Exception\ForbiddenMethodException();
92 4
        }
93
    }
94
}
95