Passed
Push — master ( 057162...5e1705 )
by Brian
02:41
created

Ussd   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Test Coverage

Coverage 94.87%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 25
eloc 75
c 1
b 0
f 0
dl 0
loc 162
ccs 74
cts 78
cp 0.9487
rs 10

10 Methods

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