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