Passed
Push — master ( 89ed0c...a5fbdb )
by Brian
02:33
created

Parser::sessionExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Bmatovu\Ussd;
4
5
use Bmatovu\Ussd\Contracts\Tag;
6
use Bmatovu\Ussd\Support\Arr;
7
use Illuminate\Contracts\Cache\Repository as CacheContract;
8
use Illuminate\Support\Facades\Log;
9
use Illuminate\Support\Str;
10
11
class Parser
12
{
13
    protected \DOMXPath $xpath;
14
    protected CacheContract $cache;
15
    protected string $prefix;
16
    protected int $ttl;
17
18 6
    public function __construct(\DOMXPath $xpath, array $options, CacheContract $cache, ?int $ttl = null)
19
    {
20 6
        $this->xpath = $xpath;
21 6
        $this->cache = $cache;
22 6
        $this->ttl = $ttl;
23
24 6
        $this->bootstrap($options);
25
    }
26
27 5
    public function parse(?string $answer): string
28
    {
29 5
        $this->processResponse($answer);
30
31 5
        $exp = $this->cache->get("{$this->prefix}_exp");
32
33 5
        $node = $this->xpath->query($exp)->item(0);
34
35 5
        if (! $node) {
36 1
            $this->setBreakpoint();
37
        }
38
39 5
        $output = $this->renderNext();
40
41 3
        if (! $output) {
42 1
            return $this->parse($answer);
43
        }
44
45 3
        return $output;
46
    }
47
48 5
    protected function sessionExists(string $sessionId): bool
49
    {
50 5
        $preSessionId = $this->cache->get("{$this->prefix}_session_id", '');
51
52 5
        return $preSessionId === $sessionId;
53
    }
54
55 6
    protected function bootstrap(array $options)
56
    {
57 6
        $required = ['session_id', 'phone_number', 'service_code', 'expression'];
58
59 6
        if ($missing = Arr::keysDiff($required, $options)) {
60 1
            $msg = array_pop($missing);
61
62 1
            if ($missing) {
63 1
                $msg = implode(', ', $missing).', and '.$msg;
64
            }
65
66 1
            throw new \Exception('Missing parser options: '.$msg);
67
        }
68
69
        [
70
            'session_id' => $session_id,
71
            'phone_number' => $phone_number,
72
            'service_code' => $service_code,
73
            'expression' => $expression,
74
        ] = $options;
75
76 5
        $this->prefix = "{$phone_number}{$service_code}";
77
78
        // ...
79
80 5
        $preSessionId = $this->cache->get("{$this->prefix}_session_id", '');
0 ignored issues
show
Unused Code introduced by
The assignment to $preSessionId is dead and can be removed.
Loading history...
81
82 5
        if ($this->sessionExists($session_id)) {
83 2
            return;
84
        }
85
86 3
        $this->cache->put("{$this->prefix}_session_id", $session_id, $this->ttl);
87 3
        $this->cache->put("{$this->prefix}_service_code", $service_code, $this->ttl);
88 3
        $this->cache->put("{$this->prefix}_phone_number", $phone_number, $this->ttl);
89
90 3
        $this->cache->put("{$this->prefix}_pre", '', $this->ttl);
91 3
        $this->cache->put("{$this->prefix}_exp", $expression, $this->ttl);
92 3
        $this->cache->put("{$this->prefix}_breakpoints", '[]', $this->ttl);
93
    }
94
95 5
    protected function processResponse(?string $answer): void
96
    {
97 5
        $pre = $this->cache->get("{$this->prefix}_pre");
98
99 5
        if (! $pre) {
100 4
            return;
101
        }
102
103 2
        $preNode = $this->xpath->query($pre)->item(0);
104
105
        // Log::debug("Process  -->", ['tag' => $preNode->tagName, 'pre' => $pre]);
106
107 2
        $tagName = Str::studly($preNode->tagName);
108 2
        $tag = $this->createTag(__NAMESPACE__."\\Tags\\{$tagName}Tag", [$preNode, $this->cache, $this->prefix, $this->ttl]);
109 2
        $tag->process($answer);
110
    }
111
112 1
    protected function setBreakpoint(): void
113
    {
114
        // Log::debug("Error    -->", ['tag' => '', 'exp' => $exp]);
115
116 1
        $exp = $this->cache->get("{$this->prefix}_exp");
117
118 1
        $breakpoints = (array) json_decode((string) $this->cache->get("{$this->prefix}_breakpoints"), true);
119
120 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...
121
            throw new \Exception('Missing tag');
122
        }
123
124 1
        $breakpoint = array_shift($breakpoints);
125 1
        $this->cache->put("{$this->prefix}_exp", $breakpoint[$exp], $this->ttl);
126 1
        $this->cache->put("{$this->prefix}_breakpoints", json_encode($breakpoints), $this->ttl);
127
    }
128
129 5
    protected function renderNext(): ?string
130
    {
131
        // Log::debug("Handle   -->", ['tag' => $node->tagName, 'exp' => $exp]);
132
133 5
        $exp = $this->cache->get("{$this->prefix}_exp");
134
135 5
        $node = $this->xpath->query($exp)->item(0);
136
137 5
        $tagName = Str::studly($node->tagName);
138 5
        $tag = $this->createTag(__NAMESPACE__."\\Tags\\{$tagName}Tag", [$node, $this->cache, $this->prefix, $this->ttl]);
139 4
        $output = $tag->handle();
140
141 3
        $exp = $this->cache->get("{$this->prefix}_exp");
142 3
        $breakpoints = (array) json_decode((string) $this->cache->get("{$this->prefix}_breakpoints"), true);
143
144 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...
145
            $breakpoint = array_shift($breakpoints);
146
            $this->cache->put("{$this->prefix}_exp", $breakpoint[$exp], $this->ttl);
147
            $this->cache->put("{$this->prefix}_breakpoints", json_encode($breakpoints), $this->ttl);
148
        }
149
150 3
        return $output;
151
    }
152
153 5
    protected function createTag(string $fqcn, array $args = []): Tag
154
    {
155 5
        if (! class_exists($fqcn)) {
156 1
            throw new \Exception("Missing class: {$fqcn}");
157
        }
158
159 4
        return \call_user_func_array([new \ReflectionClass($fqcn), 'newInstance'], $args);
160
    }
161
}
162