GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( fec5df...435afa )
by ignace
24:00
created

JSend::validateString()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 8.8571
cc 5
eloc 6
nc 2
nop 1
1
<?php
2
3
namespace Carpediem\JSend;
4
5
use InvalidArgumentException;
6
use JsonSerializable;
7
use UnexpectedValueException;
8
9
/**
10
 * A Immutable Value Object Class to represent a JSend object
11
 */
12
class JSend implements JsonSerializable
13
{
14
    const STATUS_SUCCESS = 'success';
15
16
    const STATUS_ERROR = 'error';
17
18
    const STATUS_FAIL = 'fail';
19
20
    /**
21
     * JSend status
22
     *
23
     * @var string
24
     */
25
    protected $status;
26
27
    /**
28
     * JSend Data
29
     *
30
     * @var array
31
     */
32
    protected $data = [];
33
34
    /**
35
     * JSend Error Message
36
     *
37
     * @var string
38
     */
39
    protected $errorMessage = '';
40
41
    /**
42
     * JSend Error Code
43
     * @var int|null
44
     */
45
    protected $errorCode;
46
47
    /**
48
     * New Instance
49
     *
50
     * @param string $status
51
     * @param array  $data
52
     * @param string $errorMessage
53
     * @param int    $errorCode
54
     */
55
    public function __construct($status, array $data = null, $errorMessage = null, $errorCode = null)
56
    {
57
        $this->status = $this->filterStatus($status);
58
        $this->data = $data ?: [];
59
        $this->filterError($errorMessage, $errorCode);
60
    }
61
62
    /**
63
     * Filter and Validate the JSend Status
64
     *
65
     * @param string $status
66
     *
67
     * @throws UnexpectedValueException If the status value does not conform to JSend Spec.
68
     *
69
     * @return string
70
     */
71
    protected function filterStatus($status)
72
    {
73
        $res = [self::STATUS_SUCCESS => 1, self::STATUS_ERROR => 1, self::STATUS_FAIL => 1];
74
        if (isset($res[$status])) {
75
            return $status;
76
        }
77
78
        throw new UnexpectedValueException('The given status does not conform to Jsend specification');
79
    }
80
81
    /**
82
     * Filter and Validate the JSend Error properties
83
     *
84
     * @param string $errorMessage
85
     * @param int    $errorCode
86
     */
87
    protected function filterError($errorMessage, $errorCode)
88
    {
89
        if (self::STATUS_ERROR != $this->status) {
90
            return;
91
        }
92
93
        $this->errorMessage = $this->validateString($errorMessage);
94
        if (!is_null($errorCode)) {
95
            $this->errorCode = $this->validateInt($errorCode);
96
        }
97
    }
98
99
    /**
100
     * Validate a string
101
     *
102
     * @param mixed $str
103
     *
104
     * @throws UnexpectedValueException If the data value is not a string
105
     *
106
     * @return string
107
     */
108
    protected function validateString($str)
109
    {
110
        if (is_string($str) || (is_object($str) && method_exists($str, '__toString'))) {
111
            return (string) $str;
112
        }
113
114
        throw new UnexpectedValueException(sprintf(
115
            'Expected data to be a string; received "%s"',
116
            (is_object($str) ? get_class($str) : gettype($str))
117
        ));
118
    }
119
120
    /**
121
     * Validate a integer
122
     *
123
     * @param mixed $int
124
     *
125
     * @throws UnexpectedValueException If the data value is not an integer
126
     *
127
     * @return int
128
     */
129
    protected function validateInt($int)
130
    {
131
        if (false === ($res = filter_var($int, FILTER_VALIDATE_INT))) {
132
            throw new UnexpectedValueException(sprintf(
133
                'Expected data to be a int; received "%s"',
134
                (is_object($int) ? get_class($int) : gettype($int))
135
            ));
136
        }
137
138
        return $res;
139
    }
140
141
    /**
142
     * Returns the JSend status
143
     *
144
     * @return string
145
     */
146
    public function getStatus()
147
    {
148
        return $this->status;
149
    }
150
151
    /**
152
     * Returns the JSend data
153
     *
154
     * @return array
155
     */
156
    public function getData()
157
    {
158
        return $this->data;
159
    }
160
161
    /**
162
     * Returns the JSend error message
163
     *
164
     * @return string
165
     */
166
    public function getErrorMessage()
167
    {
168
        return $this->errorMessage;
169
    }
170
171
    /**
172
     * Returns the JSend error code
173
     *
174
     * @return int|null
175
     */
176
    public function getErrorCode()
177
    {
178
        return $this->errorCode;
179
    }
180
181
    /**
182
     * Returns true if the status is success
183
     *
184
     * @return bool
185
     */
186
    public function isSuccess()
187
    {
188
        return self::STATUS_SUCCESS === $this->status;
189
    }
190
191
    /**
192
     * Returns true if the status is fail
193
     *
194
     * @return bool
195
     */
196
    public function isFail()
197
    {
198
        return self::STATUS_FAIL === $this->status;
199
    }
200
201
    /**
202
     * Returns true if the status is error
203
     *
204
     * @return bool
205
     */
206
    public function isError()
207
    {
208
        return self::STATUS_ERROR === $this->status;
209
    }
210
211
    /**
212
     * @inheritdoc
213
     */
214
    public function __toString()
215
    {
216
        return json_encode($this, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
217
    }
218
219
    /**
220
     * @inheritdoc
221
     */
222
    public function jsonSerialize()
223
    {
224
        return $this->toArray();
225
    }
226
227
    /**
228
     * Transcode the JSend object into an array
229
     *
230
     * @return array
231
     */
232
    public function toArray()
233
    {
234
        $arr = ['status' => $this->status, 'data' => $this->data ?: null];
235
        if (self::STATUS_ERROR !== $this->status) {
236
            return $arr;
237
        }
238
239
        $arr['message'] = $this->errorMessage;
240
        if (!is_null($this->errorCode)) {
241
            $arr['code'] = $this->errorCode;
242
        }
243
244
        if (is_null($arr['data'])) {
245
            unset($arr['data']);
246
        }
247
248
        return $arr;
249
    }
250
251
    /**
252
     * @inheritdoc
253
     */
254
    public function __debugInfo()
255
    {
256
        return $this->toArray();
257
    }
258
259
    /**
260
     * Encode and Send the JSend object as an HTTP Response
261
     *
262
     * @param array $headers Optional headers to add to the response
263
     *
264
     * @return string
265
     */
266
    public function send(array $headers = [])
267
    {
268
        $headers = $this->filterHeaders($headers);
269
        $headers[] = 'Content-Type: application/json;charset=utf-8';
270
        foreach ($headers as $header) {
271
            header($header);
272
        }
273
        echo $this;
274
    }
275
276
    /**
277
     * Filter Submitted Headers
278
     *
279
     * @param array $headers a Collection of key/value headers
280
     *
281
     * @return array
282
     */
283
    protected function filterHeaders(array $headers)
284
    {
285
        $formattedHeaders = [];
286
        foreach ($headers as $name => $value) {
287
            $formattedHeaders[] = $this->validateHeaderName($name).': '.$this->validateHeaderValue($value);
288
        }
289
290
        return $formattedHeaders;
291
    }
292
293
    /**
294
     * Validate Header name
295
     *
296
     * @param string $name
297
     *
298
     * @throws InvalidArgumentException if the header name is invalid
299
     *
300
     * @return string
301
     */
302
    protected function validateHeaderName($name)
303
    {
304
        if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $name)) {
305
            throw new InvalidArgumentException('Invalid header name');
306
        }
307
308
        return $name;
309
    }
