Completed
Push — master ( 3e6041...cc8efe )
by Ehsan
24:25 queued 18:06
created

AbstractBaseListener   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 94.74%

Importance

Changes 0
Metric Value
wmc 30
lcom 2
cbo 3
dl 0
loc 252
ccs 72
cts 76
cp 0.9474
rs 10
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A listen() 0 21 3
extractRequest() 0 1 ?
getChannelId() 0 1 ?
getKey() 0 1 ?
B getRequest() 0 16 5
A setRequest() 0 4 1
A getConfig() 0 8 2
A setConfig() 0 4 1
verifyOrigin() 0 1 ?
isThisBot() 0 1 ?
A getRequestUtility() 0 8 2
A setRequestUtility() 0 4 1
B respondOK() 0 36 3
B verifyRequest() 0 24 4
A determineAction() 0 15 4
A getMessage() 0 12 4
1
<?php
2
3
namespace Botonomous\listener;
4
5
use Botonomous\BotonomousException;
6
use Botonomous\Config;
7
use Botonomous\Event;
8
use Botonomous\utility\RequestUtility;
9
10
abstract class AbstractBaseListener
11
{
12
    const ORIGIN_VERIFICATION_SUCCESS_KEY = 'success';
13
    const ORIGIN_VERIFICATION_MESSAGE_KEY = 'message';
14
15
    private $config;
16
    private $request;
17
    private $requestUtility;
18
19
    /**
20
     * listen.
21
     *
22
     * @throws \Exception
23
     *
24
     * @return mixed
25
     */
26 10
    public function listen()
27
    {
28
        // This is needed otherwise timeout error is displayed
29 10
        $this->respondOK();
30
31 10
        $request = $this->extractRequest();
32
33 10
        if (empty($request)) {
34
            /* @noinspection PhpInconsistentReturnPointsInspection */
35 6
            return;
36
        }
37
38 4
        $this->setRequest($request);
39
40 4
        if ($this->isThisBot() !== false) {
41
            /* @noinspection PhpInconsistentReturnPointsInspection */
42 2
            return;
43
        }
44
45 2
        return $request;
46
    }
47
48
    /**
49
     * @return mixed
50
     */
51
    abstract public function extractRequest();
52
53
    /**
54
     * @return string
55
     */
56
    abstract public function getChannelId(): string;
57
58
    /**
59
     * @return string
60
     */
61
    abstract public function getKey(): string;
62
63
    /**
64
     * @param null|string $key
65
     *
66
     * @return mixed
67
     */
68 35
    public function getRequest(string $key = null)
69
    {
70 35
        if (!isset($this->request)) {
71
            // each listener has its own way of extracting the request
72 10
            $this->setRequest($this->extractRequest());
73
        }
74
75 35
        if ($key === null) {
76
            // return the entire request since key is null
77 18
            return $this->request;
78
        }
79
80 25
        if (is_array($this->request) && array_key_exists($key, $this->request)) {
81 23
            return $this->request[$key];
82
        }
83 9
    }
84
85
    /**
86
     * @param array $request
87
     */
88 45
    public function setRequest($request)
89
    {
90 45
        $this->request = $request;
91 45
    }
92
93
    /**
94
     * @return Config
95
     */
96 8
    public function getConfig(): Config
97
    {
98 8
        if (!isset($this->config)) {
99 5
            $this->setConfig(new Config());
100
        }
101
102 8
        return $this->config;
103
    }
104
105
    /**
106
     * @param Config $config
107
     */
108 12
    public function setConfig(Config $config)
109
    {
110 12
        $this->config = $config;
111 12
    }
112
113
    /**
114
     * Verify the request comes from Slack
115
     * Each listener must have have this and has got its own way to check the request.
116
     *
117
     * @throws \Exception
118
     *
119
     * @return array
120
     */
121
    abstract public function verifyOrigin();
122
123
    /**
124
     * Check if the request belongs to the bot itself.
125
     *
126
     * @throws \Exception
127
     *
128
     * @return bool
129
     */
130
    abstract public function isThisBot(): bool;
131
132
    /**
133
     * @return RequestUtility
134
     */
135 24
    public function getRequestUtility(): RequestUtility
136
    {
137 24
        if (!isset($this->requestUtility)) {
138 8
            $this->setRequestUtility((new RequestUtility()));
139
        }
140
141 24
        return $this->requestUtility;
142
    }
143
144
    /**
145
     * @param RequestUtility $requestUtility
146
     */
147 25
    public function setRequestUtility(RequestUtility $requestUtility)
148
    {
149 25
        $this->requestUtility = $requestUtility;
150 25
    }
151
152
    /**
153
     * respondOK.
154
     */
155 10
    protected function respondOK()
156
    {
157
        // check if fastcgi_finish_request is callable
158 10
        if (is_callable('fastcgi_finish_request')) {
159
            /*
160
             * http://stackoverflow.com/a/38918192
161
             * This works in Nginx but the next approach not
162
             */
163
            session_write_close();
164
            fastcgi_finish_request();
165
166
            /* @noinspection PhpInconsistentReturnPointsInspection */
167
            return;
168
        }
169
170 10
        ignore_user_abort(true);
171
172 10
        ob_start();
173 10
        header($this->getRequestUtility()->getServerProtocol().' 200 OK');
174
        // Disable compression (in case content length is compressed).
175 10
        header('Content-Encoding: none');
176 10
        header('Content-Length: '.ob_get_length());
177
178
        // Close the connection.
179 10
        header('Connection: close');
180
181 10
        ob_end_flush();
182
        // only if an output buffer is active do ob_flush
183 10
        if (ob_get_level() > 0) {
184 10
            ob_flush();
185
        }
186
187 10
        flush();
188
189
        /* @noinspection PhpInconsistentReturnPointsInspection */
190 10
    }
191
192
    /**
193
     * @throws \Exception
194
     *
195
     * @return array<string,boolean|string>
196
     */
197 5
    public function verifyRequest(): array
198
    {
199 5
        $originCheck = $this->verifyOrigin();
200
201 5
        if (!isset($originCheck[self::ORIGIN_VERIFICATION_SUCCESS_KEY])) {
202
            throw new BotonomousException('Success must be provided in verifyOrigin response');
203
        }
204
205 5
        if ($originCheck[self::ORIGIN_VERIFICATION_SUCCESS_KEY] !== true) {
206
            return [
207 1
                self::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
208 1
                self::ORIGIN_VERIFICATION_MESSAGE_KEY => $originCheck[self::ORIGIN_VERIFICATION_MESSAGE_KEY]
209
            ];
210
        }
211
212 4
        if ($this->isThisBot() !== false) {
213
            return [
214 1
                self::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
215 1
                self::ORIGIN_VERIFICATION_MESSAGE_KEY => 'Request comes from the bot'
216
            ];
217
        }
218
219 3
        return [self::ORIGIN_VERIFICATION_SUCCESS_KEY => true, self::ORIGIN_VERIFICATION_MESSAGE_KEY => 'Yay!'];
220
    }
221
222
    /**
223
     * @return string|null
224
     */
225 8
    public function determineAction()
226
    {
227 8
        $utility = $this->getRequestUtility();
228 8
        $getRequest = $utility->getGet();
229
230 8
        if (!empty($getRequest['action'])) {
231 2
            return strtolower($getRequest['action']);
232
        }
233
234 6
        $request = $utility->getPostedBody();
235
236 6
        if (isset($request['type']) && $request['type'] === 'url_verification') {
237 1
            return 'url_verification';
238
        }
239 5
    }
240
241
    /**
242
     * Return message based on the listener
243
     * If listener is event and event text is empty, fall back to request text.
244
     *
245
     * @throws \Exception
246
     *
247
     * @return mixed|string
248
     */
249 9
    public function getMessage()
250
    {
251 9
        if ($this instanceof EventListener && $this->getEvent() instanceof Event) {
252 1
            $message = $this->getEvent()->getText();
253
254 1
            if (!empty($message)) {
255 1
                return $message;
256
            }
257
        }
258
259 8
        return $this->getRequest('text');
260
    }
261
}
262