Passed
Push — master ( ca9e15...073e4f )
by Brian
12:50
created

Parser::entry()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Bmatovu\Ussd;
4
5
use Bmatovu\Ussd\Contracts\AnswerableTag;
6
use Bmatovu\Ussd\Traits\ParserUtils;
7
use Illuminate\Container\Container;
8
use Illuminate\Contracts\Config\Repository as ConfigRepository;
9
10
class Parser
11
{
12
    use ParserUtils;
0 ignored issues
show
introduced by
The trait Bmatovu\Ussd\Traits\ParserUtils requires some properties which are not provided by Bmatovu\Ussd\Parser: $tagName, $attributes, $nodeValue
Loading history...
13
14
    protected \DOMXPath $xpath;
15
    protected string $sessionId;
16
    protected Store $store;
17
    protected bool $newSession = false;
18
19
    /**
20
     * @param \DOMXPath|string $xpath
21
     */
22 5
    public function __construct($xpath, string $sessionId)
23
    {
24 5
        $this->xpath = $xpath instanceof \DOMXPath ? $xpath : $this->xpathFromStr($xpath);
25
26 5
        $config = Container::getInstance()->make(ConfigRepository::class);
27 5
        $store = $config->get('ussd.cache.store', 'file');
28 5
        $ttl = $config->get('ussd.cache.ttl', 120);
29 5
        $this->store = new Store($store, $ttl, $sessionId);
30
31 5
        if ($this->sessionExists($sessionId)) {
32 2
            return;
33
        }
34
35 3
        $this->newSession = true;
36 3
        $this->store->put('_session_id', $sessionId);
37 3
        $this->store->put('_answer', '');
38 3
        $this->store->put('_pre', '');
39 3
        $this->store->put('_exp', '/menu/*[1]');
40 3
        $this->store->put('_breakpoints', '[]');
41
    }
42
43 2
    public function entry(string $expression): self
44
    {
45 2
        if ($this->newSession) {
46 2
            $this->store->put('_exp', $expression);
47
        }
48
49 2
        return $this;
50
    }
51
52
    public function save(array $options): self
53
    {
54
        foreach ($options as $key => $value) {
55
            $this->store->put($key, $value);
56
        }
57
58
        return $this;
59
    }
60
61 5
    public function parse(?string $userInput = ''): string
62
    {
63 5
        $answer = $this->getAnswer($userInput);
64
65 5
        if ($this->newSession) {
66 3
            $inquiry = $this->doParse();
67
68 1
            if (! $answer) {
69 1
                return $inquiry;
70
            }
71
        }
72
73 2
        $answers = explode('*', $answer);
74
75 2
        foreach ($answers as $answer) {
76 2
            $inquiry = $this->doParse($answer);
77
        }
78
79 2
        return $inquiry;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $inquiry does not seem to be defined for all execution paths leading up to this point.
Loading history...
80
    }
81
82 5
    protected function doParse(?string $answer = ''): ?string
83
    {
84 5
        $this->doProcess($answer);
85
86 5
        $exp = $this->store->get('_exp');
87 5
        $node = $this->xpath->query($exp)->item(0);
88
89 5
        if (! $node) {
90 1
            $this->doBreak();
91
        }
92
93 5
        $inquiry = $this->doRender();
94
95 3
        if (! $inquiry) {
96 1
            return $this->doParse($answer);
97
        }
98
99 3
        return $inquiry;
100
    }
101
102 5
    protected function doProcess(?string $answer): void
103
    {
104 5
        $pre = $this->store->get('_pre');
105
106 5
        if (! $pre) {
107 4
            return;
108
        }
109
110 2
        $preNode = $this->xpath->query($pre)->item(0);
111
112 2
        $tagName = $this->resolveTagName($preNode);
113 2
        $tag = $this->instantiateTag($tagName, [$preNode, $this->store]);
114
115 2
        if (! $tag instanceof AnswerableTag) {
116 2
            return;
117
        }
118
119
        $tag->process($answer);
120
    }
121
122 1
    protected function doBreak(): void
123
    {
124 1
        $exp = $this->store->get('_exp');
125
126 1
        $breakpoints = (array) json_decode((string) $this->store->get('_breakpoints'), true);
127
128 1
        if (! $breakpoints || ! isset($breakpoints[0][$exp])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $breakpoints of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
129
            throw new \Exception('Missing tag');
130
        }
131
132 1
        $breakpoint = array_shift($breakpoints);
133 1
        $this->store->put('_exp', $breakpoint[$exp]);
134 1
        $this->store->put('_breakpoints', json_encode($breakpoints));
135
    }
136
137 5
    protected function doRender(): ?string
138
    {
139 5
        $exp = $this->store->get('_exp');
140
141 5
        $node = $this->xpath->query($exp)->item(0);
142
143 5
        $tagName = $this->resolveTagName($node);
144 5
        $tag = $this->instantiateTag($tagName, [$node, $this->store]);
145 4
        $inquiry = $tag->handle();
146
147 3
        $exp = $this->store->get('_exp');
148 3
        $breakpoints = (array) json_decode((string) $this->store->get('_breakpoints'), true);
149
150 3
        if ($breakpoints && isset($breakpoints[0][$exp])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $breakpoints of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
151
            $breakpoint = array_shift($breakpoints);
152
            $this->store->put('_exp', $breakpoint[$exp]);
153
            $this->store->put('_breakpoints', json_encode($breakpoints));
154
        }
155
156 3
        return $inquiry;
157
    }
158
}
159