Passed
Push — master ( 8d2d6f...b228e7 )
by Brian
06:43 queued 13s
created

Ussd::doParse()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 18
ccs 8
cts 8
cp 1
rs 9.9666
c 0
b 0
f 0
cc 3
nc 4
nop 1
crap 3
1
<?php
2
3
namespace Bmatovu\Ussd;
4
5
use Bmatovu\Ussd\Contracts\AnswerableTag;
6
use Bmatovu\Ussd\Traits\Utilities;
7
use Illuminate\Container\Container;
8
use Illuminate\Contracts\Config\Repository as ConfigRepository;
9
10
class Ussd
11
{
12
    use Utilities;
0 ignored issues
show
introduced by
The trait Bmatovu\Ussd\Traits\Utilities requires some properties which are not provided by Bmatovu\Ussd\Ussd: $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 $menu
21
     */
22 9
    public function __construct($menu, string $sessionId)
23
    {
24 9
        $this->xpath = $menu instanceof \DOMXPath ? $menu : $this->fileToXpath($menu);
25
26 9
        $config = Container::getInstance()->make(ConfigRepository::class);
27 9
        $store = $config->get('ussd.cache.store', 'file');
28 9
        $ttl = $config->get('ussd.cache.ttl', 120);
29 9
        $this->store = new Store($store, $ttl, $sessionId);
30
31 9
        if ($this->sessionExists($sessionId)) {
32 2
            return;
33
        }
34
35 7
        $this->newSession = true;
36 7
        $this->store->put('_session_id', $sessionId);
37 7
        $this->store->put('_answer', '');
38 7
        $this->store->put('_pre', '');
39 7
        $this->store->put('_exp', '/menu/*[1]');
40 7
        $this->store->put('_breakpoints', '[]');
41
    }
42
43 3
    public static function make(\DOMXPath|string $menu, string $sessionId): self
44
    {
45 3
        return new static($menu, $sessionId);
46 3
    }
47
48
    public static function new(...$args): self
49 3
    {
50
        return new static(...$args);
51
    }
52 1
53
    public function entry(string $expression): self
54 1
    {
55 1
        if ($this->newSession) {
56
            $this->store->put('_exp', $expression);
57
        }
58 1
59
        return $this;
60
    }
61 9
62
    public function save(array $options): self
63 9
    {
64
        foreach ($options as $key => $value) {
65 9
            $this->store->put($key, $value);
66 7
        }
67
68 5
        return $this;
69 3
    }
70
71
    public function handle(?string $userInput = ''): string
72
    {
73 4
        $answer = $this->getAnswer($userInput);
74
75 4
        if ($this->newSession) {
76 4
            $inquiry = $this->doParse();
77
78
            if (!$answer) {
79 3
                return $inquiry;
80
            }
81
        }
82 9
83
        $answers = explode('*', $answer);
84 9
85
        foreach ($answers as $answer) {
86 9
            $inquiry = $this->doParse($answer);
87 9
        }
88
89 9
        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...
90 1
    }
91
92
    protected function doParse(?string $answer = ''): ?string
93 9
    {
94
        $this->doProcess($answer);
95 7
96 1
        $exp = $this->store->get('_exp');
97
        $node = $this->xpath->query($exp)->item(0);
98
99 7
        if (!$node) {
100
            $this->doBreak();
101
        }
102 9
103
        $inquiry = $this->doRender();
104 9
105
        if (!$inquiry) {
106 9
            return $this->doParse($answer);
107 8
        }
108
109
        return $inquiry;
110 4
    }
111
112 4
    protected function doProcess(?string $answer): void
113 4
    {
114
        $pre = $this->store->get('_pre');
115 4
116 2
        if (!$pre) {
117
            return;
118
        }
119 2
120
        $preNode = $this->xpath->query($pre)->item(0);
121
122 1
        $tagName = $this->resolveTagName($preNode);
123
        $tag = $this->instantiateTag($tagName, [$preNode, $this->store]);
124 1
125
        if (!$tag instanceof AnswerableTag) {
126 1
            return;
127
        }
128 1
129
        $tag->process($answer);
130
    }
131
132 1
    protected function doBreak(): void
133 1
    {
134 1
        $exp = $this->store->get('_exp');
135
136
        $breakpoints = (array) json_decode((string) $this->store->get('_breakpoints'), true);
137 9
138
        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...
139 9
            throw new \Exception('Missing tag');
140
        }
141 9
142
        $breakpoint = array_shift($breakpoints);
143 9
        $this->store->put('_exp', $breakpoint[$exp]);
144 9
        $this->store->put('_breakpoints', json_encode($breakpoints));
145 8
    }
146
147 7
    protected function doRender(): ?string
148 7
    {
149
        $exp = $this->store->get('_exp');
150 7
151
        $node = $this->xpath->query($exp)->item(0);
152
153
        $tagName = $this->resolveTagName($node);
154
        $tag = $this->instantiateTag($tagName, [$node, $this->store]);
155
        $inquiry = $tag->handle();
156 7
157
        $exp = $this->store->get('_exp');
158
        $breakpoints = (array) json_decode((string) $this->store->get('_breakpoints'), true);
159
160
        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...
161
            $breakpoint = array_shift($breakpoints);
162
            $this->store->put('_exp', $breakpoint[$exp]);
163
            $this->store->put('_breakpoints', json_encode($breakpoints));
164
        }
165
166
        return $inquiry;
167
    }
168
}
169