310
311
    /**
312
     * Validate Header value
313
     *
314
     * @param string $value
315
     *
316
     * @throws InvalidArgumentException if the header value is invalid
317
     *
318
     * @return string
319
     */
320
    protected function validateHeaderValue($value)
321
    {
322
        if (preg_match("#(?:(?:(?<!\r)\n)|(?:\r(?!\n))|(?:\r\n(?![ \t])))#", $value)
323
            || preg_match('/[^\x09\x0a\x0d\x20-\x7E\x80-\xFE]/', $value)
324
        ) {
325
            throw new InvalidArgumentException('Invalid header value');
326
        }
327
328
        return $value;
329
    }
330
331
    /**
332
     * Returns an instance with the specified status.
333
     *
334
     * This method MUST retain the state of the current instance, and return
335
     * an instance that contains the specified status.
336
     *
337
     * @param string $status The status to use with the new instance.
338
     *
339
     * @return static A new instance with the specified status.
340
     */
341
    public function withStatus($status)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
342
    {
343
        if ($status === $this->status) {
344
            return $this;
345
        }
346
347
        return new static($status, $this->data, $this->errorMessage, $this->errorCode);
348
    }
349
350
    /**
351
     * Returns an instance with the specified data.
352
     *
353
     * This method MUST retain the state of the current instance, and return
354
     * an instance that contains the specified data.
355
     *
356
     * @param array $data The data to use with the new instance.
357
     *
358
     * @return static A new instance with the specified data.
359
     */
