Passed
Push — develop ( 44c63c...c491e8 )
by Pablo
02:18
created

ApiRouter::setCorsHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * Routes requests to the API to the appropriate controllers
5
 *
6
 * @package     Nails
7
 * @subpackage  module-api
8
 * @category    Controller
9
 * @author      Nails Dev Team
10
 * @link
11
 */
12
13
use Nails\Auth;
14
use Nails\Api\Constants;
15
use Nails\Api\Exception\ApiException;
16
use Nails\Api\Factory\ApiResponse;
17
use Nails\Common\Exception\NailsException;
18
use Nails\Common\Exception\ValidationException;
19
use Nails\Common\Factory\Logger;
20
use Nails\Common\Service\HttpCodes;
21
use Nails\Common\Service\Input;
22
use Nails\Common\Service\Output;
23
use Nails\Components;
24
use Nails\Environment;
25
use Nails\Factory;
26
27
// --------------------------------------------------------------------------
28
29
/**
30
 * Allow the app to add functionality, if needed
31
 */
32
if (class_exists('\App\Api\Controller\BaseRouter')) {
33
    abstract class BaseMiddle extends \App\Api\Controller\BaseRouter
0 ignored issues
show
Bug introduced by Pablo de la Peña
The type App\Api\Controller\BaseRouter was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34
    {
35
    }
36
} else {
37
    abstract class BaseMiddle extends \Nails\Common\Controller\Base
38
    {
39
    }
40
}
41
42
// --------------------------------------------------------------------------
43
44
/**
45
 * Class ApiRouter
46
 */
