Passed
Push — master ( e83711...c51e21 )
by Romain
80:11 queued 41:19
created

Webhook::getBody()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
crap 2
1
<?php
2
namespace Kerox\Messenger\Api;
3
4
use GuzzleHttp\ClientInterface;
5
use GuzzleHttp\Psr7\ServerRequest;
6
use Kerox\Messenger\Model\Callback\Entry;
7
use Kerox\Messenger\Request\WebhookRequest;
8
use Kerox\Messenger\Response\WebhookResponse;
9
use Psr\Http\Message\ServerRequestInterface;
10
11
class Webhook extends AbstractApi
12
{
13
14
    /**
15
     * @var string
16
     */
17
    protected $appSecret;
18
19
    /**
20
     * @var string
21
     */
22
    protected $verifyToken;
23
24
    /**
25
     * @var \Psr\Http\Message\ServerRequestInterface
26
     */
27
    protected $request;
28
29
    /**
30
     * @var string
31
     */
32
    protected $body;
33
34
    /**
35
     * @var array
36
     */
37
    protected $decodedBody;
38
39
    /**
40
     * @var \Kerox\Messenger\Model\Entry[]
41
     */
42
    protected $hydratedEntries;
43
44
    /**
45
     * Webhook constructor.
46
     *
47
     * @param string $appSecret
48
     * @param string $verifyToken
49
     * @param string $pageToken
50
     * @param \GuzzleHttp\ClientInterface $client
51
     * @param \Psr\Http\Message\ServerRequestInterface $request
52
     */
53 7
    public function __construct(string $appSecret, string $verifyToken, string $pageToken, ClientInterface $client, ServerRequestInterface $request = null)
54
    {
55 7
        parent::__construct($pageToken, $client);
56
57 7
        $this->appSecret = $appSecret;
58 7
        $this->verifyToken = $verifyToken;
59 7
        $this->request = $request ?: ServerRequest::fromGlobals();
60 7
    }
61
62
    /**
63
     * @return bool
64
     */
65 1
    public function isValidToken(): bool
66
    {
67 1
        if ($this->request->getMethod() !== 'GET') {
68
            return false;
69
        }
70
71 1
        $params = $this->request->getQueryParams();
72 1
        if (!isset($params['hub_verify_token'])) {
73
            return false;
74
        }
75
76 1
        return ($params['hub_mode'] === 'subscribe' && $params['hub_verify_token'] === $this->verifyToken);
77
    }
78
79
    /**
80
     * @return string|null
81
     */
82 1
    public function getChallenge()
83
    {
84 1
        $params = $this->request->getQueryParams();
85
86 1
        return $params['hub_challenge'] ?? null;
87
    }
88
89
    /**
90
     * @return \Kerox\Messenger\Response\WebhookResponse
91
     */
92 1
    public function sendSubscribe(): WebhookResponse
93
    {
94 1
        $request = new WebhookRequest($this->pageToken);
95 1
        $response = $this->client->post('/me/subscribed_apps', $request->build());
96
97 1
        return new WebhookResponse($response);
98
    }
99
100
    /**
101
     * @return bool
102
     */
103 1
    public function isValidCallback(): bool
104
    {
105 1
        if (!$this->isValidHubSignature()) {
106
            return false;
107
        }
108
109 1
        $decodedBody = $this->getDecodedBody();
110
111 1
        $object = $decodedBody['object'] ?? null;
112 1
        $entry = $decodedBody['entry'] ?? null;
113
114 1
        return ($object === 'page' && $entry !== null);
115
    }
116
117
    /**
118
     * @return string
119
     */
120 4
    public function getBody(): string
121
    {
122 4
        if ($this->body === null) {
123 4
            $this->body = (string)$this->request->getBody();
124
        }
125
126 4
        return $this->body;
127
    }
128
129
    /**
130
     * @return array
131
     * @throws \Exception
132
     */
133 4
    public function getDecodedBody(): array
134
    {
135 4
        if ($this->decodedBody === null) {
136 4
            $decodedBody = json_decode($this->getBody(), true);
137 4
            if (json_last_error() !== JSON_ERROR_NONE || $decodedBody === null) {
138
                $decodedBody = [];
139
            }
140
141 4
            $this->decodedBody = $decodedBody;
142
        }
143
144 4
        return $this->decodedBody;
145
    }
146
147
    /**
148
     * @return \Kerox\Messenger\Model\Entry[]
149
     */
150 1
    public function getCallbackEntries(): array
151
    {
152 1
        return $this->getHydratedEntries();
153
    }
154
155
    /**
156
     * @return array
157
     */
158 1
    public function getCallbackEvents(): array
159
    {
160 1
        $events = [];
161 1
        foreach ($this->getHydratedEntries() as $hydratedEntry) {
162
            /** @var \Kerox\Messenger\Model\Callback\Entry $hydratedEntry */
163 1
            $events = array_merge($events, $hydratedEntry->getEvents());
164
        }
165
166 1
        return $events;
167
    }
168
169
    /**
170
     * @return \Kerox\Messenger\Model\Callback\Entry[]
171
     */
172 2
    private function getHydratedEntries(): array
173
    {
174 2
        if ($this->hydratedEntries === null) {
175 2
            $decodedBody = $this->getDecodedBody();
176
177 2
            $hydrated = [];
178 2
            foreach ($decodedBody['entry'] as $entry) {
179 2
                $hydrated[] = Entry::create($entry);
180
            }
181
182 2
            $this->hydratedEntries = $hydrated;
183
        }
184
185 2
        return $this->hydratedEntries;
186
    }
187
188
    /**
189
     * @return bool
190
     */
191 1
    private function isValidHubSignature(): bool
192
    {
193 1
        $headers = $this->request->getHeader('X-Hub-Signature');
194 1
        $content = $this->getBody();
195
196 1
        if (empty($headers)) {
197
            return false;
198
        }
199
200 1
        list($algorithm, $hash) = explode('=', $headers[0]);
201
202 1
        return hash_equals(hash_hmac($algorithm, $content, $this->appSecret), $hash);
203
    }
204
}
205