Completed
Push — master ( 8ee32c...65b5b3 )
by Marcin
29s queued 11s
created

ResponseBuilder   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 509
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 29
Bugs 0 Features 0
Metric Value
eloc 115
dl 0
loc 509
ccs 94
cts 94
cp 1
rs 9.84
c 29
b 0
f 0
wmc 32

16 Methods

Rating   Name   Duplication   Size   Complexity  
A errorWithMessage() 0 3 1
A errorWithDataAndHttpCode() 0 4 1
A successWithHttpCode() 0 3 1
A buildResponse() 0 36 5
A errorWithMessageAndDataAndDebug() 0 6 1
A success() 0 4 1
A errorWithMessageAndData() 0 5 1
A errorWithData() 0 4 1
A buildSuccessResponse() 0 11 1
A getClassesMapping() 0 26 6
A getMessageForApiCode() 0 17 4
A make() 0 20 3
A buildErrorResponse() 0 27 3
A successWithCode() 0 4 1
A errorWithHttpCode() 0 3 1
A error() 0 4 1
1
<?php
2
declare(strict_types=1);
3
4
namespace MarcinOrlowski\ResponseBuilder;
5
6
/**
7
 * Laravel API Response Builder
8
 *
9
 * @package   MarcinOrlowski\ResponseBuilder
10
 *
11
 * @author    Marcin Orlowski <mail (#) marcinOrlowski (.) com>
12
 * @copyright 2016-2019 Marcin Orlowski
13
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
14
 * @link      https://github.com/MarcinOrlowski/laravel-api-response-builder
15
 */
16
17
use Illuminate\Support\Facades\Config;
18
use Illuminate\Support\Facades\Response;
19
use Symfony\Component\HttpFoundation\Response as HttpResponse;
20
21
22
/**
23
 * Builds standardized HttpResponse response object
24
 */
