Completed
Push — master ( b1a854...18a9c1 )
by Drew
01:17
created

Drip::makeRequest()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 9.536
c 0
b 0
f 0
cc 4
nc 4
nop 5
1
<?php
2
3
namespace DrewM\Drip;
4
5
class Drip
6
{
7
    protected static $eventSubscriptions = [];
8
    protected static $receivedWebhook    = false;
9
    protected        $api_endpoint       = 'https://api.getdrip.com/v2';
10
    protected        $token              = false;
11
    protected        $accountID          = null;
12
    protected        $verify_ssl         = true;
13
14
    /**
15
     * Drip constructor.
16
     *
17
     * @param string      $token     API Token
18
     * @param string|null $accountID Drip account ID to operate on
19
     */
20
    public function __construct($token, $accountID = null)
21
    {
22
        $this->token = $token;
0 ignored issues
show
Documentation Bug introduced by
The property $token was declared of type boolean, but $token is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
23
24
        if ($accountID !== null) {
25
            $this->accountID = $accountID;
26
        }
27
    }
28
29
    public static function subscribeToWebhook($event, callable $callback)
30
    {
31
        if (!isset(self::$eventSubscriptions[$event])) {
32
            self::$eventSubscriptions[$event] = [];
33
        }
34
        self::$eventSubscriptions[$event][] = $callback;
35
36
        self::receiveWebhook();
37
    }
38
39
    public static function receiveWebhook($input = null)
40
    {
41
        if ($input === null) {
42
            if (self::$receivedWebhook !== false) {
43
                $input = self::$receivedWebhook;
44
            } else {
45
                $input = file_get_contents("php://input");
46
            }
47
        }
48
49
        if ($input) {
50
            return self::processWebhook($input);
51
        }
52
53
        return false;
54
    }
55
56
    protected static function processWebhook($input)
57
    {
58
        if ($input) {
59
            self::$receivedWebhook = $input;
60
            $result                = json_decode($input, true);
61
            if ($result && isset($result['event'])) {
62
                self::dispatchWebhookEvent($result['event'], $result['data']);
63
                return $result;
64
            }
65
        }
66
67
        return false;
68
    }
69
70
    protected static function dispatchWebhookEvent($event, $data)
71
    {
72
        if (isset(self::$eventSubscriptions[$event])) {
73
            foreach (self::$eventSubscriptions[$event] as $callback) {
74
                $callback($data);
75
            }
76
            // reset subscriptions
77
            self::$eventSubscriptions[$event] = [];
78
        }
79
        return false;
80
    }
81
82
    /**
83
     * Set account ID if it was not passed into the constructor
84
     *
85
     * @param string $accountID
86
     *
87
     * @return void
88
     */
89
    public function setAccountId($accountID)
90
    {
91
        $this->accountID = $accountID;
92
    }
93
94
    /**
95
     * Make a GET request
96
     *
97
     * @param string $api_method API method to call
98
     * @param array  $args       API arguments
99
     * @param int    $timeout    Connection timeout (seconds)
100
     *
101
     * @return Response
102
     * @throws DripException
103
     */
104
    public function get($api_method, $args = [], $timeout = 10)
105
    {
106
        return $this->makeRequest('get', $api_method, $args, $timeout);
107
    }
108
109
    /**
110
     * Make the HTTP request
111
     *
112
     * @param string $http_verb  HTTP method used: get, post, delete
113
     * @param string $api_method Drip API method to call
114
     * @param array  $args       Array of arguments to the API method
115
     * @param int    $timeout    Connection timeout (seconds)
116
     * @param string $url        Optional URL to override the constructed one
117
     *
118
     * @return Response
119
     * @throws DripException
120
     */
121
    protected function makeRequest($http_verb, $api_method, $args = [], $timeout = 10, $url = null)
122
    {
123
        $this->checkDependencies();
124
125
        $url = $this->constructRequestUrl($url, $api_method);
126
        $ch  = $this->createCurlSession($url, $timeout);
127
128
        switch ($http_verb) {
129
            case 'post':
130
                curl_setopt($ch, CURLOPT_POST, 1);
131
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($args));
132
                break;
133
134
            case 'get':
135
                curl_setopt($ch, CURLOPT_URL, $url . '?' . http_build_query($args));
136
                break;
137
138
            case 'delete':
139
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
140
                break;
141
        }
142
143
        return $this->executeRequest($ch);
0 ignored issues
show
Security Bug introduced by
It seems like $ch defined by $this->createCurlSession($url, $timeout) on line 126 can also be of type false; however, DrewM\Drip\Drip::executeRequest() does only seem to accept resource, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
144
    }