360
    public function withData(array $data)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
361
    {
362
        if ($data === $this->data) {
363
            return $this;
364
        }
365
366
        return new static($this->status, $data, $this->errorMessage, $this->errorCode);
367
    }
368
369
    /**
370
     * Returns an instance with the specified error message and error code.
371
     *
372
     * This method MUST retain the state of the current instance, and return
373
     * an instance that contains the specified error message and error code.
374
     *
375
     * @param string   $errorMessage The error message to use with the new instance.
376
     * @param int|null $errorCode    The error code to use with the new instance.
377
     *
378
     * @return static A new instance with the specified status.
379
     */
380
    public function withError($errorMessage, $errorCode = null)
381
    {
382
        if ($errorMessage == $this->errorMessage && $errorCode == $this->errorCode) {
383
            return $this;
384
        }
385
386
        return new static($this->status, $this->data, $errorMessage, $errorCode);
387
    }
388
389
    /**
390
     * Returns a successful JSend object with the specified data
391
     *
392
     * @param array $data The data to use with the new instance.
393
     *
394
     * @return static A new succesful instance with the specified data.
395
     */
396
    public static function success(array $data = [])
397
    {
398
        return new static(static::STATUS_SUCCESS, $data);
399
    }
400
401
    /**
402
     * Returns a failed JSend object with the specified data
403
     *
404
     * @param array $data The data to use with the new instance.
405
     *
406
     * @return static A new failed instance with the specified data.
407
     */
408
    public static function fail(array $data = [])
409
    {
410
        return new static(static::STATUS_FAIL, $data);
411
    }
412
413
    /**
414
     * Returns a error JSend object with the specified error message and error code.
415
     *
416
     * @param string   $errorMessage The error message to use with the new instance.
417
     * @param int|null $errorCode    The error code to use with the new instance.
418
     * @param array    $data         The optional data to use with the new instance.
419
     *
420
     * @return static A new failed instance with the specified data.
421
     */
422
    public static function error($errorMessage, $errorCode = null, $data = null)
423
    {
424
        return new static(static::STATUS_ERROR, $data, $errorMessage, $errorCode);
425
    }
426
427
    /**
428
     * Returns a new instance from a JSON string
429
     *
430
     * @param string $json    The string being decoded
431
     * @param int    $depth   User specified recursion depth.
432
     * @param int    $options Bitmask of JSON decode options
433
     *
434
     * @throws InvalidArgumentException If the string can not be decode
435
     *
436
     * @return static
437
     */
438
    public static function createFromString($json, $depth = 512, $options = 0)
439
    {
440
        $raw = json_decode($json, true, $depth, $options);
441
        if (JSON_ERROR_NONE !== json_last_error()) {
442
            throw new InvalidArgumentException(sprintf(
443
                'Unable to decode JSON to array in %s: %s',
444
                __CLASS__,
445
                json_last_error_msg()
446
            ));
447
        }
448
449
        return static::createFromArray($raw);
450
    }
451
452
    /**
453
     * @inheritdoc
454
     */
455
    public static function __set_state(array $properties)
456
    {
457
        return static::createFromArray($properties);
458
    }
459
460
    /**
461
     * Returns a new instance from an array
462
     *
463
     * @param array $arr The array to build a new JSend object with
464
     *
465
     * @return static
466
     */
467
    public static function createFromArray(array $arr)
468
    {
469
        $defaultValues = ['status' => null, 'data' => null, 'message' => null, 'code' => null];
470
        $arr = array_replace($defaultValues, array_intersect_key($arr, $defaultValues));
471
472
        return new static($arr['status'], $arr['data'], $arr['message'], $arr['code']);
473
    }
474
}
475