Completed
Push — 1.1 ( 803478...46316a )
by David
02:34
created
lib/GitHub/WebHook.php 1 patch
Indentation   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -12,24 +12,24 @@
 block discarded – undo
12 12
 class WebHook extends AbstractApi
13 13
 {
14 14
 
15
-    /** Constants */
16
-    const PAYLOAD = 'Payload';
15
+	/** Constants */
16
+	const PAYLOAD = 'Payload';
17 17
 
18
-    /**
19
-     * Returns Event object
20
-     *
21
-     * @param string $event
22
-     *
23
-     * @return null|EventInterface
24
-     */
25
-    public function getEvent(string $event)
26
-    {
27
-        $class = (string)$this->sprintf(':namespace\Event\:event', __NAMESPACE__, $event);
18
+	/**
19
+	 * Returns Event object
20
+	 *
21
+	 * @param string $event
22
+	 *
23
+	 * @return null|EventInterface
24
+	 */
25
+	public function getEvent(string $event)
26
+	{
27
+		$class = (string)$this->sprintf(':namespace\Event\:event', __NAMESPACE__, $event);
28 28
 
29
-        if (class_exists($class)) {
30
-            return new $class($this);
31
-        }
29
+		if (class_exists($class)) {
30
+			return new $class($this);
31
+		}
32 32
 
33
-        return null;
34
-    }
33
+		return null;
34
+	}
35 35
 } 
36 36
\ No newline at end of file
Please login to merge, or discard this patch.
lib/GitHub/Event/EventInterface.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -11,17 +11,17 @@
 block discarded – undo
11 11
 interface EventInterface
12 12
 {
13 13
 
14
-    /**
15
-     * Constructor, pass a WebHook object
16
-     *
17
-     * @param WebHook $webHook
18
-     */
19
-    public function __construct(WebHook $webHook);
14
+	/**
15
+	 * Constructor, pass a WebHook object
16
+	 *
17
+	 * @param WebHook $webHook
18
+	 */
19
+	public function __construct(WebHook $webHook);
20 20
 
21
-    /**
22
-     * Parse raw data
23
-     *
24
-     * @return Payload
25
-     */
26
-    public function parse(): Payload;
21
+	/**
22
+	 * Parse raw data
23
+	 *
24
+	 * @return Payload
25
+	 */
26
+	public function parse(): Payload;
27 27
 }
28 28
\ No newline at end of file
Please login to merge, or discard this patch.
lib/GitHub/Event/Payload.php 1 patch
Indentation   +186 added lines, -186 removed lines patch added patch discarded remove patch
@@ -13,190 +13,190 @@
 block discarded – undo
13 13
 class Payload implements EventInterface
