Completed
Push — master ( fd8acb...0db6cd )
by Jamie
03:03 queued 59s
created

Eventbrite::makeRequest()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 22
rs 8.6737
cc 5
eloc 12
nc 8
nop 6
1
<?php
2
/**
3
 * @file
4
 * A lightweight wrapper for the Eventbrite API using Guzzle 6. Inspired by
5
 * drewm/mailchimp-api.
6
 */
7
namespace jamiehollern\eventbrite;
8
9
use GuzzleHttp\Client;
10
use GuzzleHttp\Psr7\Request;
11
use GuzzleHttp\Exception\BadResponseException;
12
use Psr\Http\Message\ResponseInterface;
13
use Exception;
14
15
/**
16
 * Class Eventbrite
17
 *
18
 * @package EventbritePHP
19
 * @todo    Add a batch method.
20
 * @todo    Allow the token to be passed as a query string.
21
 */
22
class Eventbrite
23
{
24
    /**
25
     * The current version of this library.
26
     */
27
    const VERSION = '0.1';
28
29
    /**
30
     * An array of valid HTTP verbs.
31
     */
32
    CONST VALID_VERBS = ['get', 'post', 'put', 'patch', 'delete'];
33
34
    /**
35
     * The API endpoint to get the current user's details.
36
     */
37
    const CURRENT_USER_ENDPOINT = 'users/me/';
38
39
    /**
40
     * @var string The Eventbrite OAuth token.
41
     */
42
    private $token;
43
44
    /**
45
     * @var \GuzzleHttp\Client
46
     */
47
    private $client;
48
49
    /**
50
     * @var \Psr\Http\Message\ResponseInterface
51
     */
52
    private $last_response = null;
53
54
    /**
55
     * @param string $token
56
     *   The OAuth token to authenticate the request
57
     * @param array  $config
58
     *   An array of Guzzle config options so that everything is configurable.
59
     *
60
     * @throws \Exception
61
     */
62
    public function __construct($token, $config = [])
63
    {
64
        $default_config = [
65
          'base_uri' => 'https://www.eventbriteapi.com/v3/',
66
          // Turn exceptions off so we can handle the responses ourselves.
67
          'exceptions' => false,
68
          'timeout' => 30,
69
        ];
70
        $config = array_merge($config, $default_config);
71
        // Add this last so it's always there and isn't overwritten.
72
        $config['headers']['User-Agent'] = 'jamiehollern\eventbrite v' . self::VERSION . ' ' . \GuzzleHttp\default_user_agent();
73
        if (!empty($token)) {
74
            $this->token = $token;
75
            // Set the authorisation header.
76
            $config['headers']['Authorization'] = 'Bearer ' . $this->token;
77
            $this->client = new Client($config);
78
        } else {
79
            throw new \Exception('An OAuth token is required to connect to the Eventbrite API.');
80
        }
81
    }
82
83
    /**
84
     * Make the call to Eventbrite, only synchronous calls at present.
85
     *
86
     * @param string $verb
87
     * @param string $endpoint
88
     * @param array  $options
89
     *
90
     * @return array|mixed|\Psr\Http\Message\ResponseInterface
91
     * @throws \Exception
92
     */
93
    public function call($verb, $endpoint, $options = [])
94
    {
95
        if ($this->validMethod($verb)) {
96
            // Get the headers and body from the options.
97
            $headers = isset($options['headers']) ? $options['headers'] : [];
98
            $body = isset($options['body']) ? $options['body'] : [];
99
            $pv = isset($options['protocol_version']) ? $options['protocol_version'] : '1.1';
100
            // Make the request.
101
            $request = new Request($verb, $endpoint, $headers, $body, $pv);
102
            // Send it.
103
            $response = $this->client->send($request, $options);
104
            if ($response instanceof ResponseInterface) {
105
                // Set the last response.
106
                $this->last_response = $response;
107
                // If the caller wants the raw response, give it to them.
108
                if (isset($options['parse_response']) && $options['parse_response'] === false) {
109
                    return $response;
110
                }
111
                $parsed_response = $this->parseResponse($response);
112
                return $parsed_response;
113
            } else {
114
                // This only really happens when the network is interrupted.
115
                throw new BadResponseException('A bad response was received.',
116
                  $request);
117
            }
118
        } else {
119
            throw new \Exception('Unrecognised HTTP verb.');
120
        }
121
    }
122
123
    /**
124
     * A slightly abstracted wrapper around call().
125
     *
126
     * This essentially splits the call options array into different parameters
127
     * to make it more obvious to less advanced users what parameters can be
128
     * passed to the client.
129
     *
130
     * @param       $verb
131
     * @param       $endpoint
132
     * @param null  $params
133
     * @param null  $body
134
     * @param null  $headers
135
     * @param array $options
136
     *
137
     * @return array|mixed|\Psr\Http\Message\ResponseInterface
138
     * @throws \Exception
139
     */
140
    public function makeRequest($verb, $endpoint, $params = null, $body = null, $headers = null, $options = [])
141
    {
142
        // We can only have one body, so overwrite it if it exists.
143
        if ($body !== null) {
144
            $options['body'] = $body;
145
        }
146
        // Merge the mergeable arrays if necessary.
147
        $mergeable = [
148
          'query' => $params,
149
          'headers' => $headers,
150
        ];
151
        foreach ($mergeable as $key => $value) {
152
            if ($value !== null) {
153
                if (!isset($options[$key])) {
154
                    $options[$key] = [];
155
                }
156
                $options[$key] = array_merge($options[$key], $value);
157
            }
158
        }
159
        // Make the call.
160
        return $this->call($verb, $endpoint, $options);
161
    }
162
163
    /**
164
     * Checks if the HTTP method being used is correct.
165
     *
166
     * @param $http_method
167
     *
168
     * @return bool
169
     */
170
    public function validMethod($http_method)
171
    {
172
        if (in_array(strtolower($http_method), self::VALID_VERBS)) {
173
            return true;
174
        }
175
        return false;
176
    }
177
178
    /**
179
     * Parses the response from
180
     *
181
     * @param \Psr\Http\Message\ResponseInterface $response
182
     *
183
     * @return array
184
     */
185
    private function parseResponse(ResponseInterface $response)
186
    {
187
        $body = $response->getBody()->getContents();
188
        return [
189
          'code' => $response->getStatusCode(),
190
          'headers' => $response->getHeaders(),
191
          'body' => ($this->isValidJson($body)) ? json_decode($body,
192
            true) : $body,
193
        ];
194
    }
195
196
    /**
197
     * Checks a string to see if it's JSON. True if it is, false if it's not.
198
     *
199
     * @param string $string
200
     *
201
     * @return bool
202
     */
203
    public function isValidJson($string)
204
    {
205
        if (is_string($string)) {
206
            json_decode($string);
207
            return (json_last_error() === JSON_ERROR_NONE);
208
        }
209
        return false;
210
    }
211
212
    /**
213
     * Checks if the class can connect to the Eventbrite API.
214
     *
215
     * Checks if we can connect to the API by calling the user endpoint and
216
     * checking the response code. If the response code is 2xx it returns true,
217
     * otherwise false.
218
     *
219
     * @return bool
220
     */
221
    public function canConnect()
222
    {
223
        $data = $this->get(self::CURRENT_USER_ENDPOINT);
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<jamiehollern\eventbrite\Eventbrite>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
224
        if (strpos($data['code'], '2') === 0) {
225
            return true;
226
        }
227
        return false;
228
    }
229
230
    /**
231
     * Provides shortcut methods named by HTTP verbs.
232
     *
233
     * Provides shortcut methods for GET, POST, PUT, PATCH and DELETE.
234
     *
235
     * @param string $method
236
     * @param array  $args
237
     *
238
     * @return array|mixed|\Psr\Http\Message\ResponseInterface
239
     * @throws \Exception
240
     */
241
    public function __call($method, $args) {
242
        if ($this->validMethod($method)) {
243
            array_unshift($args, $method);
244
            return call_user_func_array(array($this, 'makeRequest'), $args);
245
        }
246
        else {
247
            throw new \BadMethodCallException('Method not found in class.');
248
        }
249
    }
250
251
    /**
252
     * Returns the last response object for inspection.
253
     *
254
     * @return \Psr\Http\Message\ResponseInterface
255
     */
256
    public function getLastResponse()
257
    {
258
        return $this->last_response;
259
    }
260
261
}
262