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; |
||
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; |
|
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
|
|||
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
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 ![]() |
|||
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 |
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.