14 14
 {
15 15
 
16
-    /** Protected properties */
17
-    protected $webHook;
18
-    protected $secret = null;
19
-    protected $rawData;
20
-    protected $parsedData;
21
-
22
-    /**
23
-     * Constructor, pass a WebHook object
24
-     *
25
-     * @param WebHook $webHook
26
-     */
27
-    public function __construct(WebHook $webHook)
28
-    {
29
-        $this->setWebHook($webHook);
30
-        $this->setRawData($webHook->getRequest()->getContent());
31
-    }
32
-
33
-    /**
34
-     * Get webHook
35
-     *
36
-     * @return null|WebHook
37
-     */
38
-    public function getWebHook()
39
-    {
40
-        return $this->webHook;
41
-    }
42
-
43
-    /**
44
-     * Set webHook
45
-     *
46
-     * @param mixed $webHook
47
-     *
48
-     * @return Payload
49
-     */
50
-    public function setWebHook($webHook): Payload
51
-    {
52
-        $this->webHook = $webHook;
53
-
54
-        return $this;
55
-    }
56
-
57
-    /**
58
-     * Set secret, encode this secret with Hmac, SHA1 method
59
-     *
60
-     * @param string $secret
61
-     *
62
-     * @return Payload
63
-     */
64
-    public function setSecret(string $secret): Payload
65
-    {
66
-        $this->secret = hash_hmac('sha1', $this->rawData, $secret);
67
-
68
-        return $this;
69
-    }
70
-
71
-    /**
72
-     * Get secret
73
-     *
74
-     * @return null|string
75
-     */
76
-    public function getSecret()
77
-    {
78
-        return $this->secret;
79
-    }
80
-
81
-    /**
82
-     * Get rawData
83
-     *
84
-     * @return resource|string
85
-     */
86
-    public function getRawData()
87
-    {
88
-        return $this->rawData;
89
-    }
90
-
91
-    /**
92
-     * Set rawData
93
-     *
94
-     * @param resource|string $rawData
95
-     *
96
-     * @return Payload
97
-     */
98
-    public function setRawData($rawData): Payload
99
-    {
100
-        $this->rawData = $rawData;
101
-
102
-        return $this;
103
-    }
104
-
105
-    /**
106
-     * Get parsedData
107
-     *
108
-     * @return mixed
109
-     */
110
-    public function getData()
111
-    {
112
-        return $this->parsedData;
113
-    }
114
-
115
-    /**
116
-     * Set parsedData
117
-     *
118
-     * @param mixed $parsedData
119
-     *
120
-     * @return Payload
121
-     */
122
-    protected function setParsedData($parsedData): Payload
123
-    {
124
-        $data = json_decode($parsedData);
125
-        if (JSON_ERROR_NONE === json_last_error()) {
126
-            $this->parsedData = $data;
127
-        }
128
-
129
-        return $this;
130
-    }
131
-
132
-    /**
133
-     * Debugger
134
-     *
135
-     * @return array
136
-     */
137
-    public function __debugInfo(): array
138
-    {
139
-        return [
140
-            'ramData'     => (array)$this->getRawData(),
141
-            'jsonEncoded' => json_decode($this->getRawData())
142
-        ];
143
-    }
144
-
145
-    /**
146
-     * Parse raw data
147
-     *
148
-     * @return Payload
149
-     * @throws BadSignatureException
150
-     * @throws \Exception
151
-     */
152
-    public function parse(): Payload
153
-    {
154
-        /** Check signature from header */
155
-        if (!$this->_checkSignature()) {
156
-            throw new BadSignatureException('Hook secret does not match.');
157
-        }
158
-
159
-        /** Get data from different locations according to content-type */
160
-        switch ($_SERVER['CONTENT_TYPE']) {
161
-            case 'application/json':
162
-                $data = $this->getRawData();
163
-                break;
164
-
165
-            case 'application/x-www-form-urlencoded':
166
-                $data = $_POST['payload'];
167
-                break;
168
-
169
-            default:
170
-                throw new \Exception('Unsupported content type: "' . $_SERVER['CONTENT_TYPE'] . '"');
171
-        }
172
-        $this->setParsedData($data);
173
-
174
-        return $this;
175
-    }
176
-
177
-    /**
178
-     * Check X-Hub-Signature
179
-     *
180
-     * @throws BadSignatureException
181
-     * @return bool
182
-     */
183
-    private function _checkSignature(): bool
184
-    {
185
-        if (null !== $this->secret) {
186
-            if ($this->getWebHook()->getRequest()->server->get('HTTP_X_HUB_SIGNATURE')) {
187
-                /**
188
-                 * Split signature into algorithm and hash
189
-                 *
190
-                 * @link http://isometriks.com/verify-github-webhooks-with-php
191
-                 */
192
-                list(, $hash) = explode('=', $this->getWebHook()->getRequest()->server->get('HTTP_X_HUB_SIGNATURE'), 2);
193
-
194
-                return $this->secret == $hash;
195
-            }
196
-
197
-            throw new BadSignatureException('HTTP header "X-Hub-Signature" is missing.');
198
-        }
199
-
200
-        return true;
201
-    }
16
+	/** Protected properties */
17
+	protected $webHook;
18
+	protected $secret = null;
19
+	protected $rawData;
20
+	protected $parsedData;
21
+
22
+	/**
23
+	 * Constructor, pass a WebHook object
24
+	 *
25
+	 * @param WebHook $webHook
26
+	 */
27
+	public function __construct(WebHook $webHook)
28
+	{
29
+		$this->setWebHook($webHook);
30
+		$this->setRawData($webHook->getRequest()->getContent());
31
+	}
32
+
33
+	/**
34
+	 * Get webHook
35
+	 *
36
+	 * @return null|WebHook
37
+	 */
38
+	public function getWebHook()
39
+	{
40
+		return $this->webHook;
41
+	}
42
+
43
+	/**
44
+	 * Set webHook
45
+	 *
46
+	 * @param mixed $webHook
47
+	 *
48
+	 * @return Payload
49
+	 */
50
+	public function setWebHook($webHook): Payload
51
+	{
52
+		$this->webHook = $webHook;
53
+
54
+		return $this;
55
+	}
56
+
57
+	/**
58
+	 * Set secret, encode this secret with Hmac, SHA1 method
59
+	 *
60
+	 * @param string $secret
61
+	 *
62
+	 * @return Payload
63
+	 */
64
+	public function setSecret(string $secret): Payload
65
+	{
66
+		$this->secret = hash_hmac('sha1', $this->rawData, $secret);
67
+
68
+		return $this;
69
+	}
70
+
71
+	/**
72
+	 * Get secret
73
+	 *
74
+	 * @return null|string
75
+	 */
76
+	public function getSecret()
77
+	{
78
+		return $this->secret;
79
+	}
80
+
81
+	/**
82
+	 * Get rawData
83
+	 *
84
+	 * @return resource|string
85
+	 */
86
+	public function getRawData()
87
+	{
88
+		return $this->rawData;
89
+	}
90
+
91
+	/**
92
+	 * Set rawData
93
+	 *
94
+	 * @param resource|string $rawData
95
+	 *
96
+	 * @return Payload
97
+	 */
98
+	public function setRawData($rawData): Payload
99
+	{
100
+		$this->rawData = $rawData;
101
+
102
+		return $this;
103
+	}
104
+
105
+	/**
106
+	 * Get parsedData
107
+	 *
108
+	 * @return mixed
109
+	 */
110
+	public function getData()
111
+	{
112
+		return $this->parsedData;
113
+	}
114
+
115
+	/**
116
+	 * Set parsedData
117
+	 *
118
+	 * @param mixed $parsedData
119
+	 *
120
+	 * @return Payload
121
+	 */
122
+	protected function setParsedData($parsedData): Payload
123
+	{
124
+		$data = json_decode($parsedData);
125
+		if (JSON_ERROR_NONE === json_last_error()) {
126
+			$this->parsedData = $data;
127
+		}
128
+
129
+		return $this;
130
+	}
131
+
132
+	/**
133
+	 * Debugger
134
+	 *
135
+	 * @return array
136
+	 */
137
+	public function __debugInfo(): array
138
+	{
139
+		return [
140
+			'ramData'     => (array)$this->getRawData(),
141
+			'jsonEncoded' => json_decode($this->getRawData())
142
+		];
143
+	}
144
+
145
+	/**
146
+	 * Parse raw data
147
+	 *
148
+	 * @return Payload
149
+	 * @throws BadSignatureException
150
+	 * @throws \Exception
151
+	 */
152
+	public function parse(): Payload
153
+	{
154
+		/** Check signature from header */
155
+		if (!$this->_checkSignature()) {
156
+			throw new BadSignatureException('Hook secret does not match.');
157
+		}
158
+
159
+		/** Get data from different locations according to content-type */
160
+		switch ($_SERVER['CONTENT_TYPE']) {
161
+			case 'application/json':
162
+				$data = $this->getRawData();
163
+				break;
164
+
165
+			case 'application/x-www-form-urlencoded':
166
+				$data = $_POST['payload'];
167
+				break;
168
+
169
+			default:
170
+				throw new \Exception('Unsupported content type: "' . $_SERVER['CONTENT_TYPE'] . '"');
171
+		}
172
+		$this->setParsedData($data);
173
+
174
+		return $this;
175
+	}
176
+
177
+	/**
178
+	 * Check X-Hub-Signature
179
+	 *
180
+	 * @throws BadSignatureException
181
+	 * @return bool
182
+	 */
183
+	private function _checkSignature(): bool
184
+	{
185
+		if (null !== $this->secret) {
186
+			if ($this->getWebHook()->getRequest()->server->get('HTTP_X_HUB_SIGNATURE')) {
187
+				/**
188
+				 * Split signature into algorithm and hash
189
+				 *
190
+				 * @link http://isometriks.com/verify-github-webhooks-with-php
191
+				 */
192
+				list(, $hash) = explode('=', $this->getWebHook()->getRequest()->server->get('HTTP_X_HUB_SIGNATURE'), 2);
193
+
194
+				return $this->secret == $hash;
195
+			}
196
+
197
+			throw new BadSignatureException('HTTP header "X-Hub-Signature" is missing.');
198
+		}
199
+
200
+		return true;
201
+	}
202 202
 }