145
146
    /**
147
     * Check for required PHP functionality
148
     *
149
     * @return bool
150
     * @throws DripException
151
     */
152
    private function checkDependencies()
153
    {
154
        if (!function_exists('curl_init') || !function_exists('curl_setopt')) {
155
            throw new DripException("cURL support is required, but can't be found.", 1);
156
        }
157
158
        return true;
159
    }
160
161
    /**
162
     * @param string|null $url
163
     * @param string      $api_method
164
     *
165
     * @return string
166
     * @throws DripException
167
     */
168
    private function constructRequestUrl($url, $api_method)
169
    {
170
        if ($url !== null) {
171
            return $url;
172
        }
173
174
        if ($this->accountID === null) {
175
            throw new DripException("This method requires an account ID and none has been set.", 2);
176
        }
177
178
        return $this->api_endpoint . '/' . $this->accountID . '/' . $api_method;
179
    }
180
181
    /**
182
     * Create a new CURL session (common setup etc)
183
     *
184
     * @param string $url
185
     * @param int    $timeout
186
     *
187
     * @return false|resource
188
     */
189
    private function createCurlSession($url, $timeout = 10)
190
    {
191
        $ch = curl_init();
192
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
193
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
194
            'Accept: application/vnd.api+json',
195
            'Content-Type: application/vnd.api+json',
196
        ]);
197
        curl_setopt($ch, CURLOPT_USERAGENT, 'DrewM/Drip (github.com/drewm/drip)');
198
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
199
        curl_setopt($ch, CURLOPT_USERPWD, $this->token . ': ');
200
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl);
201
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
202
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
203
        curl_setopt($ch, CURLOPT_URL, $url);
204
205
        return $ch;
206
    }
207
208
    /**
209
     * Execute and handle the request result
210
     *
211
     * @param resource $ch Curl handle
212
     *
213
     * @return Response
214
     * @throws DripException
215
     */
216
    private function executeRequest(&$ch)
217
    {
218
        $result = curl_exec($ch);
219
220
        if (!curl_errno($ch)) {
221
            $info = curl_getinfo($ch);
222
            curl_close($ch);
223
            return new Response($info, $result);
224
        }
225
226
        $errno = curl_errno($ch);
227
        $error = curl_error($ch);
228
229
        curl_close($ch);
230
231
        throw new DripException($error, $errno);
232
    }
233
234
    /**
235
     * Make a GET request to a top-level method outside of this account
236
     *
237
     * @param string $api_method
238
     * @param array  $args
239
     * @param int    $timeout
240
     *
241
     * @return Response
242
     * @throws DripException
243
     */
244
    public function getGlobal($api_method, $args = [], $timeout = 10)
245
    {
246
        $url = $this->api_endpoint . '/' . $api_method;
247
        return $this->makeRequest('get', $api_method, $args, $timeout, $url);
248
    }
249
250
    /**
251
     * Make a POST request
252
     *
253
     * @param string $api_method API method
254
     * @param array  $args       Arguments to API method
255
     * @param int    $timeout    Connection timeout (seconds)
256
     *
257
     * @return Response
258
     * @throws DripException
259
     */
260
    public function post($api_method, $args = [], $timeout = 10)
261
    {
262
        return $this->makeRequest('post', $api_method, $args, $timeout);
263
    }
264
265
    /**
266
     * Make a DELETE request
267
     *
268
     * @param string $api_method API method
269
     * @param array  $args       Arguments to the API method
270
     * @param int    $timeout    Connection timeout (seconds)
271
     *
272
     * @return Response
273
     * @throws DripException
274
     */
275
    public function delete($api_method, $args = [], $timeout = 10)
276
    {
277
        return $this->makeRequest('delete', $api_method, $args, $timeout);
278
    }
279
280
    public function disableSSLVerification()
281
    {
282
        $this->verify_ssl = false;
283
    }
284
}
285