Test Failed
Pull Request — master (#171)
by
unknown
01:56
created

Parameter::expandFilterArgs()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 0
cts 0
cp 0
rs 9.7666
c 0
b 0
f 0
cc 3
nc 3
nop 3
crap 12
1
<?php
2
namespace GuzzleHttp\Command\Guzzle;
3
4
use GuzzleHttp\Command\ToArrayInterface;
5
6
/**
7
 * API parameter object used with service descriptions
8
 */
9
class Parameter implements ToArrayInterface
10
{
11
    /**
12
     * The name of the filter stage that happens before a parameter is
13
     * validated, for filtering raw data (e.g. clean-up before validation).
14
     */
15
    const FILTER_STAGE_BEFORE_VALIDATION = 'before_validation';
16
17
    /**
18
     * The name of the filter stage that happens immediately after a parameter
19
     * has been validated but before it is evaluated by location handlers to be
20
     * written out on the wire.
21
     */
22
    const FILTER_STAGE_AFTER_VALIDATION = 'after_validation';
23
24
    /**
25
     * The name of the filter stage that happens right before a validated value
26
     * is being written out "on the wire" (e.g. for adjusting the structure or
27
     * format of the data before sending it to the server).
28
     */
29
    const FILTER_STAGE_REQUEST_WIRE = 'request_wire';
30
31
    /**
32
     * The name of the filter stage that happens right after a value has been
33
     * read out of a response "on the wire" (e.g. for adjusting the structure or
34
     * format of the data after receiving it back from the server).
35
     */
36
    const FILTER_STAGE_RESPONSE_WIRE = 'response_wire';
37
38
    /**
39
     * A list of all allowed filter stages.
40
     */
41
    const FILTER_STAGES = [
42
        self::FILTER_STAGE_BEFORE_VALIDATION,
43
        self::FILTER_STAGE_AFTER_VALIDATION,
44
        self::FILTER_STAGE_REQUEST_WIRE,
45
        self::FILTER_STAGE_RESPONSE_WIRE
46
    ];
47
48
    private $originalData;
49
50
    /** @var string $name */
51
    private $name;
52
53
    /** @var string $description */
54
    private $description;
55
56
    /** @var string|array $type */
57
    private $type;
58
59
    /** @var bool $required*/
60
    private $required;
61
62
    /** @var array|null $enum */
63
    private $enum;
64
65
    /** @var string $pattern */
66
    private $pattern;
67
68
    /** @var int $minimum*/
69
    private $minimum;
70
71
    /** @var int $maximum */
72
    private $maximum;
73
74
    /** @var int $minLength */
75
    private $minLength;
76
77
    /** @var int $maxLength */
78
    private $maxLength;
79
80
    /** @var int $minItems */
81
    private $minItems;
82
83
    /** @var int $maxItems */
84
    private $maxItems;
85
86
    /** @var mixed $default */
87
    private $default;
88
89
    /** @var bool $static */
90
    private $static;
91
92
    /** @var array $filters */
93
    private $filters;
94
95
    /** @var string $location */
96
    private $location;
97
98
    /** @var string $sentAs */
99
    private $sentAs;
100
101
    /** @var array $data */
102
    private $data;
103
104
    /** @var array $properties */
105
    private $properties = [];
106
107
    /** @var array|bool|Parameter $additionalProperties */
108
    private $additionalProperties;
109
110
    /** @var array|Parameter $items */
111
    private $items;
112
113
    /** @var string $format */
114
    private $format;
115
116
    private $propertiesCache = null;
117
118
    /** @var Description */
119
    private $serviceDescription;
120
121
    /**
122
     * Create a new Parameter using an associative array of data.
123
     *
124
     * The array can contain the following information:
125
     *
126
     * - name: (string) Unique name of the parameter
127
     *
128
     * - type: (string|array) Type of variable (string, number, integer,
129
     *   boolean, object, array, numeric, null, any). Types are used for
130
     *   validation and determining the structure of a parameter. You can use a
131
     *   union type by providing an array of simple types. If one of the union
132
     *   types matches the provided value, then the value is valid.
133
     *
134
     * - required: (bool) Whether or not the parameter is required
135
     *
136
     * - default: (mixed) Default value to use if no value is supplied
137
     *
138
     * - static: (bool) Set to true to specify that the parameter value cannot
139
     *   be changed from the default.
140
     *
141
     * - description: (string) Documentation of the parameter
142
     *
143
     * - location: (string) The location of a request used to apply a parameter.
144
     *   Custom locations can be registered with a command, but the defaults
145
     *   are uri, query, header, body, json, xml, formParam, multipart.
146
     *
147
     * - sentAs: (string) Specifies how the data being modeled is sent over the
148
     *   wire. For example, you may wish to include certain headers in a
149
     *   response model that have a normalized casing of FooBar, but the actual
150
     *   header is x-foo-bar. In this case, sentAs would be set to x-foo-bar.
151
     *
152
     * - filters: (array) Array of static method names to run a parameter
153
     *   value through. Each value in the array must be a string containing the
154
     *   full class path to a static method or an array of complex filter
155
     *   information. You can specify static methods of classes using the full
156
     *   namespace class name followed by '::' (e.g. Foo\Bar::baz). Some
157
     *   filters require arguments in order to properly filter a value.
158
     *
159
     *   For complex filters, use a hash containing a 'method' key pointing to a
160
     *   static method, an 'args' key containing an array of positional
161
     *   arguments to pass to the method, and an optional 'stage' key. Arguments
162
     *   can contain keywords that are replaced when filtering a value: '@value'
163
     *   is replaced with the value being validated, '@api' is replaced with the
164
     *   Parameter object, and '@stage' is replaced with the current filter
165
     *   stage (if any was provided).
166
     *
167
     *   The optional 'stage' key can be provided to control when the filter is
168
     *   invoked. The key can indicate that a filter should only be invoked
169
     *   'before_validation', 'after_validation', when being written out to the
170
     *   'request_wire' or being read from the 'response_wire'.
171
     *
172
     * - properties: When the type is an object, you can specify nested
173
     *   parameters
174
     *
175
     * - additionalProperties: (array) This attribute defines a schema for all
176
     *   properties that are not explicitly defined in an object type
177
     *   definition. If specified, the value MUST be a schema or a boolean. If
178 34
     *   false is provided, no additional properties are allowed beyond the
179
     *   properties defined in the schema. The default value is an empty schema
180 34
     *   which allows any value for additional properties.
181
     *
182 34
     * - items: This attribute defines the allowed items in an instance array,
183 7
     *   and MUST be a schema or an array of schemas. The default value is an
184 7
     *   empty schema which allows any value for items in the instance array.
185 1
     *   When this attribute value is a schema and the instance value is an
186
     *   array, then all the items in the array MUST be valid according to the
187 6
     *   schema.
188 1
     *
189 1
     * - pattern: When the type is a string, you can specify the regex pattern
190 1
     *   that a value must match
191 1
     *
192
     * - enum: When the type is a string, you can specify a list of acceptable
193
     *   values.
194 1
     *
195 6
     * - minItems: (int) Minimum number of items allowed in an array
196
     *
197
     * - maxItems: (int) Maximum number of items allowed in an array
198
     *
199 1
     * - minLength: (int) Minimum length of a string
200 1
     *
201 1
     * - maxLength: (int) Maximum length of a string
202 1
     *
203 6
     * - minimum: (int) Minimum value of an integer
204
     *
205
     * - maximum: (int) Maximum value of an integer
206 33
     *
207 31
     * - data: (array) Any additional custom data to use when serializing,
208 33
     *   validating, etc
209
     *
210 33
     * - format: (string) Format used to coax a value into the correct format
211 33
     *   when serializing or unserializing. You may specify either an array of
212
     *   filters OR a format, but not both. Supported values: date-time, date,
213 33
     *   time, timestamp, date-time-http, and boolean-string.
214 10
     *
215 9
     * - $ref: (string) String referencing a service description model. The
216
     *   parameter is replaced by the schema contained in the model.
217 32
     *
218 2
     * @param array $data    Array of data as seen in service descriptions
219 2
     * @param array $options Options used when creating the parameter. You can
220 32
     *     specify a Guzzle service description in the 'description' key.
221
     *
222
     * @throws \InvalidArgumentException
223
     */
224
    public function __construct(array $data = [], array $options = [])
225
    {
226
        $this->originalData = $data;
227 7
228
        if (isset($options['description'])) {
229 7
            $this->serviceDescription = $options['description'];
230
            if (!($this->serviceDescription instanceof DescriptionInterface)) {
231
                throw new \InvalidArgumentException('description must be a Description');
232
            }
233
            if (isset($data['$ref'])) {
234
                if ($model = $this->serviceDescription->getModel($data['$ref'])) {
235
                    $name = isset($data['name']) ? $data['name'] : null;
236
                    $data = $model->toArray() + $data;
237
                    if ($name) {
238
                        $data['name'] = $name;
239 4
                    }
240
                }
241 4
            } elseif (isset($data['extends'])) {
242 2
                // If this parameter extends from another parameter then start
243
                // with the actual data union in the parent's data (e.g. actual
244
                // supersedes parent)
245 2
                if ($extends = $this->serviceDescription->getModel($data['extends'])) {
246
                    $data += $extends->toArray();
247
                }
248
            }
249
        }
250
251
        // Pull configuration data into the parameter
252
        foreach ($data as $key => $value) {
253
            $this->{$key} = $value;
254
        }
255
256
        $this->required = (bool) $this->required;
257
        $this->data = (array) $this->data;
258 8
259
        if (empty($this->filters)) {
260
            $this->filters = [];
261 8
        } else {
262 5
            $this->setFilters((array) $this->filters);
263 1
        }
264 1
265
        if ($this->type == 'object' && $this->additionalProperties === null) {
266 4
            $this->additionalProperties = true;
267
        }
268
    }
269
270 3
    /**
271 1
     * Convert the object to an array
272 1
     *
273
     * @return array
274
     */
275 3
    public function toArray()
276 2
    {
277 2
        return $this->originalData;
278
    }
279 1
280 1
    /**
281 1
     * Get the default or static value of the command based on a value
282 1
     *
283 1
     * @param string $value Value that is currently set
284 1
     *
285 1
     * @return mixed Returns the value, a static value if one is present, or a default value
286 1
     */
287 1
    public function getValue($value)
288 1
    {
289 1
        if ($this->static || ($this->default !== null && $value === null)) {
290 1
            return $this->default;
291 1
        }
292
293 2
        return $value;
294 2
    }
295
296 3
    /**
297
     * Run a value through the filters OR format attribute associated with the
298
     * parameter.
299
     *
300
     * @param mixed $value Value to filter
301
     * @param string $stage An optional specifier of what filter stage to
302
     *     invoke. If null, then all filters are invoked no matter what stage
303
     *     they apply to. Otherwise, only filters for the specified stage are
304 1
     *     invoked.
305
     *
306 1
     * @return mixed Returns the filtered value
307
     * @throws \RuntimeException when trying to format when no service
308
     *     description is available.
309
     * @throws \InvalidArgumentException if an invalid validation stage is
310
     *     provided.
311
     */
312
    public function filter($value, $stage = null)
313
    {
314 1
        if (($stage !== null) && !in_array($stage, self::FILTER_STAGES)) {
315
            throw new \InvalidArgumentException(
316 1
                sprintf(
317 1
                    '$stage must be one of [%s], but was given "%s"',
318
                    implode(', ', self::FILTER_STAGES),
319
                    $stage
320
                )
321
            );
322
        }
323
324
        // Formats are applied exclusively and supercede filters
325 1
        if (!empty($this->format)) {
326
            if (!$this->serviceDescription) {
327 1
                throw new \RuntimeException('No service description was set so '
328
                    . 'the value cannot be formatted.');
329
            }
330
            return $this->serviceDescription->format($this->format, $value);
331
        }
332
333
        // Convert Boolean values
334
        if ($this->type == 'boolean' && !is_bool($value)) {
335 2
            $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
336
        }
337 2
338
        // Apply filters to the value
339
        if (!empty($this->filters)) {
340
            $value = $this->invokeCustomFilters($value, $stage);
341
        }
342
343
        return $value;
344
    }
345 1
346
    /**
347 1
     * Get the name of the parameter
348
     *
349
     * @return string
350
     */
351
    public function getName()
352
    {
353
        return $this->name;
354
    }
355 1
356
    /**
357 1
     * Set the name of the parameter
358
     *
359
     * @param string $name Name to set
360
     */
361
    public function setName($name)
362
    {
363
        $this->name = $name;
364
    }
365 1
366
    /**
367 1
     * Get the key of the parameter, where sentAs will supersede name if it is
368
     * set.
369
     *
370
     * @return string
371
     */
372
    public function getWireName()
373
    {
374
        return $this->sentAs ?: $this->name;
375 1
    }
376
377 1
    /**
378
     * Get the type(s) of the parameter
379
     *
380
     * @return string|array
381
     */
382
    public function getType()
383
    {
384
        return $this->type;
385 1
    }
386
387 1
    /**
388
     * Get if the parameter is required
389
     *
390
     * @return bool
391
     */
392
    public function isRequired()
393
    {
394
        return $this->required;
395 1
    }
396
397 1
    /**
398
     * Get the default value of the parameter
399
     *
400
     * @return string|null
401
     */
402
    public function getDefault()
403
    {
404
        return $this->default;
405 1
    }
406
407 1
    /**
408
     * Get the description of the parameter
409
     *
410
     * @return string|null
411
     */
412
    public function getDescription()
413
    {
414
        return $this->description;
415 1
    }
416
417 1
    /**
418
     * Get the minimum acceptable value for an integer
419
     *
420
     * @return int|null
421
     */
422
    public function getMinimum()
423
    {
424
        return $this->minimum;
425 1
    }
426
427 1
    /**
428
     * Get the maximum acceptable value for an integer
429
     *
430
     * @return int|null
431
     */
432
    public function getMaximum()
433
    {
434
        return $this->maximum;
435 2
    }
436
437 2
    /**
438
     * Get the minimum allowed length of a string value
439
     *
440
     * @return int
441
     */
442
    public function getMinLength()
443
    {
444
        return $this->minLength;
445
    }
446 1
447
    /**
448 1
     * Get the maximum allowed length of a string value
449
     *
450
     * @return int|null
451
     */
452
    public function getMaxLength()
453
    {
454
        return $this->maxLength;
455
    }
456
457
    /**
458
     * Get the maximum allowed number of items in an array value
459
     *
460 1
     * @return int|null
461
     */
462 1
    public function getMaxItems()
463 1
    {
464 1
        return $this->maxItems;
465 1
    }
466 1
467 1
    /**
468
     * Get the minimum allowed number of items in an array value
469
     *
470 1
     * @return int
471
     */
472
    public function getMinItems()
473
    {
474
        return $this->minItems;
475
    }
476
477
    /**
478 1
     * Get the location of the parameter
479
     *
480 1
     * @return string|null
481
     */
482
    public function getLocation()
483
    {
484
        return $this->location;
485
    }
486
487
    /**
488 2
     * Get the sentAs attribute of the parameter that used with locations to
489
     * sentAs an attribute when it is being applied to a location.
490 2
     *
491
     * @return string|null
492
     */
493
    public function getSentAs()
494
    {
495
        return $this->sentAs;
496
    }
497
498 1
    /**
499
     * Retrieve a known property from the parameter by name or a data property
500 1
     * by name. When no specific name value is passed, all data properties
501 1
     * will be returned.
502 1
     *
503 1
     * @param string|null $name Specify a particular property name to retrieve
504 1
     *
505 1
     * @return array|mixed|null
506
     */
507 1
    public function getData($name = null)
508
    {
509
        if (!$name) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $name of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
510
            return $this->data;
511
        } elseif (isset($this->data[$name])) {
512
            return $this->data[$name];
513
        } elseif (isset($this->{$name})) {
514
            return $this->{$name};
515
        }
516
517 1
        return null;
518
    }
519 1
520 1
    /**
521
     * Get whether or not the default value can be changed
522
     *
523 1
     * @return bool
524 1
     */
525 1
    public function isStatic()
526 1
    {
527 1
        return $this->static;
528 1
    }
529 1
530
    /**
531 1
     * Get an array of filters used by the parameter
532
     *
533
     * @return array
534
     */
535
    public function getFilters()
536
    {
537
        return $this->filters;
538
    }
539 1
540
    /**
541 1
     * Get the properties of the parameter
542 1
     *
543 1
     * @return Parameter[]
544 1
     */
545 1
    public function getProperties()
546 1
    {
547
        if (!$this->propertiesCache) {
548 1
            $this->propertiesCache = [];
549
            foreach (array_keys($this->properties) as $name) {
550
                $this->propertiesCache[$name] = $this->getProperty($name);
551
            }
552
        }
553
554
        return $this->propertiesCache;
555
    }
556 1
557
    /**
558 1
     * Get a specific property from the parameter
559 1
     *
560 1
     * @param string $name Name of the property to retrieve
561 1
     *
562 1
     * @return null|Parameter
563 1
     */
564
    public function getProperty($name)
565 1
    {
566
        if (!isset($this->properties[$name])) {
567
            return null;
568
        }
569
570
        if (!($this->properties[$name] instanceof self)) {
571
            $this->properties[$name]['name'] = $name;
572
            $this->properties[$name] = new static(
573 1
                $this->properties[$name],
574
                ['description' => $this->serviceDescription]
575 1
            );
576
        }
577
578
        return $this->properties[$name];
579
    }
580
581
    /**
582
     * Get the additionalProperties value of the parameter
583 1
     *
584
     * @return bool|Parameter|null
585 1
     */
586
    public function getAdditionalProperties()
587
    {
588
        if (is_array($this->additionalProperties)) {
589
            $this->additionalProperties = new static(
590
                $this->additionalProperties,
591
                ['description' => $this->serviceDescription]
592
            );
593 4
        }
594
595 4
        return $this->additionalProperties;
596
    }
597
598
    /**
599
     * Get the item data of the parameter
600
     *
601
     * @return Parameter
602
     */
603
    public function getItems()
604
    {
605 10
        if (is_array($this->items)) {
606
            $this->items = new static(
607 10
                $this->items,
608 10
                ['description' => $this->serviceDescription]
609 10
            );
610 9
        }
611
612 9
        return $this->items;
613
    }
614
615
    /**
616
     * Get the enum of strings that are valid for the parameter
617
     *
618
     * @return array|null
619
     */
620
    public function getEnum()
621
    {
622
        return $this->enum;
623 10
    }
624
625 10
    /**
626 2
     * Get the regex pattern that must match a value when the value is a string
627 1
     *
628
     * @return string
629 1
     */
630
    public function getPattern()
631 1
    {
632
        return $this->pattern;
633 9
    }
634 9
635 9
    /**
636 7
     * Get the format attribute of the schema
637
     *
638
     * @return string
639 9
     */
640
    public function getFormat()
641
    {
642
        return $this->format;
643
    }
644
645
    /**
646
     * Set the array of filters used by the parameter
647
     *
648 3
     * @param array $filters Array of functions to use as filters
649
     *
650 3
     * @return self
651 1
     */
652
    private function setFilters(array $filters)
653 2
    {
654
        $this->filters = [];
655
656
        foreach ($filters as $filter) {
657
            $this->addFilter($filter);
658
        }
659
660
        return $this;
661
    }
662
663
    /**
664
     * Add a filter to the parameter
665
     *
666
     * @param string|array $filter Method to filter the value through
667
     *
668
     * @return self
669
     * @throws \InvalidArgumentException
670
     */
671
    private function addFilter($filter)
672
    {
673
        if (is_array($filter)) {
674
            if (!isset($filter['method'])) {
675
                throw new \InvalidArgumentException(
676
                    'A [method] value must be specified for each complex filter'
677
                );
678
            }
679
680
            if (isset($filter['stage'])
681
                && !in_array($filter['stage'], self::FILTER_STAGES)) {
682
                throw new \InvalidArgumentException(
683
                    sprintf(
684
                        '[stage] value must be one of [%s], but was given "%s"',
685
                        implode(', ', self::FILTER_STAGES),
686
                        $filter['stage']
687
                    )
688
                );
689
            }
690
        }
691
692
        $this->filters[] = $filter;
693
694
        return $this;
695
    }
696
697
    /**
698
     * Check if a parameter has a specific variable and if it set.
699
     *
700
     * @param string $var
701
     * @return bool
702
     */
703
    public function has($var)
704
    {
705
        if (!is_string($var)) {
706
            throw new \InvalidArgumentException('Expected a string. Got: ' . (is_object($var) ? get_class($var) : gettype($var)));
707
        }
708
        return isset($this->{$var}) && !empty($this->{$var});
709
    }
710
711
    /**
712
     * Filters the given data using filter methods specified in the config.
713
     *
714
     * If $stage is provided, only filters that apply to the provided filter
715
     * stage will be invoked. To preserve legacy behavior, filters that do not
716
     * specify a stage are implicitly invoked only in the pre-validation stage.
717
     *
718
     * @param mixed $value The value to filter.
719
     * @param string $stage An optional specifier of what filter stage to
720
     *     invoke. If null, then all filters are invoked no matter what stage
721
     *     they apply to. Otherwise, only filters for the specified stage are
722
     *     invoked.
723
     *
724
     * @return mixed The filtered value.
725
     */
726
    private function invokeCustomFilters($value, $stage) {
727
        $filteredValue = $value;
728
729
        foreach ($this->filters as $filter) {
730
            if (is_array($filter)) {
731
                $filteredValue =
732
                    $this->invokeComplexFilter($filter, $value, $stage);
733
            } else {
734
                $filteredValue =
735
                    $this->invokeSimpleFilter($filter, $value, $stage);
736
            }
737
        }
738
739
        return $filteredValue;
740
    }
741
742
    /**
743
     * Invokes a filter that uses value substitution and/or should only be
744
     * invoked for a particular filter stage.
745
     *
746
     * If $stage is provided, and the filter specifies a stage, it is not
747
     * invoked unless $stage matches the stage the filter indicates it applies
748
     * to. If the filter is not invoked, $value is returned exactly as it was
749
     * provided to this method.
750
     *
751
     * To preserve legacy behavior, if the filter does not specify a stage, it
752
     * is implicitly invoked only in the pre-validation stage.
753
     *
754
     * @param array $filter Information about the filter to invoke.
755
     * @param mixed $value The value to filter.
756
     * @param string $stage An optional specifier of what filter stage to
757
     *     invoke. If null, then the filter is invoked no matter what stage it
758
     *     indicates it applies to. Otherwise, the filter is only invoked if it
759
     *     matches the specified stage.
760
     *
761
     * @return mixed The filtered value.
762
     */
763
    private function invokeComplexFilter(array $filter, $value, $stage) {
764
        if (isset($filter['stage'])) {
765
            $filterStage = $filter['stage'];
766
        } else {
767
            $filterStage = self::FILTER_STAGE_AFTER_VALIDATION;
768
        }
769
770
        if (($stage === null) || ($filterStage == $stage)) {
771
            // Convert complex filters that hold value place holders
772
            $filterArgs =
773
                $this->expandFilterArgs($filter['args'], $value, $stage);
774
775
            $filteredValue =
776
                call_user_func_array($filter['method'], $filterArgs);
777
        } else {
778
            $filteredValue = $value;
779
        }
780
781
        return $filteredValue;
782
    }
783
784
    /**
785
     * Replaces any placeholders in filter arguments with values from the
786
     * current context.
787
     *
788
     * @param array $filterArgs The array of arguments to pass to the filter
789
     *     function. Some of the elements of this array are expected to be
790
     *     placeholders that will be replaced by this function.
791
     *
792
     * @return array The array of arguments, with all placeholders replaced.
793
     */
794
    private function expandFilterArgs(array $filterArgs, $value, $stage) {
795
        $replacements = [
796
            '@value'  => $value,
797
            '@api'    => $this,
798
            '@stage'  => $stage,
799
        ];
800
801
        foreach ($filterArgs as &$argValue) {
802
            if (isset($replacements[$argValue])) {
803
              $argValue = $replacements[$argValue];
804
            }
805
        }
806
807
        return $filterArgs;
808
    }
809
810
    /**
811
     * Invokes a filter only provides a function or method name to invoke,
812
     * without additional parameters.
813
     *
814
     * If $stage is provided, the filter is not invoked unless we are in the
815
     * pre-validation stage, to preserve legacy behavior.
816
     *
817
     * @param array $filter Information about the filter to invoke.
818
     * @param mixed $value The value to filter.
819
     * @param string $stage An optional specifier of what filter stage to
820
     *     invoke. If null, then the filter is invoked no matter what.
821
     *     Otherwise, the filter is only invoked if the value is
822
     *     FILTER_STAGE_AFTER_VALIDATION.
823
     *
824
     * @return mixed The filtered value.
825
     */
826
    private function invokeSimpleFilter($filter, $value, $stage) {
827
        if ($stage === self::FILTER_STAGE_AFTER_VALIDATION) {
828
            return $value;
829
        } else {
830
            return call_user_func($filter, $value);
831
        }
832
    }
833
}
834