203 203
\ No newline at end of file
Please login to merge, or discard this patch.
lib/GitHub/AbstractApi.php 1 patch
Indentation   +568 added lines, -568 removed lines patch added patch discarded remove patch
@@ -8,572 +8,572 @@
 block discarded – undo
8 8
 abstract class AbstractApi
9 9
 {
10 10
 
11
-    /** API version */
12
-    const API_VERSION = 'v3';
13
-
14
-    /** API constants */
15
-    const API_URL        = 'https://api.github.com';
16
-    const API_UPLOADS    = 'https://uploads.github.com';
17
-    const API_RAW_URL    = 'https://raw.github.com';
18
-    const CONTENT_TYPE   = 'application/json';
19
-    const DEFAULT_ACCEPT = 'application/vnd.github.' . self::API_VERSION . '+json';
20
-    const USER_AGENT     = 'scion.github-api';
21
-
22
-    /** Archive constants */
23
-    const ARCHIVE_TARBALL = 'tarball';
24
-    const ARCHIVE_ZIPBALL = 'zipball';
25
-
26
-    /** Authentication constants */
27
-    const OAUTH_AUTH             = 0;
28
-    const OAUTH2_HEADER_AUTH     = 1;
29
-    const OAUTH2_PARAMETERS_AUTH = 2;
30
-
31
-    /** Branch constants */
32
-    const BRANCH_MASTER  = 'master';
33
-    const BRANCH_DEVELOP = 'develop';
34
-
35
-    /** Direction constants */
36
-    const DIRECTION_ASC  = 'asc';
37
-    const DIRECTION_DESC = 'desc';
38
-
39
-    /** Environment constants */
40
-    const ENVIRONMENT_PRODUCTION = 'production';
41
-    const ENVIRONMENT_STAGING    = 'staging';
42
-    const ENVIRONMENT_QA         = 'qa';
43
-
44
-    /** Events constants */
45
-    const EVENTS_PULL         = 'pull';
46
-    const EVENTS_PULL_REQUEST = 'pull_request';
47
-    const EVENTS_PUSH         = 'push';
48
-
49
-    /** Filter constants */
50
-    const FILTER_ALL        = 'all';
51
-    const FILTER_ASSIGNED   = 'assigned';
52
-    const FILTER_CREATED    = 'created';
53
-    const FILTER_MENTIONED  = 'mentioned';
54
-    const FILTER_SUBSCRIBED = 'subscribed';
55
-
56
-    /** Media types constants */
57
-    const MEDIA_TYPE_JSON = 'json';
58
-    const MEDIA_TYPE_RAW  = 'raw';
59
-    const MEDIA_TYPE_FULL = 'full';
60
-    const MEDIA_TYPE_TEXT = 'text';
61
-
62
-    /** Modes constants */
63
-    const MODE_MARKDOWN = 'markdown';
64
-    const MODE_GFM      = 'gfm';
65
-
66
-    /** Permissions constants */
67
-    const PERMISSION_ADMIN = 'admin';
68
-    const PERMISSION_PULL  = 'pull';
69
-    const PERMISSION_PUSH  = 'push';
70
-
71
-    /** Sort constants */
72
-    const SORT_COMPLETENESS = 'completeness';
73
-    const SORT_CREATED      = 'created';
74
-    const SORT_DUE_DATE     = 'due_date';
75
-    const SORT_FULL_NAME    = 'full_name';
76
-    const SORT_NEWEST       = 'newest';
77
-    const SORT_OLDEST       = 'oldest';
78
-    const SORT_PUSHED       = 'pushed';
79
-    const SORT_STARGAZERS   = 'stargazers';
80
-    const SORT_UPDATED      = 'updated';
81
-
82
-    /** State constants */
83
-    const STATE_ACTIVE  = 'active';
84
-    const STATE_ALL     = 'all';
85
-    const STATE_CLOSED  = 'closed';
86
-    const STATE_ERROR   = 'error';
87
-    const STATE_FAILURE = 'failure';
88
-    const STATE_OPEN    = 'open';
89
-    const STATE_PENDING = 'pending';
90
-    const STATE_SUCCESS = 'success';
91
-
92
-    /** Task constants */
93
-    const TASK_DEPLOY            = 'deploy';
94
-    const TASK_DEPLOY_MIGRATIONS = 'deploy:migrations';
95
-
96
-    /** Type constants */
97
-    const TYPE_ALL        = 'all';
98
-    const TYPE_COMMENTS   = 'comments';
99
-    const TYPE_GISTS      = 'gists';
100
-    const TYPE_HOOKS      = 'hooks';
101
-    const TYPE_ISSUES     = 'issues';
102
-    const TYPE_MEMBER     = 'member';
103
-    const TYPE_MILESTONES = 'milestones';
104
-    const TYPE_ORGS       = 'orgs';
105
-    const TYPE_OWNER      = 'owner';
106
-    const TYPE_PAGES      = 'pages';
107
-    const TYPE_PUBLIC     = 'public';
108
-    const TYPE_PULLS      = 'pulls';
109
-    const TYPE_PRIVATE    = 'private';
110
-    const TYPE_REPOS      = 'repos';
111
-    const TYPE_USERS      = 'users';
112
-
113
-    /** Properties */
114
-    protected $accept         = self::DEFAULT_ACCEPT;
115
-    protected $apiUrl         = self::API_URL;
116
-    protected $authentication = self::OAUTH_AUTH;
117
-    protected $clientId;
118
-    protected $clientSecret;
119
-    protected $contentType    = self::CONTENT_TYPE;
120
-    protected $failure;
121
-    protected $headers        = [];
122
-    protected $httpAuth       = ['username' => '', 'password' => ''];
123
-    protected $success;
124
-    protected $timeout        = 240;
125
-    protected $token          = '';
126
-    protected $request;
127
-
128
-    /**
129
-     * Constructor
130
-     */
131
-    public function __construct()
132
-    {
133
-        $this->request = Request::createFromGlobals();
134
-    }
135
-
136
-    /**
137
-     * Get request
138
-     *
139
-     * @return Request
140
-     */
141
-    public function getRequest(): Request
142
-    {
143
-        return $this->request;
144
-    }
145
-
146
-    /**
147
-     * Get accept
148
-     *
149
-     * @return mixed
150
-     */
151
-    public function getAccept()
152
-    {
153
-        return $this->accept;
154
-    }
155
-
156
-    /**
157
-     * Set accept
158
-     *
159
-     * @param array|string $accept
160
-     *
161
-     * @return AbstractApi
162
-     */
163
-    public function setAccept($accept): AbstractApi
164
-    {
165
-        $this->accept = $accept;
166
-
167
-        return $this;
168
-    }
169
-
170
-    /**
171
-     * Get authentication
172
-     *
173
-     * @return int
174
-     */
175
-    public function getAuthentication(): int
176
-    {
177
-        return $this->authentication;
178
-    }
179
-
180
-    /**
181
-     * Set authentication
182
-     *
183
-     * @param int $authentication
184
-     *
185
-     * @return AbstractApi
186
-     */
187
-    public function setAuthentication(int $authentication): AbstractApi
188
-    {
189
-        $this->authentication = $authentication;
190
-
191
-        return $this;
192
-    }
193
-
194
-    /**
195
-     * Get apiUrl
196
-     *
197
-     * @return string
198
-     */
199
-    public function getApiUrl(): string
200
-    {
201
-        return $this->apiUrl;
202
-    }
203
-
204
-    /**
205
-     * Set apiUrl
206
-     *
207
-     * @param mixed $apiUrl
208
-     *
209
-     * @return AbstractApi
210
-     */
211
-    public function setApiUrl($apiUrl): AbstractApi
212
-    {
213
-        $this->apiUrl = $apiUrl;
214
-
215
-        return $this;
216
-    }
217
-
218
-    /**
219
-     * Get clientId
220
-     *
221
-     * @return null|int
222
-     */
223
-    public function getClientId()
224
-    {
225
-        return $this->clientId;
226
-    }
227
-
228
-    /**
229
-     * Set clientId
230
-     *
231
-     * @param mixed $clientId
232
-     *
233
-     * @return AbstractApi
234
-     */
235
-    public function setClientId($clientId): AbstractApi
236
-    {
237
-        $this->clientId = $clientId;
238
-
239
-        return $this;
240
-    }
241
-
242
-    /**
243
-     * Get clientSecret
244
-     *
245
-     * @return null|string
246
-     */
247
-    public function getClientSecret()
248
-    {
249
-        return $this->clientSecret;
250
-    }
251
-
252
-    /**
253
-     * Set clientSecret
254
-     *
255
-     * @param mixed $clientSecret
256
-     *
257
-     * @return AbstractApi
258
-     */
259
-    public function setClientSecret($clientSecret): AbstractApi
260
-    {
261
-        $this->clientSecret = $clientSecret;
262
-
263
-        return $this;
264
-    }
265
-
266
-    /**
267
-     * Get httpAuth
268
-     *
269
-     * @return array
270
-     */
271
-    public function getHttpAuth(): array
272
-    {
273
-        return $this->httpAuth;
274
-    }
275
-
276
-    /**
277
-     * Set httpAuth
278
-     *
279
-     * @param string $username
280
-     * @param string $password
281
-     *
282
-     * @return AbstractApi
283
-     */
284
-    public function setHttpAuth(string $username, string $password = ''): AbstractApi
285
-    {
286
-        $this->httpAuth['username'] = $username;
287
-        $this->httpAuth['password'] = $password;
288
-
289
-        return $this;
290
-    }
291
-
292
-    /**
293
-     * Get token
294
-     *
295
-     * @return string
296
-     */
297
-    public function getToken(): string
298
-    {
299
-        return $this->token;
300
-    }
301
-
302
-    /**
303
-     * Set token
304
-     *
305
-     * @param string $token
306
-     * @param int    $authentication
307
-     *
308
-     * @return AbstractApi
309
-     */
310
-    public function setToken(string $token, int $authentication = self::OAUTH_AUTH): AbstractApi
311
-    {
312
-        $this->token = $token;
313
-        $this->setAuthentication($authentication);
314
-
315
-        return $this;
316
-    }
317
-
318
-    /**
319
-     * Get timeout
320
-     *
321
-     * @return int
322
-     */
323
-    public function getTimeout(): int
324
-    {
325
-        return $this->timeout;
326
-    }
327
-
328
-    /**
329
-     * Set timeout
330
-     *
331
-     * @param int $timeout
332
-     *
333
-     * @return AbstractApi
334
-     */
335
-    public function setTimeout(int $timeout): AbstractApi
336
-    {
337
-        $this->timeout = $timeout;
338
-
339
-        return $this;
340
-    }
341
-
342
-    /**
343
-     * Get contentType
344
-     *
345
-     * @return string
346
-     */
347
-    public function getContentType(): string
348
-    {
349
-        return $this->contentType;
350
-    }
351
-
352
-    /**
353
-     * Set contentType
354
-     *
355
-     * @param string $contentType
356
-     *
357
-     * @return AbstractApi
358
-     */
359
-    public function setContentType(string $contentType): AbstractApi
360
-    {
361
-        $this->contentType = $contentType;
362
-
363
-        return $this;
364
-    }
365
-
366
-    /**
367
-     * Get headers
368
-     *
369
-     * @return array
370
-     */
371
-    public function getHeaders(): array
372
-    {
373
-        return $this->headers;
374
-    }
375
-
376
-    /**
377
-     * Curl request
378
-     *
379
-     * @param string      $url
380
-     * @param string      $method
381
-     * @param array       $postFields
382
-     * @param null|string $apiUrl
383
-     *
384
-     * @return array
385
-     */
386
-    public function request(string $url, string $method = Request::METHOD_GET, array $postFields = [],
387
-                            string $apiUrl = null): array
388
-    {
389
-        /** Building url */
390
-        if (null === $apiUrl) {
391
-            $apiUrl = $this->getApiUrl();
392
-        }
393
-        $url = $apiUrl . $url;
394
-
395
-        /**
396
-         * OAuth2 Key/Secret authentication
397
-         *
398
-         * @see https://developer.github.com/v3/#oauth2-keysecret
399
-         */
400
-        if (null !== $this->getClientId() && null !== $this->getClientSecret()) {
401
-            $url .= (strstr($url, '?') !== false ? '&' : '?');
402
-            $url .= http_build_query(['client_id'     => $this->getClientId(),
403
-                                      'client_secret' => $this->getClientSecret()
404
-            ]);
405
-        } /**
406
-         * Basic authentication via OAuth2 Token (sent as a parameter)
407
-         *
408
-         * @see https://developer.github.com/v3/#oauth2-token-sent-as-a-parameter
409
-         */ else if ($this->getAuthentication() === self::OAUTH2_PARAMETERS_AUTH) {
410
-            $url .= http_build_query(['access_token' => $this->getToken()]);
411
-        }
412
-
413
-        /** Call curl */
414
-        $curl = new CurlClient();
415
-        $curl->setOption([
416
-            CURLOPT_USERAGENT      => self::USER_AGENT,
417
-            CURLOPT_TIMEOUT        => $this->getTimeout(),
418
-            CURLOPT_HEADER         => false, // Use $client->getHeaders() to get full header
419
-            CURLOPT_FOLLOWLOCATION => true,
420
-            CURLOPT_HTTPHEADER     => [
421
-                'Accept: ' . $this->getAccept(),
422
-                'Content-Type: ' . $this->getContentType()
423
-            ],
424
-            CURLOPT_URL            => $url
425
-        ]);
426
-
427
-        /**
428
-         * Basic authentication via username and Password
429
-         *
430
-         * @see https://developer.github.com/v3/auth/#via-username-and-password
431
-         */
432
-        if (!empty($this->getHttpAuth())) {
433
-            if (!isset($this->getHttpAuth()['password']) || empty($this->getHttpAuth()['password'])) {
434
-                $curl->setOption([
435
-                    CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
436
-                    CURLOPT_USERPWD  => $this->getHttpAuth()['username']
437
-                ]);
438
-            } else {
439
-                $curl->setOption([
440
-                    CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
441
-                    CURLOPT_USERPWD  => sprintf('%s:%s', $this->getHttpAuth()['username'],
442
-                        $this->getHttpAuth()['password'])
443
-                ]);
444
-            }
445
-        }
446
-
447
-        if (!empty($this->getToken()) && $this->getAuthentication() !== self::OAUTH2_PARAMETERS_AUTH) {
448
-            /**
449
-             * Basic authentication via OAuth token
450
-             *
451
-             * @see https://developer.github.com/v3/auth/#via-oauth-tokens
452
-             **/
453
-            if ($this->getAuthentication() === self::OAUTH_AUTH) {
454
-                $curl->setOption([
455
-                    CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
456
-                    CURLOPT_USERPWD  => sprintf('%s:x-oauth-basic', $this->getToken())
457
-                ]);
458
-            } /**
459
-             * Basic authentication via OAuth2 Token (sent in a header)
460
-             *
461
-             * @see https://developer.github.com/v3/#oauth2-token-sent-in-a-header
462
-             */ else if ($this->getAuthentication() === self::OAUTH2_HEADER_AUTH) {
463
-                $curl->setOption([
464
-                    CURLOPT_HTTPAUTH   => CURLAUTH_BASIC,
465
-                    CURLOPT_HTTPHEADER => [sprintf('Authorization: token %s', $this->getToken())]
466
-                ]);
467
-            }
468
-        }
469
-
470
-        /** Methods */
471
-        switch ($method) {
472
-            /** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7 */
473
-            case Request::METHOD_DELETE:
474
-                /** @see http://tools.ietf.org/html/rfc5789 */
475
-            case Request::METHOD_PATCH:
476
-                $curl->setOption([
477
-                    CURLOPT_CUSTOMREQUEST => $method,
478
-                    CURLOPT_POST          => true,
479
-                    CURLOPT_POSTFIELDS    => json_encode(array_filter($postFields))
480
-                ]);
481
-                break;
482
-
483
-            /** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3 */
484
-            case Request::METHOD_GET:
485
-                $curl->setOption(CURLOPT_HTTPGET, true);
486
-                break;
487
-
488
-            /** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4 */
489
-            case Request::METHOD_HEAD:
490
-                $curl->setOption([
491
-                    CURLOPT_CUSTOMREQUEST => $method,
492
-                    CURLOPT_NOBODY        => true
493
-                ]);
494
-                break;
495
-
496
-            /** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5 */
497
-            case Request::METHOD_POST:
498
-                $curl->setOption([
499
-                    CURLOPT_POST       => true,
500
-                    CURLOPT_POSTFIELDS => json_encode(array_filter($postFields))
501
-                ]);
502
-                break;
503
-
504
-            /** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6 */
505
-            case Request::METHOD_PUT:
506
-                $curl->setOption([
507
-                    CURLOPT_CUSTOMREQUEST => $method,
508
-                    CURLOPT_PUT           => true,
509
-                    CURLOPT_HTTPHEADER    => [
510
-                        'X-HTTP-Method-Override: PUT',
511
-                        'Content-type: application/x-www-form-urlencoded'
512
-                    ]
513
-                ]);
514
-                break;
515
-
516
-            default:
517
-                break;
518
-        }
519
-
520
-        $curl->success(function (CurlClient $instance) {
521
-            $this->headers = $instance->getHeaders();
522
-            $this->success = $instance->getResponse();
523
-            $data          = json_decode($this->success, true);
524
-            if (JSON_ERROR_NONE === json_last_error()) {
525
-                $this->success = $data;
526
-            }
527
-        });
528
-        $curl->error(function (CurlClient $instance) {
529
-            $this->headers = $instance->getHeaders();
530
-            $this->failure = $instance->getResponse();
531
-            $data          = json_decode($this->failure, true);
532
-            if (JSON_ERROR_NONE === json_last_error()) {
533
-                $this->failure = $data;
534
-            }
535
-        });
536
-        $curl->perform();
537
-
538
-        return $this->success ?? $this->failure;
539
-    }
540
-
541
-    /**
542
-     * Return a formatted string. Modified version of sprintf using colon(:)
543
-     *
544
-     * @param string $string
545
-     * @param array  $params
546
-     *
547
-     * @return String
548
-     * @throws Exception
549
-     */
550
-    public function sprintf(string $string, ...$params): string
551
-    {
552
-        preg_match_all('/\:([A-Za-z0-9_]+)/', $string, $matches);
553
-        $matches = $matches[1];
554
-
555
-        if (count($matches)) {
556
-            $tokens   = [];
557
-            $replaces = [];
558
-
559
-            foreach ($matches as $key => $value) {
560
-                if (count($params) > 1 || !is_array($params[0])) {
561
-                    if (!array_key_exists($key, $params)) {
562
-                        throw new Exception('Too few arguments, missing argument: ' . $key);
563
-                    }
564
-                    $replaces[] = $params[$key];
565
-                } else {
566
-                    if (!array_key_exists($value, $params[0])) {
567
-                        throw new Exception('Missing array argument: ' . $key);
568
-                    }
569
-                    $replaces[] = $params[0][$value];
570
-                }
571
-                $tokens[] = ':' . $value;
572
-            }
573
-
574
-            $string = str_replace($tokens, $replaces, $string);
575
-        }
576
-
577
-        return $string;
578
-    }
11
+	/** API version */
12
+	const API_VERSION = 'v3';
13
+
14
+	/** API constants */
15
+	const API_URL        = 'https://api.github.com';
16
+	const API_UPLOADS    = 'https://uploads.github.com';
17
+	const API_RAW_URL    = 'https://raw.github.com';
18
+	const CONTENT_TYPE   = 'application/json';
19
+	const DEFAULT_ACCEPT = 'application/vnd.github.' . self::API_VERSION . '+json';
20
+	const USER_AGENT     = 'scion.github-api';
21
+
22
+	/** Archive constants */
23
+	const ARCHIVE_TARBALL = 'tarball';
24
+	const ARCHIVE_ZIPBALL = 'zipball';
25
+
26
+	/** Authentication constants */
27
+	const OAUTH_AUTH             = 0;
28
+	const OAUTH2_HEADER_AUTH     = 1;
29
+	const OAUTH2_PARAMETERS_AUTH = 2;
30
+
31
+	/** Branch constants */
32
+	const BRANCH_MASTER  = 'master';
33
+	const BRANCH_DEVELOP = 'develop';
34
+
35
+	/** Direction constants */
36
+	const DIRECTION_ASC  = 'asc';
37
+	const DIRECTION_DESC = 'desc';
38
+
39
+	/** Environment constants */
40
+	const ENVIRONMENT_PRODUCTION = 'production';
41
+	const ENVIRONMENT_STAGING    = 'staging';
42
+	const ENVIRONMENT_QA         = 'qa';
43
+
44
+	/** Events constants */
45
+	const EVENTS_PULL         = 'pull';
46
+	const EVENTS_PULL_REQUEST = 'pull_request';
47
+	const EVENTS_PUSH         = 'push';
48
+
49
+	/** Filter constants */
50
+	const FILTER_ALL        = 'all';
51
+	const FILTER_ASSIGNED   = 'assigned';
52
+	const FILTER_CREATED    = 'created';
53
+	const FILTER_MENTIONED  = 'mentioned';
54
+	const FILTER_SUBSCRIBED = 'subscribed';
55
+
56
+	/** Media types constants */
57
+	const MEDIA_TYPE_JSON = 'json';
58
+	const MEDIA_TYPE_RAW  = 'raw';
59
+	const MEDIA_TYPE_FULL = 'full';
60
+	const MEDIA_TYPE_TEXT = 'text';
61
+
62
+	/** Modes constants */
63
+	const MODE_MARKDOWN = 'markdown';
64
+	const MODE_GFM      = 'gfm';
65
+
66
+	/** Permissions constants */
67
+	const PERMISSION_ADMIN = 'admin';
68
+	const PERMISSION_PULL  = 'pull';
69
+	const PERMISSION_PUSH  = 'push';
70
+
71
+	/** Sort constants */
72
+	const SORT_COMPLETENESS = 'completeness';
73
+	const SORT_CREATED      = 'created';
74
+	const SORT_DUE_DATE     = 'due_date';
75
+	const SORT_FULL_NAME    = 'full_name';
76
+	const SORT_NEWEST       = 'newest';
77
+	const SORT_OLDEST       = 'oldest';
78
+	const SORT_PUSHED       = 'pushed';
79
+	const SORT_STARGAZERS   = 'stargazers';
80
+	const SORT_UPDATED      = 'updated';
81
+
82
+	/** State constants */
83
+	const STATE_ACTIVE  = 'active';
84
+	const STATE_ALL     = 'all';
85
+	const STATE_CLOSED  = 'closed';
86
+	const STATE_ERROR   = 'error';
87
+	const STATE_FAILURE = 'failure';
88
+	const STATE_OPEN    = 'open';
89
+	const STATE_PENDING = 'pending';
90
+	const STATE_SUCCESS = 'success';
91
+
92
+	/** Task constants */
93
+	const TASK_DEPLOY            = 'deploy';
94
+	const TASK_DEPLOY_MIGRATIONS = 'deploy:migrations';
95
+
96
+	/** Type constants */
97
+	const TYPE_ALL        = 'all';
98
+	const TYPE_COMMENTS   = 'comments';
99
+	const TYPE_GISTS      = 'gists';
100
+	const TYPE_HOOKS      = 'hooks';
101
+	const TYPE_ISSUES     = 'issues';
102
+	const TYPE_MEMBER     = 'member';
103
+	const TYPE_MILESTONES = 'milestones';
104
+	const TYPE_ORGS       = 'orgs';
105
+	const TYPE_OWNER      = 'owner';
106
+	const TYPE_PAGES      = 'pages';
107
+	const TYPE_PUBLIC     = 'public';
108
+	const TYPE_PULLS      = 'pulls';
109
+	const TYPE_PRIVATE    = 'private';
110
+	const TYPE_REPOS      = 'repos';
111
+	const TYPE_USERS      = 'users';
112
+
113
+	/** Properties */
114
+	protected $accept         = self::DEFAULT_ACCEPT;
115
+	protected $apiUrl         = self::API_URL;
116
+	protected $authentication = self::OAUTH_AUTH;
117
+	protected $clientId;
118
+	protected $clientSecret;
119
+	protected $contentType    = self::CONTENT_TYPE;
120
+	protected $failure;
121
+	protected $headers        = [];
122
+	protected $httpAuth       = ['username' => '', 'password' => ''];
123
+	protected $success;
124
+	protected $timeout        = 240;
125
+	protected $token          = '';
126
+	protected $request;
127
+
128
+	/**
129
+	 * Constructor
130
+	 */
131
+	public function __construct()
132
+	{
133
+		$this->request = Request::createFromGlobals();
134
+	}
135
+
136
+	/**
137
+	 * Get request
138
+	 *
139
+	 * @return Request
140
+	 */
141
+	public function getRequest(): Request
142
+	{
143
+		return $this->request;
144
+	}
145
+
146
+	/**
147
+	 * Get accept
148
+	 *
149
+	 * @return mixed
150
+	 */
151
+	public function getAccept()
152
+	{
153
+		return $this->accept;
154
+	}
155
+
156
+	/**
157
+	 * Set accept
158
+	 *
159
+	 * @param array|string $accept
160
+	 *
161
+	 * @return AbstractApi
162
+	 */
163
+	public function setAccept($accept): AbstractApi
164
+	{
165
+		$this->accept = $accept;
166
+
167
+		return $this;
168
+	}
169
+
170
+	/**
171
+	 * Get authentication
172
+	 *
173
+	 * @return int
174
+	 */
175
+	public function getAuthentication(): int
176
+	{
177
+		return $this->authentication;
178
+	}
179
+
180
+	/**
181
+	 * Set authentication
182
+	 *
183
+	 * @param int $authentication
184
+	 *
185
+	 * @return AbstractApi
186
+	 */
187
+	public function setAuthentication(int $authentication): AbstractApi
188
+	{
189
+		$this->authentication = $authentication;
190
+
191
+		return $this;
192
+	}
193
+
194
+	/**
195
+	 * Get apiUrl
196
+	 *
197
+	 * @return string
198
+	 */
199
+	public function getApiUrl(): string
200
+	{
201
+		return $this->apiUrl;
202
+	}
203
+
204
+	/**
205
+	 * Set apiUrl
206
+	 *
207
+	 * @param mixed $apiUrl
208
+	 *
209
+	 * @return AbstractApi
210
+	 */
211
+	public function setApiUrl($apiUrl): AbstractApi
212
+	{
213
+		$this->apiUrl = $apiUrl;
214
+
215
+		return $this;
216
+	}
217
+
218
+	/**
219
+	 * Get clientId
220
+	 *
221
+	 * @return null|int
222
+	 */
223
+	public function getClientId()
224
+	{
225
+		return $this->clientId;
226
+	}
227
+
228
+	/**
229
+	 * Set clientId
230
+	 *
231
+	 * @param mixed $clientId
232
+	 *
233
+	 * @return AbstractApi
234
+	 */
235
+	public function setClientId($clientId): AbstractApi
236
+	{
237
+		$this->clientId = $clientId;
238
+
239
+		return $this;
240
+	}
241
+
242
+	/**
243
+	 * Get clientSecret
244
+	 *
245
+	 * @return null|string
246
+	 */
247
+	public function getClientSecret()
248
+	{
249
+		return $this->clientSecret;
250
+	}
251
+
252
+	/**
253
+	 * Set clientSecret
254
+	 *
255
+	 * @param mixed $clientSecret
256
+	 *
257
+	 * @return AbstractApi
258
+	 */
259
+	public function setClientSecret($clientSecret): AbstractApi
260
+	{
261
+		$this->clientSecret = $clientSecret;
262
+
263
+		return $this;
264
+	}
265
+
266
+	/**
267
+	 * Get httpAuth
268
+	 *
269
+	 * @return array
270
+	 */
271
+	public function getHttpAuth(): array
272
+	{
273
+		return $this->httpAuth;
274
+	}
275
+
276
+	/**
277
+	 * Set httpAuth
278
+	 *
279
+	 * @param string $username
280
+	 * @param string $password
281
+	 *
282
+	 * @return AbstractApi
283
+	 */
284
+	public function setHttpAuth(string $username, string $password = ''): AbstractApi
285
+	{
286
+		$this->httpAuth['username'] = $username;
287
+		$this->httpAuth['password'] = $password;
288
+
289
+		return $this;
290
+	}
291
+
292
+	/**
293
+	 * Get token
294
+	 *
295
+	 * @return string
296
+	 */
297
+	public function getToken(): string
298
+	{
299
+		return $this->token;
300
+	}
301
+
302
+	/**
303
+	 * Set token
304
+	 *
305
+	 * @param string $token
306
+	 * @param int    $authentication
307
+	 *
308
+	 * @return AbstractApi
309
+	 */
310
+	public function setToken(string $token, int $authentication = self::OAUTH_AUTH): AbstractApi
311
+	{
312
+		$this->token = $token;
313
+		$this->setAuthentication($authentication);
314
+
315
+		return $this;
316
+	}
317
+
318
+	/**
319
+	 * Get timeout
320
+	 *
321
+	 * @return int
322
+	 */
323
+	public function getTimeout(): int
324
+	{
325
+		return $this->timeout;
326
+	}
327
+
328
+	/**
329
+	 * Set timeout
330
+	 *
331
+	 * @param int $timeout
332
+	 *
333
+	 * @return AbstractApi
334
+	 */
335
+	public function setTimeout(int $timeout): AbstractApi
336
+	{
337
+		$this->timeout = $timeout;
338
+
339
+		return $this;
340
+	}
341
+
342
+	/**
343
+	 * Get contentType
344
+	 *
345
+	 * @return string
346
+	 */
347
+	public function getContentType(): string
348
+	{
349
+		return $this->contentType;
350
+	}
351
+
352
+	/**
353
+	 * Set contentType
354
+	 *
355
+	 * @param string $contentType
356
+	 *
357
+	 * @return AbstractApi
358
+	 */
359
+	public function setContentType(string $contentType): AbstractApi
360
+	{
361
+		$this->contentType = $contentType;
362
+
363
+		return $this;
364
+	}
365
+
366
+	/**
367
+	 * Get headers
368
+	 *
369
+	 * @return array
370
+	 */
371
+	public function getHeaders(): array
372
+	{
373
+		return $this->headers;
374
+	}
375
+
376
+	/**
377
+	 * Curl request
378
+	 *
379
+	 * @param string      $url
380
+	 * @param string      $method
381
+	 * @param array       $postFields
382
+	 * @param null|string $apiUrl
383
+	 *
384
+	 * @return array
385
+	 */
386
+	public function request(string $url, string $method = Request::METHOD_GET, array $postFields = [],
387
+							string $apiUrl = null): array
388
+	{
389
+		/** Building url */
390
+		if (null === $apiUrl) {
391
+			$apiUrl = $this->getApiUrl();
392
+		}
393
+		$url = $apiUrl . $url;
394
+
395
+		/**
396
+		 * OAuth2 Key/Secret authentication
397
+		 *
398
+		 * @see https://developer.github.com/v3/#oauth2-keysecret
399
+		 */
400
+		if (null !== $this->getClientId() && null !== $this->getClientSecret()) {
401
+			$url .= (strstr($url, '?') !== false ? '&' : '?');
402
+			$url .= http_build_query(['client_id'     => $this->getClientId(),
403
+									  'client_secret' => $this->getClientSecret()
404
+			]);
405
+		} /**
406
+		 * Basic authentication via OAuth2 Token (sent as a parameter)
407
+		 *
408
+		 * @see https://developer.github.com/v3/#oauth2-token-sent-as-a-parameter
409
+		 */ else if ($this->getAuthentication() === self::OAUTH2_PARAMETERS_AUTH) {
410
+			$url .= http_build_query(['access_token' => $this->getToken()]);
411
+		}
412
+
413
+		/** Call curl */
414
+		$curl = new CurlClient();
415
+		$curl->setOption([
416
+			CURLOPT_USERAGENT      => self::USER_AGENT,
417
+			CURLOPT_TIMEOUT        => $this->getTimeout(),
418
+			CURLOPT_HEADER         => false, // Use $client->getHeaders() to get full header
419
+			CURLOPT_FOLLOWLOCATION => true,
420
+			CURLOPT_HTTPHEADER     => [
421
+				'Accept: ' . $this->getAccept(),
422
+				'Content-Type: ' . $this->getContentType()
423
+			],
424
+			CURLOPT_URL            => $url
425
+		]);
426
+
427
+		/**
428
+		 * Basic authentication via username and Password
429
+		 *
430
+		 * @see https://developer.github.com/v3/auth/#via-username-and-password
431
+		 */
432
+		if (!empty($this->getHttpAuth())) {
433
+			if (!isset($this->getHttpAuth()['password']) || empty($this->getHttpAuth()['password'])) {
434
+				$curl->setOption([
435
+					CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
436
+					CURLOPT_USERPWD  => $this->getHttpAuth()['username']
437
+				]);
438
+			} else {
439
+				$curl->setOption([
440
+					CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
441
+					CURLOPT_USERPWD  => sprintf('%s:%s', $this->getHttpAuth()['username'],
442
+						$this->getHttpAuth()['password'])
443
+				]);
444
+			}
445
+		}
446
+
447
+		if (!empty($this->getToken()) && $this->getAuthentication() !== self::OAUTH2_PARAMETERS_AUTH) {
448
+			/**
449
+			 * Basic authentication via OAuth token
450
+			 *
451
+			 * @see https://developer.github.com/v3/auth/#via-oauth-tokens
452
+			 **/
453
+			if ($this->getAuthentication() === self::OAUTH_AUTH) {
454
+				$curl->setOption([
455
+					CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
456
+					CURLOPT_USERPWD  => sprintf('%s:x-oauth-basic', $this->getToken())
457
+				]);
458
+			} /**
459
+			 * Basic authentication via OAuth2 Token (sent in a header)
460
+			 *
461
+			 * @see https://developer.github.com/v3/#oauth2-token-sent-in-a-header
462
+			 */ else if ($this->getAuthentication() === self::OAUTH2_HEADER_AUTH) {
463
+				$curl->setOption([
464
+					CURLOPT_HTTPAUTH   => CURLAUTH_BASIC,
465
+					CURLOPT_HTTPHEADER => [sprintf('Authorization: token %s', $this->getToken())]
466
+				]);
467
+			}
468
+		}
469
+
470
+		/** Methods */
471
+		switch ($method) {
472
+			/** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7 */
473
+			case Request::METHOD_DELETE:
474
+				/** @see http://tools.ietf.org/html/rfc5789 */
475
+			case Request::METHOD_PATCH:
476
+				$curl->setOption([
477
+					CURLOPT_CUSTOMREQUEST => $method,
478
+					CURLOPT_POST          => true,
479
+					CURLOPT_POSTFIELDS    => json_encode(array_filter($postFields))
480
+				]);
481
+				break;
482
+
483
+			/** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3 */
484
+			case Request::METHOD_GET:
485
+				$curl->setOption(CURLOPT_HTTPGET, true);
486
+				break;
487
+
488
+			/** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4 */
489
+			case Request::METHOD_HEAD:
490
+				$curl->setOption([
491
+					CURLOPT_CUSTOMREQUEST => $method,
492
+					CURLOPT_NOBODY        => true
493
+				]);
494
+				break;
495
+
496
+			/** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5 */
497
+			case Request::METHOD_POST:
498
+				$curl->setOption([
499
+					CURLOPT_POST       => true,
500
+					CURLOPT_POSTFIELDS => json_encode(array_filter($postFields))
501
+				]);
502
+				break;
503
+
504
+			/** @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6 */
505
+			case Request::METHOD_PUT:
506
+				$curl->setOption([
507
+					CURLOPT_CUSTOMREQUEST => $method,
508
+					CURLOPT_PUT           => true,
509
+					CURLOPT_HTTPHEADER    => [
510
+						'X-HTTP-Method-Override: PUT',
511
+						'Content-type: application/x-www-form-urlencoded'
512
+					]
513
+				]);
514
+				break;
515
+
516
+			default:
517
+				break;
518
+		}
519
+
520
+		$curl->success(function (CurlClient $instance) {
521
+			$this->headers = $instance->getHeaders();
522
+			$this->success = $instance->getResponse();
523
+			$data          = json_decode($this->success, true);
524
+			if (JSON_ERROR_NONE === json_last_error()) {
525
+				$this->success = $data;
526
+			}
527
+		});
528
+		$curl->error(function (CurlClient $instance) {
529
+			$this->headers = $instance->getHeaders();
530
+			$this->failure = $instance->getResponse();
531
+			$data          = json_decode($this->failure, true);
532
+			if (JSON_ERROR_NONE === json_last_error()) {
533
+				$this->failure = $data;
534
+			}
535
+		});
536
+		$curl->perform();
537
+
538
+		return $this->success ?? $this->failure;
539
+	}
540
+
541
+	/**
542
+	 * Return a formatted string. Modified version of sprintf using colon(:)
543
+	 *
544
+	 * @param string $string
545
+	 * @param array  $params
546
+	 *
547
+	 * @return String
548
+	 * @throws Exception
549
+	 */
550
+	public function sprintf(string $string, ...$params): string
551
+	{
552
+		preg_match_all('/\:([A-Za-z0-9_]+)/', $string, $matches);
553
+		$matches = $matches[1];
554
+
555
+		if (count($matches)) {
556
+			$tokens   = [];
557
+			$replaces = [];
558
+
559
+			foreach ($matches as $key => $value) {
560
+				if (count($params) > 1 || !is_array($params[0])) {
561
+					if (!array_key_exists($key, $params)) {
562
+						throw new Exception('Too few arguments, missing argument: ' . $key);
563
+					}
564
+					$replaces[] = $params[$key];
565
+				} else {
566
+					if (!array_key_exists($value, $params[0])) {
567
+						throw new Exception('Missing array argument: ' . $key);
568
+					}
569
+					$replaces[] = $params[0][$value];
570
+				}
571
+				$tokens[] = ':' . $value;
572
+			}
573
+
574
+			$string = str_replace($tokens, $replaces, $string);
575
+		}
576
+
577
+		return $string;
578
+	}
579 579
 }
580 580
\ No newline at end of file
Please login to merge, or discard this patch.