47
class ApiRouter extends BaseMiddle
48
{
49
    const FORMAT_JSON                      = 'JSON';
50
    const FORMAT_TXT                       = 'TXT';
51
    const DEFAULT_FORMAT                   = 'JSON';
52
    const REQUEST_METHOD_GET               = 'GET';
53
    const REQUEST_METHOD_PUT               = 'PUT';
54
    const REQUEST_METHOD_POST              = 'POST';
55
    const REQUEST_METHOD_DELETE            = 'DELETE';
56
    const REQUEST_METHOD_OPTIONS           = 'OPTIONS';
57
    const VALID_FORMATS                    = [
58
        self::FORMAT_TXT,
59
        self::FORMAT_JSON,
60
    ];
61
    const ACCESS_TOKEN_HEADER              = 'X-Access-Token';
62
    const ACCESS_TOKEN_POST_PARAM          = 'accessToken';
63
    const ACCESS_TOKEN_GET_PARAM           = 'accessToken';
64
    const ACCESS_CONTROL_ALLOW_ORIGIN      = '*';
65
    const ACCESS_CONTROL_ALLOW_CREDENTIALS = 'true';
66
    const ACCESS_CONTROL_MAX_AGE           = 86400;
67
    const ACCESS_CONTROL_ALLOW_HEADERS     = [
68
        self::ACCESS_TOKEN_HEADER,
69
        'content',
70
        'origin',
71
        'content-type',
72
    ];
73
    const ACCESS_CONTROL_ALLOW_METHODS     = [
74
        self::REQUEST_METHOD_GET,
75
        self::REQUEST_METHOD_PUT,
76
        self::REQUEST_METHOD_POST,
77
        self::REQUEST_METHOD_DELETE,
78
        self::REQUEST_METHOD_OPTIONS,
79
    ];
80
    const OUTPUT_FORMAT_PATTERN            = '/\.([a-z]*)$/';
81
82
    // --------------------------------------------------------------------------
83
84
    /** @var string */
85
    private $sRequestMethod;
86
87
    /** @var string */
88
    private $sModuleName;
89
90
    /** @var string */
91
    private $sClassName;
92
93
    /** @var string */
94
    private $sMethod;
95
96
    /** @var array */
97
    private $aOutputValidFormats;
98
99
    /** @var string */
100
    private $sOutputFormat;
101
102
    /** @var bool */
103
    private $bOutputSendHeader;
104
105
    /** @var Logger */
106
    private $oLogger;
107
108
    /** @var string */
109
    private $sAccessToken;
110
111
    // --------------------------------------------------------------------------
112
113
    /**
114
     * ApiRouter constructor.
115
     *
116
     * @throws \Nails\Common\Exception\FactoryException
117
     */
118
    public function __construct()
119
    {
120
        parent::__construct();
121
122
        // --------------------------------------------------------------------------
123
124
        //  Set defaults
125
        $this->bOutputSendHeader = true;
126
127
        // --------------------------------------------------------------------------
128
129
        //  Work out the request method
130
        /** @var Input $oInput */
131
        $oInput               = Factory::service('Input');
132
        $this->sRequestMethod = $oInput->server('REQUEST_METHOD');
133
        $this->sRequestMethod = $this->sRequestMethod ? $this->sRequestMethod : static::REQUEST_METHOD_GET;
134
135
        /**
136
         * In order to work out the next few parts we'll analyse the URI string manually.
137
         * We're doing this because of the optional return type at the end of the string;
138
         * it's easier to regex that quickly, remove it, then split up the segments.
139
         */
140
141
        $this->sOutputFormat = static::getOutputFormat();
142
        $sUri                = preg_replace(static::OUTPUT_FORMAT_PATTERN, '', uri_string());
143
144
        //  Remove the module prefix (i.e "api/") then explode into segments
145
        //  Using regex as some systems will report a leading slash (e.g CLI)
146
        $sUri = preg_replace('#/?api/#', '', $sUri);
147
        $aUri = explode('/', $sUri);
148
149
        //  Work out the sModuleName, sClassName and method
150
        $this->sModuleName = getFromArray(0, $aUri, null);
151
        $this->sClassName  = ucfirst(getFromArray(1, $aUri, $this->sModuleName));
152
        $this->sMethod     = getFromArray(2, $aUri, 'index');
153
154
        //  Configure logging
155
        $oNow          = Factory::factory('DateTime');
156
        $this->oLogger = Factory::factory('Logger');
157
        $this->oLogger->setFile('api-' . $oNow->format('Y-m-d') . '.php');
158
    }
159
160
    // --------------------------------------------------------------------------
161
162
    /**
163
     * Returns the output format
164
     *
165
     * @return string|null
166
     */
167
    public static function getOutputFormat()
168
    {
169
        preg_match(static::OUTPUT_FORMAT_PATTERN, uri_string(), $aMatches);
170
        $sFormat = !empty($aMatches[1]) ? strtoupper($aMatches[1]) : null;
171
        return static::isValidFormat($sFormat) ? $sFormat : static::DEFAULT_FORMAT;
172
    }
173
174
    // --------------------------------------------------------------------------
175
176
    /**
177
     * Route the call to the correct place
178
     */
179
    public function index()
180
    {
181
        //  Handle OPTIONS CORS pre-flight requests
182
        if ($this->sRequestMethod === static::REQUEST_METHOD_OPTIONS) {
183
184
            $this->setCorsHeaders();
185
            /** @var Output $oOutput */
186
            $oOutput = Factory::service('Output');
187
            $oOutput->set_status_header(HttpCodes::STATUS_NO_CONTENT);
188
            return;
189
190
        } else {
191
192
            try {
193
                /**
194
                 * If an access token has been passed then verify it
195
                 *
196
                 * Passing the token via the header is preferred, but fallback to the GET
197
                 * and POST arrays.
198
                 */
199
200
                /** @var Input $oInput */
201
                $oInput = Factory::service('Input');
202
                /** @var HttpCodes $oHttpCodes */
203
                $oHttpCodes = Factory::service('HttpCodes');
204
                /** @var Auth\Model\User\AccessToken $oUserAccessTokenModel */
205
                $oUserAccessTokenModel = Factory::model('UserAccessToken', Auth\Constants::MODULE_SLUG);
206
207
                $sAccessToken = $oInput->header(static::ACCESS_TOKEN_HEADER);
208
209
                if (!$sAccessToken) {
210
                    $sAccessToken = $oInput->post(static::ACCESS_TOKEN_POST_PARAM);
211
                }
212
213
                if (!$sAccessToken) {
214
                    $sAccessToken = $oInput->get(static::ACCESS_TOKEN_GET_PARAM);
215
                }
216
217
                if ($sAccessToken) {
218
219
                    $this->sAccessToken = $sAccessToken;
0 ignored issues
show
Documentation Bug introduced by Pablo de la Peña
It seems like $sAccessToken can also be of type array. However, the property $sAccessToken is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
220
                    $oAccessToken       = $oUserAccessTokenModel->getByValidToken($sAccessToken);
0 ignored issues
show
Bug introduced by Pablo de la Peña
It seems like $sAccessToken can also be of type array; however, parameter $sToken of Nails\Auth\Model\User\Ac...oken::getByValidToken() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

220
                    $oAccessToken       = $oUserAccessTokenModel->getByValidToken(/** @scrutinizer ignore-type */ $sAccessToken);
Loading history...
221
222
                    if ($oAccessToken) {
0 ignored issues
show
introduced by Pablo de la Peña
$oAccessToken is of type Nails\Common\Resource, thus it always evaluated to true.
Loading history...
223
                        /** @var Auth\Model\User $oUserModel */
224
                        $oUserModel = Factory::model('User', Auth\Constants::MODULE_SLUG);
225
                        $oUserModel->setLoginData($oAccessToken->user_id, false);
0 ignored issues
show
Bug introduced by Pablo de la Peña
The property user_id does not seem to exist on Nails\Common\Resource.
Loading history...
226
                    } else {
227
                        throw new ApiException(
228
                            'Invalid access token',
229
                            $oHttpCodes::STATUS_UNAUTHORIZED
230
                        );
231
                    }
232
                }
233
234
                // --------------------------------------------------------------------------
235
236
                if (!$this->outputSetFormat($this->sOutputFormat)) {
237
                    throw new ApiException(
238
                        '"' . $this->sOutputFormat . '" is not a valid format.',
239
                        $oHttpCodes::STATUS_BAD_REQUEST
240
                    );
241
                }
242
243
                // --------------------------------------------------------------------------
244
245
                //  Register API modules
246
                $aNamespaces = [
247
                    'app' => (object) [
248
                        'namespace' => 'App\\',
249
                    ],
250
                ];
251
                foreach (Components::modules() as $oModule) {
252
                    if (!empty($oModule->data->{Constants::MODULE_SLUG}->namespace)) {
253
                        $sNamespace = $oModule->data->{Constants::MODULE_SLUG}->namespace;
254
                        if (array_key_exists($sNamespace, $aNamespaces)) {
255
                            throw new NailsException(
256
                                'Conflicting API namespace "' . $sNamespace . '" in use by ' .
257
                                '"' . $oModule->slug . '" and "' . $aNamespaces[$sNamespace]->slug . '"'
258
                            );
259
                        }
260
                        $aNamespaces[$sNamespace] = $oModule;
261
                    }
262
                }
263
264
                $i404Status = $oHttpCodes::STATUS_NOT_FOUND;
265
                $s404Error  = '"' . strtolower($this->sModuleName . '/' . $this->sClassName . '/' . $this->sMethod) . '" is not a valid API route.';
266
267
                if (!array_key_exists($this->sModuleName, $aNamespaces)) {
268
                    throw new ApiException($s404Error, $i404Status);
269
                }
270
271
                $oNamespace          = $aNamespaces[$this->sModuleName];
272
                $sOriginalController = $this->sClassName;
273
274
                //  Do we need to remap the controller?
275
                if (!empty($oNamespace->data->{Constants::MODULE_SLUG}->{'controller-map'})) {
276
277
                    $aMap             = (array) $oNamespace->data->{Constants::MODULE_SLUG}->{'controller-map'};
278
                    $this->sClassName = getFromArray($this->sClassName, $aMap, $this->sClassName);
279
280
                    //  This prevents users from accessing the "correct" controller, so we only have one valid route
281
                    $sRemapped = array_search($sOriginalController, $aMap);
282
                    if ($sRemapped !== false) {
283
                        $this->sClassName = $sRemapped;
284
                    }
285
                }
286
287
                $sController = $oNamespace->namespace . 'Api\\Controller\\' . $this->sClassName;
288
289
                if (!class_exists($sController)) {
290
                    throw new ApiException($s404Error, $i404Status);
291
                }
292
293
                $mAuth = $sController::isAuthenticated($this->sRequestMethod, $this->sMethod);
294
                if ($mAuth !== true) {
295
296
                    if (is_array($mAuth)) {
297
                        $sError  = getFromArray('error', $mAuth, $oHttpCodes::getByCode($oHttpCodes::STATUS_UNAUTHORIZED));
0 ignored issues
show
Bug introduced by Pablo de la Peña
Are you sure the usage of $oHttpCodes::getByCode($...s::STATUS_UNAUTHORIZED) targeting Nails\Common\Service\HttpCodes::getByCode() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
298
                        $iStatus = (int) getFromArray('status', $mAuth, $oHttpCodes::STATUS_UNAUTHORIZED);
299
                    } else {
300
                        $sError  = 'You must be logged in to access this resource';
301
                        $iStatus = $oHttpCodes::STATUS_UNAUTHORIZED;
302
                    }
303
304
                    throw new ApiException($sError, $iStatus);
305
                }
306
307
                if (!empty($sController::REQUIRE_SCOPE)) {
308
                    if (!$oUserAccessTokenModel->hasScope($oAccessToken, $sController::REQUIRE_SCOPE)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by Pablo de la Peña
The variable $oAccessToken does not seem to be defined for all execution paths leading up to this point.
Loading history...
309
                        throw new ApiException(
310
                            'Access token with "' . $sController::REQUIRE_SCOPE . '" scope is required.',
311
                            $oHttpCodes::STATUS_UNAUTHORIZED
312
                        );
313
                    }
314
                }
315
316
                //  New instance of the controller
317
                $oInstance = new $sController($this);
318
319
                /**
320
                 * We need to look for the appropriate method; we'll look in the following order:
321
                 *
322
                 * - {sRequestMethod}Remap()
323
                 * - {sRequestMethod}{method}()
324
                 * - anyRemap()
325
                 * - any{method}()
326
                 *
327
                 * The second parameter is whether the method is a remap method or not.
328
                 */
329
                $bDidFindRoute = false;
330
                $aMethods      = [
331
                    [
332
                        strtolower($this->sRequestMethod) . 'Remap',
333
                        true,
334
                    ],
335
                    [
336
                        strtolower($this->sRequestMethod) . ucfirst($this->sMethod),
337
                        false,
338
                    ],
339
                    [
340
                        'anyRemap',
341
                        true,
342
                    ],
343
                    [
344
                        'any' . ucfirst($this->sMethod),
345
                        false,
346
                    ],
347
                ];
348
349
                foreach ($aMethods as $aMethodName) {
350
351
                    $sMethod  = getFromArray(0, $aMethodName);
352
                    $bIsRemap = (bool) getFromArray(1, $aMethodName);
353
                    if (is_callable([$oInstance, $sMethod])) {
354
355
                        $bDidFindRoute = true;
356
357
                        /**
358
                         * If the method we're trying to call is a remap method, then the first
359
                         * param should be the name of the method being called
360
                         */
361
                        if ($bIsRemap) {
362
                            $oResponse = call_user_func_array([$oInstance, $sMethod], [$this->sMethod]);
363
                        } else {
364
                            $oResponse = call_user_func([$oInstance, $sMethod]);
365
                        }
366
                        break;
367
                    }
368
                }
369
370
                if (!$bDidFindRoute) {
371
                    throw new ApiException(
372
                        '"' . $this->sRequestMethod . ': ' . $this->sModuleName . '/' . $this->sClassName . '/' . $this->sMethod . '" is not a valid API route.',
373
                        $oHttpCodes::STATUS_NOT_FOUND
374
                    );
375
                }
376
377
                if (!($oResponse instanceof ApiResponse)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by Pablo de la Peña
The variable $oResponse does not seem to be defined for all execution paths leading up to this point.
Loading history...
378
                    //  This is a misconfiguration error, which we want to bubble up to the error handler
379
                    throw new NailsException(
380
                        'Return object must be an instance of \Nails\Api\Factory\ApiResponse',
381
                        $oHttpCodes::STATUS_INTERNAL_SERVER_ERROR
382
                    );
383
                }
384
385
                $aOut = [
386
                    'status' => $oResponse->getCode(),
387
                    'data'   => $oResponse->getData(),
388
                    'meta'   => $oResponse->getMeta(),
389
                ];
390
391
            } catch (ValidationException $e) {
392
393
                $aOut = [
394
                    'status'  => $e->getCode() ?: $oHttpCodes::STATUS_BAD_REQUEST,
0 ignored issues
show
Comprehensibility Best Practice introduced by Pablo de la Peña
The variable $oHttpCodes does not seem to be defined for all execution paths leading up to this point.
Loading history...
395
                    'error'   => $e->getMessage() ?: 'An unkown validation error occurred',
396
                    'details' => $e->getData() ?: [],
397
                ];
398
                if (isSuperuser()) {
399
                    $aOut['exception'] = (object) array_filter([
400
                        'type' => get_class($e),
401
                        'file' => $e->getFile(),
402
                        'line' => $e->getLine(),
403
                    ]);
404
                }
405
406
                $this->writeLog($aOut);
407
408
            } catch (ApiException $e) {
409
410
                $aOut = [
411
                    'status'  => $e->getCode() ?: $oHttpCodes::STATUS_INTERNAL_SERVER_ERROR,
412
                    'error'   => $e->getMessage() ?: 'An unkown error occurred',
413
                    'details' => $e->getData() ?: [],
414
                ];
415
                if (isSuperuser()) {
416
                    $aOut['exception'] = (object) array_filter([
417
                        'type' => get_class($e),
418
                        'file' => $e->getFile(),
419
                        'line' => $e->getLine(),
420
                    ]);
421
                }
422
423
                $this->writeLog($aOut);
424
425
            } catch (\Exception $e) {
426
                /**
427
                 * When running in PRODUCTION we want the global error handler to catch exceptions so that they
428
                 * can be handled proeprly and reported if necessary. In other environments we want to show the
429
                 * developer the error quickly and with as much info as possible.
430
                 */
431
                if (Environment::is(Environment::ENV_PROD)) {
432
                    throw $e;
433
                } else {
434
                    $aOut = [
435
                        'status'    => $e->getCode() ?: $oHttpCodes::STATUS_INTERNAL_SERVER_ERROR,
436
                        'error'     => $e->getMessage() ?: 'An unkown error occurred',
437
                        'exception' => (object) array_filter([
438
                            'type' => get_class($e),
439
                            'file' => $e->getFile(),
440
                            'line' => $e->getLine(),
441
                        ]),
442
                    ];
443
444
                    $this->writeLog($aOut);
445
                }
446
            }
447
448
            $this->output($aOut);
449
        }
450
    }
451
452
    // --------------------------------------------------------------------------
453
454
    /**
455
     * Sends $aOut to the browser in the desired format
456
     *
457
     * @param array $aOut The data to output to the browser
458
     */
459
    protected function output($aOut = [])
460
    {
461
        /** @var Input $oInput */
462
        $oInput = Factory::service('Input');
463
        /** @var Output $oOutput */
464
        $oOutput = Factory::service('Output');
465
        /** @var HttpCodes $oHttpCodes */
466
        $oHttpCodes = Factory::service('HttpCodes');
467
468
        //  Set cache headers
469
        $oOutput->set_header('Cache-Control: no-store, no-cache, must-revalidate');
470
        $oOutput->set_header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
471
        $oOutput->set_header('Pragma: no-cache');
472
473
        //  Set access control headers
474
        $this->setCorsHeaders();
475
476
        // --------------------------------------------------------------------------
477
478
        //  Send the correct status header, default to 200 OK
479
        if ($this->bOutputSendHeader) {
480
            $sProtocol   = $oInput->server('SERVER_PROTOCOL');
481
            $iHttpCode   = getFromArray('status', $aOut, $oHttpCodes::STATUS_OK);
482
            $sHttpString = $oHttpCodes::getByCode($iHttpCode);
0 ignored issues
show
Bug introduced by Pablo de la Peña
Are you sure the assignment to $sHttpString is correct as $oHttpCodes::getByCode($iHttpCode) targeting Nails\Common\Service\HttpCodes::getByCode() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
483
            $oOutput->set_header($sProtocol . ' ' . $iHttpCode . ' ' . $sHttpString);
0 ignored issues
show
Bug introduced by Pablo de la Peña
Are you sure $sProtocol of type array|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

483
            $oOutput->set_header(/** @scrutinizer ignore-type */ $sProtocol . ' ' . $iHttpCode . ' ' . $sHttpString);
Loading history...
484
        }
485
486
        // --------------------------------------------------------------------------
487
488
        //  Output content
489
        switch ($this->sOutputFormat) {
490
            case static::FORMAT_TXT:
491
                $sOut = $this->outputTxt($aOut);
492
                break;
493
494
            case static::FORMAT_JSON:
495
                $sOut = $this->outputJson($aOut);
496
                break;
497
        }
498
499
        $oOutput->set_output($sOut);
0 ignored issues
show
Comprehensibility Best Practice introduced by Pablo de la Peña
The variable $sOut does not seem to be defined for all execution paths leading up to this point.
Loading history...
500
    }
501
502
    // --------------------------------------------------------------------------
503
504
    /**
505
     * Sets CORS headers
506
     *
507
     * @throws \Nails\Common\Exception\FactoryException
508
     */
509
    protected function setCorsHeaders()
510
    {
511
        /** @var Output $oOutput */
512
        $oOutput = Factory::service('Output');
513
        $oOutput->set_header('Access-Control-Allow-Origin: ' . static::ACCESS_CONTROL_ALLOW_ORIGIN);
514
        $oOutput->set_header('Access-Control-Allow-Credentials: ' . static::ACCESS_CONTROL_ALLOW_CREDENTIALS);
515
        $oOutput->set_header('Access-Control-Allow-Headers: ' . implode(', ', static::ACCESS_CONTROL_ALLOW_HEADERS));
516
        $oOutput->set_header('Access-Control-Allow-Methods: ' . implode(', ', static::ACCESS_CONTROL_ALLOW_METHODS));
517
        $oOutput->set_header('Access-Control-Max-Age: ' . static::ACCESS_CONTROL_MAX_AGE);
518
    }
519
520
    // --------------------------------------------------------------------------
521
522
    /**
523
     * Formats $aOut as a plain text string formatted as JSON (for easy reading)
524
     * but a plaintext contentType
525
     *
526
     * @param array $aOut The result of the API call
527
     *
528
     * @return string
529
     */
530
    private function outputTxt($aOut)
531
    {
532
        /** @var Output $oOutput */
533
        $oOutput = Factory::service('Output');
534
        $oOutput->set_content_type('text/html');
535
        if (Environment::not(Environment::ENV_PROD) && defined('JSON_PRETTY_PRINT')) {
536
            return json_encode($aOut, JSON_PRETTY_PRINT);
537
        } else {
538
            return json_encode($aOut);
539
        }
540
    }
541
542
    // --------------------------------------------------------------------------
543
544
    /**
545
     * Formats $aOut as a JSON string
546
     *
547
     * @param array $aOut The result of the API call
548
     *
549
     * @return string
550
     */
551
    private function outputJson($aOut)
552
    {
553
        /** @var Output $oOutput */
554
        $oOutput = Factory::service('Output');
555
        $oOutput->set_content_type('application/json');
556
        if (Environment::not(Environment::ENV_PROD) && defined('JSON_PRETTY_PRINT')) {
557
            return json_encode($aOut, JSON_PRETTY_PRINT);
558
        } else {
559
            return json_encode($aOut);
560
        }
561
    }
562
563
    // --------------------------------------------------------------------------
564
565
    /**
566
     * Sets the output format
567
     *
568
     * @param string $sFormat The format to use
569
     *
570
     * @return bool
571
     */
572
    public function outputSetFormat($sFormat): bool
573
    {
574
        if (static::isValidFormat($sFormat)) {
575
            $this->sOutputFormat = strtoupper($sFormat);
576
            return true;
577
        }
578
579
        return false;
580
    }
581
582
    // --------------------------------------------------------------------------
583
584
    /**
585
     * Sets whether the status header should be sent or not
586
     *
587
     * @param bool $sendHeader Whether the header should be sent or not
588
     */
589
    public function outputSendHeader($sendHeader): bool
590
    {
591
        $this->bOutputSendHeader = !empty($sendHeader);
592
    }
593
594
    // --------------------------------------------------------------------------
595
596
    /**
597
     * Determines whether the format is valid
598
     *
599
     * @param string $sFormat The format to check
600
     *
601
     * @return bool
602
     */
603
    private static function isValidFormat($sFormat): bool
604
    {
605
        return in_array(strtoupper($sFormat), static::VALID_FORMATS);
606
    }
607
608
    // --------------------------------------------------------------------------
609
610
    /**
611
     * Write a line to the API log
612
     *
613
     * @param string $sLine The line to write
614
     */
615
    public function writeLog($sLine)
616
    {
617
        if (!is_string($sLine)) {
0 ignored issues
show
introduced by Pablo de la Peña
The condition is_string($sLine) is always true.
Loading history...
618
            $sLine = print_r($sLine, true);
619
        }
620
        $sLine = ' [' . $this->sModuleName . '->' . $this->sMethod . '] ' . $sLine;
621
        $this->oLogger->line($sLine);
622
    }
623
624
    // --------------------------------------------------------------------------
625
626
    /**
627
     * Returns the current request method
628
     *
629
     * @return string
630
     */
631
    public function getRequestMethod(): string
632
    {
633
        return $this->sRequestMethod;
634
    }
635
636
    // --------------------------------------------------------------------------
637
638
    /**
639
     * Determines whether the request is a GET request
640
     *
641
     * @return bool
642
     */
643
    public function isGetRequest(): bool
644
    {
645
        return $this->getRequestMethod() === static::REQUEST_METHOD_GET;
646
    }
647
648
    // --------------------------------------------------------------------------
649
650
    /**
651
     * Determines whether the request is a PUT request
652
     *
653
     * @return bool
654
     */
655
    public function isPutRequest(): bool
656
    {
657
        return $this->getRequestMethod() === static::REQUEST_METHOD_PUT;
658
    }
659
660
    // --------------------------------------------------------------------------
661
662
    /**
663
     * Determines whether the request is a POST request
664
     *
665
     * @return bool
666
     */
667
    public function isPostRequest(): bool
668
    {
669
        return $this->getRequestMethod() === static::REQUEST_METHOD_POST;
670
    }
671
672
    // --------------------------------------------------------------------------
673
674
    /**
675
     * Determines whether the request is a DELETE request
676
     *
677
     * @return bool
678
     */
679
    public function isDeleteRequest(): bool
680
    {
681
        return $this->getRequestMethod() === static::REQUEST_METHOD_DELETE;
682
    }
683
684
    // --------------------------------------------------------------------------
685
686
    /**
687
     * Returns the current Access Token
688
     *
689
     * @return string|null
690
     */
691
    public function getAccessToken(): ?string
692
    {
693
        return $this->sAccessToken;
694
    }
695
}
696