Completed
Pull Request — master (#91)
by Chad
01:16
created

Filterer::handleFilterAliases()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 4
nc 2
nop 2
1
<?php
2
3
namespace TraderInteractive;
4
5
use Exception;
6
use InvalidArgumentException;
7
use Throwable;
8
use TraderInteractive\Exceptions\FilterException;
9
10
/**
11
 * Class to filter an array of input.
12
 */
13
final class Filterer implements FiltererInterface
14
{
15
    /**
16
     * @var array
17
     */
18
    const DEFAULT_FILTER_ALIASES = [
19
        'array' => '\\TraderInteractive\\Filter\\Arrays::filter',
20
        'arrayize' => '\\TraderInteractive\\Filter\\Arrays::arrayize',
21
        'bool' => '\\TraderInteractive\\Filter\\Booleans::filter',
22
        'bool-convert' => '\\TraderInteractive\\Filter\\Booleans::convert',
23
        'compress-string' => '\\TraderInteractive\\Filter\\Strings::compress',
24
        'concat' => '\\TraderInteractive\\Filter\\Strings::concat',
25
        'date' => '\\TraderInteractive\\Filter\\DateTime::filter',
26
        'date-format' => '\\TraderInteractive\\Filter\\DateTime::format',
27
        'email' => '\\TraderInteractive\\Filter\\Email::filter',
28
        'explode' => '\\TraderInteractive\\Filter\\Strings::explode',
29
        'flatten' => '\\TraderInteractive\\Filter\\Arrays::flatten',
30
        'float' => '\\TraderInteractive\\Filter\\Floats::filter',
31
        'in' => '\\TraderInteractive\\Filter\\Arrays::in',
32
        'int' => '\\TraderInteractive\\Filter\\Ints::filter',
33
        'ofArray' => '\\TraderInteractive\\Filterer::ofArray',
34
        'ofArrays' => '\\TraderInteractive\\Filterer::ofArrays',
35
        'ofScalars' => '\\TraderInteractive\\Filterer::ofScalars',
36
        'redact' => '\\TraderInteractive\\Filter\\Strings::redact',
37
        'string' => '\\TraderInteractive\\Filter\\Strings::filter',
38
        'strip-tags' => '\\TraderInteractive\\Filter\\Strings::stripTags',
39
        'timezone' => '\\TraderInteractive\\Filter\\DateTimeZone::filter',
40
        'translate' => '\\TraderInteractive\\Filter\\Strings::translate',
41
        'uint' => '\\TraderInteractive\\Filter\\UnsignedInt::filter',
42
        'url' => '\\TraderInteractive\\Filter\\Url::filter',
43
    ];
44
45
    /**
46
     * @var array
47
     */
48
    const DEFAULT_OPTIONS = [
49
        FiltererOptions::ALLOW_UNKNOWNS => false,
50
        FiltererOptions::DEFAULT_REQUIRED => false,
51
        FiltererOptions::RESPONSE_TYPE => self::RESPONSE_TYPE_ARRAY,
52
    ];
53
54
    /**
55
     * @var string
56
     */
57
    const RESPONSE_TYPE_ARRAY = 'array';
58
59
    /**
60
     * @var string
61
     */
62
    const RESPONSE_TYPE_FILTER = FilterResponse::class;
63
64
    /**
65
     * @var array
66
     */
67
    private static $registeredFilterAliases = self::DEFAULT_FILTER_ALIASES;
68
69
    /**
70
     * @var array|null
71
     */
72
    private $filterAliases;
73
74
    /**
75
     * @var array
76
     */
77
    private $specification;
78
79
    /**
80
     * @var bool
81
     */
82
    private $allowUnknowns;
83
84
    /**
85
     * @var bool
86
     */
87
    private $defaultRequired;
88
89
    /**
90
     * @param array      $specification The specification to apply to the value.
91
     * @param array      $options       The options apply during filtering.
92
     *                                  'allowUnknowns' (default false) true to allow or false to treat as error.
93
     *                                  'defaultRequired' (default false) true to make fields required by default.
94
     * @param array|null $filterAliases The filter aliases to accept.
95
     *
96
     * @throws InvalidArgumentException if 'allowUnknowns' option was not a bool
97
     * @throws InvalidArgumentException if 'defaultRequired' option was not a bool
98
     */
99
    public function __construct(array $specification, array $options = [], array $filterAliases = null)
100
    {
101
        $options += self::DEFAULT_OPTIONS;
102
103
        $this->specification = $specification;
104
        $this->filterAliases = $filterAliases;
105
        $this->allowUnknowns = self::getAllowUnknowns($options);
106
        $this->defaultRequired = self::getDefaultRequired($options);
107
    }
108
109
    /**
110
     * @param mixed $input The input to filter.
111
     *
112
     * @return FilterResponse
113
     *
114
     * @throws InvalidArgumentException Thrown if the filters for a field were not an array.
115
     * @throws InvalidArgumentException Thrown if any one filter for a field was not an array.
116
     * @throws InvalidArgumentException Thrown if the 'required' value for a field was not a bool.
117
     */
118
    public function execute(array $input) : FilterResponse
119
    {
120
        $filterAliases = $this->getAliases();
121
        $inputToFilter = [];
122
        $leftOverSpec = [];
123
124
        foreach ($this->specification as $field => $specification) {
125
            if (array_key_exists($field, $input)) {
126
                $inputToFilter[$field] = $input[$field];
127
                continue;
128
            }
129
130
            $leftOverSpec[$field] = $specification;
131
        }
132
133
        $leftOverInput = array_diff_key($input, $inputToFilter);
134
135
        $filteredInput = [];
136
        $errors = [];
137
        $conflicts = [];
138
        foreach ($inputToFilter as $field => $input) {
139
            $filters = $this->specification[$field];
140
            self::assertFiltersIsAnArray($filters, $field);
141
            $customError = self::validateCustomError($filters, $field);
142
            unset($filters[FilterOptions::IS_REQUIRED]);//doesn't matter if required since we have this one
143
            unset($filters[FilterOptions::DEFAULT_VALUE]);//doesn't matter if there is a default since we have a value
144
            $conflicts = self::extractConflicts($filters, $field, $conflicts);
145
146
            $uses = $filters[FilterOptions::USES] ?? [];
147
            unset($filters[FilterOptions::USES]);
148
149
            foreach ($filters as $filter) {
150
                self::assertFilterIsNotArray($filter, $field);
151
152
                if (empty($filter)) {
153
                    continue;
154
                }
155
156
                $function = array_shift($filter);
157
                $function = self::handleFilterAliases($function, $filterAliases);
158
159
                self::assertFunctionIsCallable($function, $field);
160
161
                array_unshift($filter, $input);
162
                try {
163
                    foreach ($uses as $usedField) {
164
                        if (!array_key_exists($usedField, $filteredInput)) {
165
                            throw new FilterException(
166
                                "{$field} uses {$usedField} but {$usedField} was not given."
167
                            );
168
                        }
169
170
                        array_push($filter, $filteredInput[$usedField]);
171
                    }
172
173
                    $input = call_user_func_array($function, $filter);
174
                } catch (Exception $exception) {
175
                    $errors = self::handleCustomError($field, $input, $exception, $errors, $customError);
176
                    continue 2;//next field
177
                }
178
            }
179
180
            $filteredInput[$field] = $input;
181
        }
182
183
        foreach ($leftOverSpec as $field => $filters) {
184
            self::assertFiltersIsAnArray($filters, $field);
185
            $required = self::getRequired($filters, $this->defaultRequired, $field);
186
            if (array_key_exists(FilterOptions::DEFAULT_VALUE, $filters)) {
187
                $filteredInput[$field] = $filters[FilterOptions::DEFAULT_VALUE];
188
                continue;
189
            }
190
191
            $errors = self::handleRequiredFields($required, $field, $errors);
192
        }
193
194
        $errors = self::handleAllowUnknowns($this->allowUnknowns, $leftOverInput, $errors);
195
        $errors = self::handleConflicts($filteredInput, $conflicts, $errors);
196
197
        return new FilterResponse($filteredInput, $errors, $leftOverInput);
198
    }
199
200
    /**
201
     * @return array
202
     *
203
     * @see FiltererInterface::getAliases
204
     */
205
    public function getAliases() : array
206
    {
207
        return $this->filterAliases ?? self::$registeredFilterAliases;
208
    }
209
210
    private static function extractConflicts(array &$filters, string $field, array $conflicts) : array
211
    {
212
        $conflictsWith = $filters[FilterOptions::CONFLICTS_WITH] ?? null;
213
        unset($filters[FilterOptions::CONFLICTS_WITH]);
214
        if ($conflictsWith === null) {
215
            return $conflicts;
216
        }
217
218
        if (!is_array($conflictsWith)) {
219
            $conflictsWith = [$conflictsWith];
220
        }
221
222
        $conflicts[$field] = $conflictsWith;
223
224
        return $conflicts;
225
    }
226
227
    private static function handleConflicts(array $inputToFilter, array $conflicts, array $errors)
228
    {
229
        foreach (array_keys($inputToFilter) as $field) {
230
            if (!array_key_exists($field, $conflicts)) {
231
                continue;
232
            }
233
234
            foreach ($conflicts[$field] as $conflictsWith) {
235
                if (array_key_exists($conflictsWith, $inputToFilter)) {
236
                    $errors[] = "Field '{$field}' cannot be given if field '{$conflictsWith}' is present.";
237
                }
238
            }
239
        }
240
241
        return $errors;
242
    }
243
244
    /**
245
     * @return array
246
     *
247
     * @see FiltererInterface::getSpecification
248
     */
249
    public function getSpecification() : array
250
    {
251
        return $this->specification;
252
    }
253
254
    /**
255
     * @param array $filterAliases
256
     *
257
     * @return FiltererInterface
258
     *
259
     * @see FiltererInterface::withAliases
260
     */
261
    public function withAliases(array $filterAliases) : FiltererInterface
262
    {
263
        return new Filterer($this->specification, $this->getOptions(), $filterAliases);
264
    }
265
266
    /**
267
     * @param array $specification
268
     *
269
     * @return FiltererInterface
270
     *
271
     * @see FiltererInterface::withSpecification
272
     */
273
    public function withSpecification(array $specification) : FiltererInterface
274
    {
275
        return new Filterer($specification, $this->getOptions(), $this->filterAliases);
276
    }
277
278
    /**
279
     * @return array
280
     */
281
    private function getOptions() : array
282
    {
283
        return [
284
            FiltererOptions::DEFAULT_REQUIRED => $this->defaultRequired,
285
            FiltererOptions::ALLOW_UNKNOWNS => $this->allowUnknowns,
286
        ];
287
    }
288
289
    /**
290
     * Example:
291
     * <pre>
292
     * <?php
293
     * class AppendFilter
294
     * {
295
     *     public function filter($value, $extraArg)
296
     *     {
297
     *         return $value . $extraArg;
298
     *     }
299
     * }
300
     * $appendFilter = new AppendFilter();
301
     *
302
     * $trimFunc = function($val) { return trim($val); };
303
     *
304
     * list($status, $result, $error, $unknowns) = TraderInteractive\Filterer::filter(
305
     *     [
306
     *         'field one' => [[$trimFunc], ['substr', 0, 3], [[$appendFilter, 'filter'], 'boo']],
307
     *         'field two' => ['required' => true, ['floatval']],
308
     *         'field three' => ['required' => false, ['float']],
309
     *         'field four' => ['required' => true, 'default' => 1, ['uint']],
310
     *     ],
311
     *     ['field one' => ' abcd', 'field two' => '3.14']
312
     * );
313
     *
314
     * var_dump($status);
315
     * var_dump($result);
316
     * var_dump($error);
317
     * var_dump($unknowns);
318
     * </pre>
319
     * prints:
320
     * <pre>
321
     * bool(true)
322
     * array(3) {
323
     *   'field one' =>
324
     *   string(6) "abcboo"
325
     *   'field two' =>
326
     *   double(3.14)
327
     *   'field four' =>
328
     *   int(1)
329
     * }
330
     * NULL
331
     * array(0) {
332
     * }
333
     * </pre>
334
     *
335
     * @param array $specification The specification to apply to the input.
336
     * @param array $input          The input the apply the specification to.
337
     * @param array $options        The options apply during filtering.
338
     *                              'allowUnknowns' (default false) true to allow or false to treat as error.
339
     *                              'defaultRequired' (default false) true to make fields required by default.
340
     *                              'responseType' (default RESPONSE_TYPE_ARRAY)
341
     *                                  Determines the return type, as described in the return section.
342
     *
343
     * @return array|FilterResponse If 'responseType' option is RESPONSE_TYPE_ARRAY:
344
     *                                  On success: [true, $input filtered, null, array of unknown fields]
345
     *                                  On error: [false, null, 'error message', array of unknown fields]
346
     *                              If 'responseType' option is RESPONSE_TYPE_FILTER: a FilterResponse instance
347
     *
348
     * @throws Exception
349
     * @throws InvalidArgumentException Thrown if the 'allowUnknowns' option was not a bool
350
     * @throws InvalidArgumentException Thrown if the 'defaultRequired' option was not a bool
351
     * @throws InvalidArgumentException Thrown if the 'responseType' option was not a recognized type.
352
     * @throws InvalidArgumentException Thrown if the filters for a field were not an array.
353
     * @throws InvalidArgumentException Thrown if any one filter for a field was not an array.
354
     * @throws InvalidArgumentException Thrown if the 'required' value for a field was not a bool.
355
     *
356
     * @see FiltererInterface::getSpecification For more information on specifications.
357
     */
358
    public static function filter(array $specification, array $input, array $options = [])
359
    {
360
        $options += self::DEFAULT_OPTIONS;
361
        $responseType = $options[FiltererOptions::RESPONSE_TYPE];
362
363
        $filterer = new Filterer($specification, $options);
364
        $filterResponse = $filterer->execute($input);
365
366
        return self::generateFilterResponse($responseType, $filterResponse);
367
    }
368
369
    /**
370
     * Return the filter aliases.
371
     *
372
     * @return array array where keys are aliases and values pass is_callable().
373
     */
374
    public static function getFilterAliases() : array
375
    {
376
        return self::$registeredFilterAliases;
377
    }
378
379
    /**
380
     * Set the filter aliases.
381
     *
382
     * @param array $aliases array where keys are aliases and values pass is_callable().
383
     * @return void
384
     *
385
     * @throws Exception Thrown if any of the given $aliases is not valid. @see registerAlias()
386
     */
387
    public static function setFilterAliases(array $aliases)
388
    {
389
        $originalAliases = self::$registeredFilterAliases;
390
        self::$registeredFilterAliases = [];
391
        try {
392
            foreach ($aliases as $alias => $callback) {
393
                self::registerAlias($alias, $callback);
394
            }
395
        } catch (Throwable $throwable) {
396
            self::$registeredFilterAliases = $originalAliases;
397
            throw $throwable;
398
        }
399
    }
400
401
    /**
402
     * Register a new alias with the Filterer
403
     *
404
     * @param string|int $alias the alias to register
405
     * @param callable $filter the aliased callable filter
406
     * @param bool $overwrite Flag to overwrite existing alias if it exists
407
     *
408
     * @return void
409
     *
410
     * @throws \InvalidArgumentException if $alias was not a string or int
411
     * @throws Exception if $overwrite is false and $alias exists
412
     */
413
    public static function registerAlias($alias, callable $filter, bool $overwrite = false)
414
    {
415
        self::assertIfStringOrInt($alias);
416
        self::assertIfAliasExists($alias, $overwrite);
417
        self::$registeredFilterAliases[$alias] = $filter;
418
    }
419
420
    /**
421
     * Filter an array by applying filters to each member
422
     *
423
     * @param array $values an array to be filtered. Use the Arrays::filter() before this method to ensure counts when
424
     *                      you pass into Filterer
425
     * @param array $filters filters with each specified the same as in @see self::filter.
426
     *                       Eg [['string', false, 2], ['uint']]
427
     *
428
     * @return array the filtered $values
429
     *
430
     * @throws FilterException if any member of $values fails filtering
431
     */
432
    public static function ofScalars(array $values, array $filters) : array
433
    {
434
        $wrappedFilters = [];
435
        foreach ($values as $key => $item) {
436
            $wrappedFilters[$key] = $filters;
437
        }
438
439
        list($status, $result, $error) = self::filter($wrappedFilters, $values);
440
        if (!$status) {
441
            throw new FilterException($error);
442
        }
443
444
        return $result;
445
    }
446
447
    /**
448
     * Filter an array by applying filters to each member
449
     *
450
     * @param array $values as array to be filtered. Use the Arrays::filter() before this method to ensure counts when
451
     *                      you pass into Filterer
452
     * @param array $spec spec to apply to each $values member, specified the same as in @see self::filter.
453
     *     Eg ['key' => ['required' => true, ['string', false], ['unit']], 'key2' => ...]
454
     *
455
     * @return array the filtered $values
456
     *
457
     * @throws Exception if any member of $values fails filtering
458
     */
459
    public static function ofArrays(array $values, array $spec) : array
460
    {
461
        $results = [];
462
        $errors = [];
463
        foreach ($values as $key => $item) {
464
            if (!is_array($item)) {
465
                $errors[] = "Value at position '{$key}' was not an array";
466
                continue;
467
            }
468
469
            list($status, $result, $error) = self::filter($spec, $item);
470
            if (!$status) {
471
                $errors[] = $error;
472
                continue;
473
            }
474
475
            $results[$key] = $result;
476
        }
477
478
        if (!empty($errors)) {
479
            throw new FilterException(implode("\n", $errors));
480
        }
481
482
        return $results;
483
    }
484
485
    /**
486
     * Filter $value by using a Filterer $spec and Filterer's default options.
487
     *
488
     * @param array $value array to be filtered. Use the Arrays::filter() before this method to ensure counts when you
489
     *                     pass into Filterer
490
     * @param array $spec spec to apply to $value, specified the same as in @see self::filter.
491
     *     Eg ['key' => ['required' => true, ['string', false], ['unit']], 'key2' => ...]
492
     *
493
     * @return array the filtered $value
494
     *
495
     * @throws FilterException if $value fails filtering
496
     */
497
    public static function ofArray(array $value, array $spec) : array
498
    {
499
        list($status, $result, $error) = self::filter($spec, $value);
500
        if (!$status) {
501
            throw new FilterException($error);
502
        }
503
504
        return $result;
505
    }
506
507
    private static function assertIfStringOrInt($alias)
508
    {
509
        if (!is_string($alias) && !is_int($alias)) {
510
            throw new InvalidArgumentException('$alias was not a string or int');
511
        }
512
    }
513
514
    private static function assertIfAliasExists($alias, bool $overwrite)
515
    {
516
        if (array_key_exists($alias, self::$registeredFilterAliases) && !$overwrite) {
517
            throw new Exception("Alias '{$alias}' exists");
518
        }
519
    }
520
521
    private static function checkForUnknowns(array $leftOverInput, array $errors) : array
522
    {
523
        foreach ($leftOverInput as $field => $value) {
524
            $errors[$field] = "Field '{$field}' with value '" . trim(var_export($value, true), "'") . "' is unknown";
525
        }
526
527
        return $errors;
528
    }
529
530
    private static function handleAllowUnknowns(bool $allowUnknowns, array $leftOverInput, array $errors) : array
531
    {
532
        if (!$allowUnknowns) {
533
            $errors = self::checkForUnknowns($leftOverInput, $errors);
534
        }
535
536
        return $errors;
537
    }
538
539
    private static function handleRequiredFields(bool $required, string $field, array $errors) : array
540
    {
541
        if ($required) {
542
            $errors[$field] = "Field '{$field}' was required and not present";
543
        }
544
        return $errors;
545
    }
546
547
    private static function getRequired($filters, $defaultRequired, $field) : bool
548
    {
549
        $required = $filters[FilterOptions::IS_REQUIRED] ?? $defaultRequired;
550
        if ($required !== false && $required !== true) {
551
            throw new InvalidArgumentException(
552
                sprintf("'%s' for field '%s' was not a bool", FilterOptions::IS_REQUIRED, $field)
553
            );
554
        }
555
556
        return $required;
557
    }
558
559
    private static function assertFiltersIsAnArray($filters, string $field)
560
    {
561
        if (!is_array($filters)) {
562
            throw new InvalidArgumentException("filters for field '{$field}' was not a array");
563
        }
564
    }
565
566
    private static function handleCustomError(
567
        string $field,
568
        $value,
569
        Throwable $e,
570
        array $errors,
571
        string $customError = null
572
    ) : array {
573
        $error = $customError;
574
        if ($error === null) {
575
            $errorFormat = "Field '%s' with value '{value}' failed filtering, message '%s'";
576
            $error = sprintf($errorFormat, $field, $e->getMessage());
577
        }
578
579
        $errors[$field] = str_replace('{value}', trim(var_export($value, true), "'"), $error);
580
        return $errors;
581
    }
582
583
    private static function assertFunctionIsCallable($function, string $field)
584
    {
585
        if (!is_callable($function)) {
586
            throw new Exception(
587
                "Function '" . trim(var_export($function, true), "'") . "' for field '{$field}' is not callable"
588
            );
589
        }
590
    }
591
592
    private static function handleFilterAliases($function, $filterAliases)
593
    {
594
        if ((is_string($function) || is_int($function)) && array_key_exists($function, $filterAliases)) {
595
            $function = $filterAliases[$function];
596
        }
597
598
        return $function;
599
    }
600
601
    private static function assertFilterIsNotArray($filter, string $field)
602
    {
603
        if (!is_array($filter)) {
604
            throw new InvalidArgumentException("filter for field '{$field}' was not a array");
605
        }
606
    }
607
608
    private static function validateCustomError(array &$filters, string $field)
609
    {
610
        $customError = null;
611
        if (array_key_exists(FilterOptions::CUSTOM_ERROR, $filters)) {
612
            $customError = $filters[FilterOptions::CUSTOM_ERROR];
613
            if (!is_string($customError) || trim($customError) === '') {
614
                throw new InvalidArgumentException(
615
                    sprintf("%s for field '%s' was not a non-empty string", FilterOptions::CUSTOM_ERROR, $field)
616
                );
617
            }
618
619
            unset($filters[FilterOptions::CUSTOM_ERROR]);//unset so its not used as a filter
620
        }
621
622
        return $customError;
623
    }
624
625 View Code Duplication
    private static function getAllowUnknowns(array $options) : bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
626
    {
627
        $allowUnknowns = $options[FiltererOptions::ALLOW_UNKNOWNS];
628
        if ($allowUnknowns !== false && $allowUnknowns !== true) {
629
            throw new InvalidArgumentException(sprintf("'%s' option was not a bool", FiltererOptions::ALLOW_UNKNOWNS));
630
        }
631
632
        return $allowUnknowns;
633
    }
634
635 View Code Duplication
    private static function getDefaultRequired(array $options) : bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
636
    {
637
        $defaultRequired = $options[FiltererOptions::DEFAULT_REQUIRED];
638
        if ($defaultRequired !== false && $defaultRequired !== true) {
639
            throw new InvalidArgumentException(
640
                sprintf("'%s' option was not a bool", FiltererOptions::DEFAULT_REQUIRED)
641
            );
642
        }
643
644
        return $defaultRequired;
645
    }
646
647
    /**
648
     * @param string         $responseType   The type of object that should be returned.
649
     * @param FilterResponse $filterResponse The filter response to generate the typed response from.
650
     *
651
     * @return array|FilterResponse
652
     *
653
     * @see filter For more information on how responseType is handled and returns are structured.
654
     */
655
    private static function generateFilterResponse(string $responseType, FilterResponse $filterResponse)
656
    {
657
        if ($responseType === self::RESPONSE_TYPE_FILTER) {
658
            return $filterResponse;
659
        }
660
661
        if ($responseType === self::RESPONSE_TYPE_ARRAY) {
662
            return [
663
                $filterResponse->success,
664
                $filterResponse->success ? $filterResponse->filteredValue : null,
665
                $filterResponse->errorMessage,
666
                $filterResponse->unknowns
667
            ];
668
        }
669
670
        throw new InvalidArgumentException(sprintf("'%s' was not a recognized value", FiltererOptions::RESPONSE_TYPE));
671
    }
672
}
673