25
class ResponseBuilder
26
{
27
    /**
28
     * Default HTTP code to be used with success responses
29
     *
30
     * @var int
31
     */
32
    public const DEFAULT_HTTP_CODE_OK = HttpResponse::HTTP_OK;
33
34
    /**
35
     * Default HTTP code to be used with error responses
36
     *
37
     * @var int
38
     */
39
    public const DEFAULT_HTTP_CODE_ERROR = HttpResponse::HTTP_BAD_REQUEST;
40
41
    /**
42
     * Min allowed HTTP code for errorXXX()
43
     *
44
     * @var int
45
     */
46
    public const ERROR_HTTP_CODE_MIN = 400;
47
48
    /**
49
     * Max allowed HTTP code for errorXXX()
50
     *
51
     * @var int
52
     */
53
    public const ERROR_HTTP_CODE_MAX = 599;
54
55
    /**
56
     * Configuration keys
57
     */
58
    public const CONF_KEY_DEBUG_DEBUG_KEY        = 'response_builder.debug.debug_key';
59
    public const CONF_KEY_DEBUG_EX_TRACE_ENABLED = 'response_builder.debug.exception_handler.trace_enabled';
60
    public const CONF_KEY_DEBUG_EX_TRACE_KEY     = 'response_builder.debug.exception_handler.trace_key';
61
    public const CONF_KEY_MAP                    = 'response_builder.map';
62
    public const CONF_KEY_ENCODING_OPTIONS       = 'response_builder.encoding_options';
63
    public const CONF_KEY_CLASSES                = 'response_builder.classes';
64
    public const CONF_KEY_MIN_CODE               = 'response_builder.min_code';
65
    public const CONF_KEY_MAX_CODE               = 'response_builder.max_code';
66
    public const CONF_KEY_RESPONSE_KEY_MAP       = 'response_builder.map';
67
    public const CONF_EXCEPTION_HANDLER_KEY      = 'response_builder.exception_handler';
68
69
    /**
70
     * Default keys to be used by exception handler while adding debug information
71
     */
72
    public const KEY_DEBUG   = 'debug';
73
    public const KEY_TRACE   = 'trace';
74
    public const KEY_CLASS   = 'class';
75
    public const KEY_FILE    = 'file';
76
    public const KEY_LINE    = 'line';
77
    public const KEY_KEY     = 'key';
78
    public const KEY_METHOD  = 'method';
79
    public const KEY_SUCCESS = 'success';
80
    public const KEY_CODE    = 'code';
81
    public const KEY_LOCALE  = 'locale';
82
    public const KEY_MESSAGE = 'message';
83
    public const KEY_DATA    = 'data';
84
85
    /**
86
     * Default key to be used by exception handler while processing ValidationException
87
     * to return all the error messages
88
     *
89
     * @var string
90
     */
91
    public const KEY_MESSAGES = 'messages';
92
93
    /**
94
     * Default JSON encoding options. Must be specified as final value (i.e. 271) and NOT
95
     * PHP expression i.e. `JSON_HEX_TAG|JSON_HEX_APOS|...` as such syntax is not yet supported
96
     * by PHP.
97
     *
98
     * 271 = JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_AMP|JSON_HEX_QUOT|JSON_UNESCAPED_UNICODE
99 4
     *
100
     * @var int
101 4
     */
102
    public const DEFAULT_ENCODING_OPTIONS = 271;
103 4
104 3
    /**
105 2
     * Reads and validates "classes" config mapping
106 2
     *
107
     * @return array Classes mapping as specified in configuration or empty array if configuration found
108
     *
109
     * @throws \RuntimeException if "classes" mapping is technically invalid (i.e. not array etc).
110 1
     */
111 1
    protected static function getClassesMapping(): ?array
112
    {
113 1
        $classes = Config::get(self::CONF_KEY_CLASSES);
114 1
115 1
        if ($classes !== null) {
116 1
            if (!is_array($classes)) {
117
                throw new \RuntimeException(
118
                    sprintf('CONFIG: "classes" mapping must be an array (%s given)', gettype($classes)));
119
            }
120
121 1
            $mandatory_keys = [
122
                static::KEY_KEY,
123
                static::KEY_METHOD,
124 1
            ];
125
            foreach ($classes as $class_name => $class_config) {
126
                foreach ($mandatory_keys as $key_name) {
127
                    if (!array_key_exists($key_name, $class_config)) {
128
                        throw new \RuntimeException("CONFIG: Missing '{$key_name}' for '{$class_name}' class mapping");
129
                    }
130
                }
131
            }
132
        } else {
133
            $classes = [];
134
        }
135
136
        return $classes;
137
    }
138
139 12
    /**
140
     * Returns success
141
     *
142 12
     * @param object|array|null $data          Array of primitives and supported objects to be returned in 'data' node
143
     *                                         of the JSON response, single supported object or @null if there's no
144
     *                                         to be returned.
145
     * @param integer|null      $api_code      API code to be returned or @null to use value of BaseApiCodes::OK().
146
     * @param array|null        $placeholders  Placeholders passed to Lang::get() for message placeholders
147
     *                                         substitution or @null if none.
148
     * @param integer|null      $http_code     HTTP code to be used for HttpResponse sent or @null
149
     *                                         for default DEFAULT_HTTP_CODE_OK.
150
     * @param integer|null      $json_opts     See http://php.net/manual/en/function.json-encode.php for supported
151
     *                                         options or pass @null to use value from your config (or defaults).
152
     *
153
     * @return HttpResponse
154 1
     */
155
    public static function success($data = null, $api_code = null, array $placeholders = null,
156 1
                                   int $http_code = null, int $json_opts = null): HttpResponse
157
    {
158
        return static::buildSuccessResponse($data, $api_code, $placeholders, $http_code, $json_opts);
159
    }
160
161
    /**
162
     * Returns success
163
     *
164
     * @param integer|null $api_code      API code to be returned or @null to use value of BaseApiCodes::OK().
165
     * @param array|null   $placeholders  Placeholders passed to Lang::get() for message placeholders
166
     *                                    substitution or @null if none.
167 4
     * @param integer|null $http_code     HTTP code to be used for HttpResponse sent or @null
168
     *                                    for default DEFAULT_HTTP_CODE_OK.
169 4
     *
170
     * @return HttpResponse
171
     */
172
    public static function successWithCode(int $api_code = null, array $placeholders = null,
173
                                           int $http_code = null): HttpResponse
174
    {
175
        return static::success(null, $api_code, $placeholders, $http_code);
176
    }
177
178
    /**
179
     * Returns success with custom HTTP code
180
     *
181
     * @param integer|null $http_code HTTP return code to be set for this response. If @null is passed, falls back
182
     *                                to DEFAULT_HTTP_CODE_OK.
183
     *
184 16
     * @return HttpResponse
185
     */
186
    public static function successWithHttpCode(int $http_code = null): HttpResponse
187 16
    {
188 16
        return static::buildSuccessResponse(null, BaseApiCodes::OK(), [], $http_code);
189
    }
190 16
191 16
    /**
192 16
     * @param object|array|null $data          Array of primitives and supported objects to be returned in 'data' node.
193
     *                                         of the JSON response, single supported object or @null if there's no
194 14
     *                                         to be returned.
195
     * @param integer|null      $api_code      API code to be returned or @null to use value of BaseApiCodes::OK().
196
     * @param array|null        $placeholders  Placeholders passed to Lang::get() for message placeholders
197
     *                                         substitution or @null if none.
198
     * @param integer|null      $http_code     HTTP code to be used for HttpResponse sent or @null
199
     *                                         for default DEFAULT_HTTP_CODE_OK.
200
     * @param integer|null      $json_opts     See http://php.net/manual/en/function.json-encode.php for supported
201
     *                                         options or pass @null to use value from your config (or defaults).
202
     *
203
     * @return HttpResponse
204
     *
205
     * @throws \InvalidArgumentException Thrown when provided arguments are invalid.
206
     */
207
    protected static function buildSuccessResponse($data = null, int $api_code = null, array $placeholders = null,
208
                                                   int $http_code = null, int $json_opts = null): HttpResponse
209
    {
210 4
        $http_code = $http_code ?? static::DEFAULT_HTTP_CODE_OK;
211
        $api_code = $api_code ?? BaseApiCodes::OK();
212
213 4
        Validator::assertInt('api_code', $api_code);
214
        Validator::assertInt('http_code', $http_code);
215
        Validator::assertIntRange('http_code', $http_code, 200, 299);
216
217
        return static::make(true, $api_code, $api_code, $data, $http_code, $placeholders, null, $json_opts);
218
    }
219
220
    /**
221
     * Builds error Response object. Supports optional arguments passed to Lang::get() if associated error
222
     * message uses placeholders as well as return data payload
223
     *
224
     * @param integer           $api_code      Your API code to be returned with the response object.
225 1
     * @param array|null        $placeholders  Placeholders passed to Lang::get() for message placeholders
226
     *                                         substitution or @null if none.
227
     * @param object|array|null $data          Array of primitives and supported objects to be returned in 'data' node
228 1
     *                                         of the JSON response, single supported object or @null if there's no
229
     *                                         to be returned.
230
     * @param integer|null      $http_code     HTTP code to be used for HttpResponse sent or @null
231
     *                                         for default DEFAULT_HTTP_CODE_ERROR.
232
     * @param integer|null      $json_opts     See http://php.net/manual/en/function.json-encode.php for supported
233
     *                                         options or pass @null to use value from your config (or defaults).
234
     *
235
     * @return HttpResponse
236
     */
237
    public static function error(int $api_code, array $placeholders = null, $data = null, int $http_code = null,
238
                                 int $encoding_options = null): HttpResponse
239
    {
240
        return static::buildErrorResponse($data, $api_code, $http_code, $placeholders, $encoding_options);
241
    }
242
243 1
    /**
244
     * @param integer           $api_code      Your API code to be returned with the response object.
245
     * @param object|array|null $data          Array of primitives and supported objects to be returned in 'data' node
246 1
     *                                         of the JSON response, single supported object or @null if there's no
247
     *                                         to be returned.
248
     * @param array|null        $placeholders  Placeholders passed to Lang::get() for message placeholders
249
     *                                         substitution or @null if none.
250
     * @param integer|null      $json_opts     See http://php.net/manual/en/function.json-encode.php for supported
251
     *                                         options or pass @null to use value from your config (or defaults).
252
     *
253
     * @return HttpResponse
254
     */
255
    public static function errorWithData(int $api_code, $data, array $placeholders = null,
256
                                         int $json_opts = null): HttpResponse
257
    {
258 1
        return static::buildErrorResponse($data, $api_code, null, $placeholders, $json_opts);
259
    }
260 1
261
    /**
262
     * @param integer           $api_code      Your API code to be returned with the response object.
263
     * @param object|array|null $data          Array of primitives and supported objects to be returned in 'data' node
264
     *                                         of the JSON response, single supported object or @null if there's no
265
     *                                         to be returned.
266
     * @param integer           $http_code     HTTP code to be used for HttpResponse sent.
267
     * @param array|null        $placeholders  Placeholders passed to Lang::get() for message placeholders
268
     *                                         substitution or @null if none.
269
     * @param integer|null      $json_opts     See http://php.net/manual/en/function.json-encode.php for supported
270
     *                                         options or pass @null to use value from your config (or defaults).
271
     *
272
     * @return HttpResponse
273 1
     *
274
     * @throws \InvalidArgumentException if http_code is @null
275
     */
276 1
    public static function errorWithDataAndHttpCode(int $api_code, $data, int $http_code, array $placeholders = null,
277 1
                                                    int $json_opts = null): HttpResponse
278
    {
279
        return static::buildErrorResponse($data, $api_code, $http_code, $placeholders, $json_opts);
280
    }
281
282
    /**
283
     * @param integer    $api_code     Your API code to be returned with the response object.
284
     * @param integer    $http_code    HTTP code to be used for HttpResponse sent or @null
285
     *                                 for default DEFAULT_HTTP_CODE_ERROR.
286
     * @param array|null $placeholders Placeholders passed to Lang::get() for message placeholders
287
     *                                 substitution or @null if none.
288
     *
289
     * @return HttpResponse
290
     *
291 6
     * @throws \InvalidArgumentException if http_code is @null
292
     */
293
    public static function errorWithHttpCode(int $api_code, int $http_code, array $placeholders = null): HttpResponse
294
    {
295 6
        return static::buildErrorResponse(null, $api_code, $http_code, $placeholders);
296 6
    }
297
298
    /**
299
     * @param integer           $api_code  Your API code to be returned with the response object.
300
     * @param string            $message   Custom message to be returned as part of error response
301
     * @param object|array|null $data      Array of primitives and supported objects to be returned in 'data' node
302
     *                                     of the JSON response, single supported object or @null if there's no
303
     *                                     to be returned.
304
     * @param integer|null      $http_code Optional HTTP status code to be used for HttpResponse sent
305
     *                                     or @null for DEFAULT_HTTP_CODE_ERROR
306 1
     * @param integer|null      $json_opts See http://php.net/manual/en/function.json-encode.php for supported
307
     *                                     options or pass @null to use value from your config (or defaults).
308 1
     *
309
     * @return HttpResponse
310
     */
311
    public static function errorWithMessageAndData(int $api_code, string $message, $data,
312
                                                   int $http_code = null, int $json_opts = null): HttpResponse
313
    {
314
        return static::buildErrorResponse($data, $api_code, $http_code, null,
315
            $message, null, $json_opts);
316
    }
317
318
    /**
319
     * @param integer           $api_code   Your API code to be returned with the response object.
320
     * @param string            $message    custom message to be returned as part of error response
321
     * @param object|array|null $data       Array of primitives and supported objects to be returned in 'data' node
322
     *                                      of the JSON response, single supported object or @null if there's no
323
     *                                      to be returned.
324
     * @param integer|null      $http_code  HTTP code to be used for HttpResponse sent or @null
325
     *                                      for default DEFAULT_HTTP_CODE_ERROR.
326
     * @param integer|null      $json_opts  See http://php.net/manual/en/function.json-encode.php for supported
327
     *                                      options or pass @null to use value from your config (or defaults).
328
     * @param array|null        $debug_data optional debug data array to be added to returned JSON.
329
     *
330
     * @return HttpResponse
331 18
     */
332
    public static function errorWithMessageAndDataAndDebug(int $api_code, string $message, $data,
333
                                                           int $http_code = null, int $json_opts = null,
334
                                                           array $debug_data = null): HttpResponse
335 18
    {
336 18
        return static::buildErrorResponse($data, $api_code, $http_code, null,
337
            $message, null, $json_opts, $debug_data);
338 18
    }
339
340 18
    /**
341 18
     * @param integer      $api_code  Your API code to be returned with the response object.
342 16
     * @param string       $message   Custom message to be returned as part of error response
343
     * @param integer|null $http_code HTTP code to be used with final response sent or @null
344 18
     *                                for default DEFAULT_HTTP_CODE_ERROR.
345 2
     *
346
     * @return HttpResponse
347
     */
348 16
    public static function errorWithMessage(int $api_code, string $message, int $http_code = null): HttpResponse
349 16
    {
350
        return static::buildErrorResponse(null, $api_code, $http_code, null, $message);
351 15
    }
352
353 15
    /**
354 15
     * Builds error Response object. Supports optional arguments passed to Lang::get() if associated error message
355
     * uses placeholders as well as return data payload
356
     *
357
     * @param object|array|null $data          Array of primitives and supported objects to be returned in 'data' node
358
     *                                         of the JSON response, single supported object or @null if there's no
359
     *                                         to be returned.
360
     * @param integer           $api_code      Your API code to be returned with the response object.
361
     * @param integer|null      $http_code     HTTP code to be used for HttpResponse sent or @null
362
     *                                         for default DEFAULT_HTTP_CODE_ERROR.
363
     * @param array|null        $placeholders  Placeholders passed to Lang::get() for message placeholders
364
     *                                         substitution or @null if none.
365
     * @param string|null       $message       custom message to be returned as part of error response
366
     * @param array|null        $headers       optional HTTP headers to be returned in error response
367
     * @param integer|null      $json_opts     See http://php.net/manual/en/function.json-encode.php for supported
368
     *                                         options or pass @null to use value from your config (or defaults).
369
     * @param array|null        $debug_data    optional debug data array to be added to returned JSON.
370
     *
371
     * @return HttpResponse
372
     *
373
     * @throws \InvalidArgumentException Thrown if $code is not correct, outside the range, equals OK code etc.
374
     *
375 35
     * @noinspection MoreThanThreeArgumentsInspection
376
     */
377
    protected static function buildErrorResponse($data, int $api_code, int $http_code = null,
378
                                                 array $placeholders = null,
379 35
                                                 string $message = null, array $headers = null,
380 35
                                                 int $json_opts = null,
381 35
                                                 array $debug_data = null): HttpResponse
382
    {
383 35
        $http_code = $http_code ?? static::DEFAULT_HTTP_CODE_ERROR;
384
        $headers = $headers ?? [];
385 34
386 34
        Validator::assertInt('api_code', $api_code);
387 1
388 1
        $code_ok = BaseApiCodes::OK();
389 1
        if ($api_code !== $code_ok) {
390
            Validator::assertIntRange('api_code', $api_code, BaseApiCodes::getMinCode(), BaseApiCodes::getMaxCode());
391
        }
392 33
        if ($api_code === $code_ok) {
393 2
            throw new \InvalidArgumentException(
394 2
                "Error response cannot use api_code of value  {$code_ok} which is reserved for OK");
395 2
        }
396
397
        Validator::assertInt('http_code', $http_code);
398
        Validator::assertIntRange('http_code', $http_code, static::ERROR_HTTP_CODE_MIN, static::ERROR_HTTP_CODE_MAX);
399
400
        $msg_or_api_code = $message ?? $api_code;
401 31
402 23
        return static::make(false, $api_code, $msg_or_api_code, $data, $http_code,
403 23
            $placeholders, $headers, $json_opts, $debug_data);
404
    }
405 2
406
    /**
407
     * @param boolean           $success         @true if response reports successful operation, @false otherwise.
408 23
     * @param integer           $api_code        Your API code to be returned with the response object.
409 23
     * @param string|integer    $msg_or_api_code message string or valid API code to get message for
410
     * @param object|array|null $data            optional additional data to be included in response object
411
     * @param integer|null      $http_code       HTTP code for the HttpResponse or @null for either DEFAULT_HTTP_CODE_OK
412 31
     *                                           or DEFAULT_HTTP_CODE_ERROR depending on the $success.
413 31
     * @param array|null        $placeholders    Placeholders passed to Lang::get() for message placeholders
414 28
     *                                           substitution or @null if none.
415
     * @param array|null        $headers         Optional HTTP headers to be returned in the response.
416
     * @param integer|null      $json_opts       See http://php.net/manual/en/function.json-encode.php for supported
417
     *                                           options or pass @null to use value from your config (or defaults).
418
     * @param array|null        $debug_data      Optional debug data array to be added to returned JSON.
419
     *
420
     * @return HttpResponse
421
     *
422
     * @throws \InvalidArgumentException If $api_code is neither a string nor valid integer code.
423
     * @throws \InvalidArgumentException if $data is an object of class that is not configured in "classes" mapping.
424
     *
425
     * @noinspection MoreThanThreeArgumentsInspection
426
     */
427
    protected static function make(bool $success, int $api_code, $msg_or_api_code, $data = null,
428
                                   int $http_code = null, array $placeholders = null, array $headers = null,
429
                                   int $json_opts = null, array $debug_data = null): HttpResponse
430
    {
431
        $headers = $headers ?? [];
432 31
        $http_code = $http_code ?? ($success ? static::DEFAULT_HTTP_CODE_OK : static::DEFAULT_HTTP_CODE_ERROR);
433
        $json_opts = $json_opts ?? Config::get(self::CONF_KEY_ENCODING_OPTIONS, static::DEFAULT_ENCODING_OPTIONS);
434
435
        Validator::assertInt('encoding_options', $json_opts);
436 31
437
        Validator::assertInt('api_code', $api_code);
438 31
        if (!BaseApiCodes::isCodeValid($api_code)) {
439 28
            $min = BaseApiCodes::getMinCode();
440
            $max = BaseApiCodes::getMaxCode();
441 12
            throw new \InvalidArgumentException("API code value ({$api_code}) is out of allowed range {$min}-{$max}");
442
        }
443
444
        return Response::json(
445
            static::buildResponse($success, $api_code, $msg_or_api_code, $placeholders, $data, $debug_data),
446 28
            $http_code, $headers, $json_opts);
447 28
    }
448 28
449 28
    /**
450 28
     * If $msg_or_api_code is integer value, returns human readable message associated with that code (with
451
     * fallback to built-in default string if no api code mapping is set. If $msg_or_api_code is a string,
452
     * returns it unaltered.
453 28
     *
454 2
     * @param boolean    $success      @true if response reports successful operation, @false otherwise.
455 2
     * @param integer    $api_code     Your API code to be returned with the response object.
456
     * @param array|null $placeholders Placeholders passed to Lang::get() for message placeholders
457
     *                                 substitution or @null if none.
458 28
     *
459
     * @return string
460
     */
461
    protected static function getMessageForApiCode(bool $success, int $api_code, array $placeholders = null): string
462
    {
463
        // We got integer value here not a message string, so we need to check if we have the mapping for
464
        // this string already configured.
465
        $key = BaseApiCodes::getCodeMessageKey($api_code);
466
        if ($key === null) {
467
            // nope, let's get the default one instead, based of
468
            $fallback_code = $success ? BaseApiCodes::OK() : BaseApiCodes::NO_ERROR_MESSAGE();
469
            $key = BaseApiCodes::getCodeMessageKey($fallback_code);
470
        }
471
472
        $placeholders = $placeholders ?? [];
473
        if (!array_key_exists('api_code', $placeholders)) {
474
            $placeholders['api_code'] = $api_code;
475
        }
476
477
        return \Lang::get($key, $placeholders);
478
    }
479
480
    /**
481
     * Creates standardised API response array. This is final method called in the whole pipeline before we
482
     * return final JSON back to client. If you want to manipulate your response, this is the place to do that.
483
     * If you set APP_DEBUG to true, 'code_hex' field will be additionally added to reported JSON for easier
484
     * manual debugging.
485
     *
486
     * @param boolean           $success         @true if response reports successful operation, @false otherwise.
487
     * @param integer           $api_code        Your API code to be returned with the response object.
488
     * @param string|integer    $msg_or_api_code Message string or valid API code to get message for.
489
     * @param array|null        $placeholders    Placeholders passed to Lang::get() for message placeholders
490
     *                                           substitution or @null if none.
491
     * @param object|array|null $data            API response data if any
492
     * @param array|null        $debug_data      optional debug data array to be added to returned JSON.
493
     *
494
     * @return array response ready to be encoded as json and sent back to client
495
     *
496
     * @throws \RuntimeException in case of missing or invalid "classes" mapping configuration
497
     */
498
    protected static function buildResponse(bool $success, int $api_code,
499
                                            $msg_or_api_code, array $placeholders = null,
500
                                            $data = null, array $debug_data = null): array
501
    {
502
        // ensure $data is either @null, array or object of class with configured mapping.
503
        $converter = new Converter();
504
505
        $data = $converter->convert($data);
506
        if ($data !== null && !is_object($data)) {
507
            // ensure we get object in final JSON structure in data node
508
            $data = (object)$data;
509
        }
510
511
        // get human readable message for API code or use message string (if given instead of API code)
512
        if (is_int($msg_or_api_code)) {
513
            $message = self::getMessageForApiCode($success, $msg_or_api_code, $placeholders);
514
        } else {
515
            Validator::assertString('message', $msg_or_api_code);
516
            $message = $msg_or_api_code;
517
        }
518
519
        /** @noinspection PhpUndefinedClassInspection */
520
        $response = [
521
            static::KEY_SUCCESS => $success,
522
            static::KEY_CODE    => $api_code,
523
            static::KEY_LOCALE  => \App::getLocale(),
524
            static::KEY_MESSAGE => $message,
525
            static::KEY_DATA    => $data,
526
        ];
527
528
        if ($debug_data !== null) {
529
            $debug_key = Config::get(static::CONF_KEY_DEBUG_DEBUG_KEY, self::KEY_DEBUG);
530
            $response[ $debug_key ] = $debug_data;
531
        }
532
533
        return $response;
534
    }
535
536
}
537