Parameter   F
last analyzed

Complexity

Total Complexity 75

Size/Duplication

Total Lines 647
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 98.83%

Importance

Changes 0
Metric Value
wmc 75
lcom 1
cbo 2
dl 0
loc 647
ccs 169
cts 171
cp 0.9883
rs 2.353
c 0
b 0
f 0

32 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 43 13
A toArray() 0 4 1
A getValue() 0 8 4
B filter() 0 40 11
A getName() 0 4 1
A setName() 0 4 1
A getWireName() 0 4 2
A getType() 0 4 1
A isRequired() 0 4 1
A getDefault() 0 4 1
A getDescription() 0 4 1
A getMinimum() 0 4 1
A getMaximum() 0 4 1
A getMinLength() 0 4 1
A getMaxLength() 0 4 1
A getMaxItems() 0 4 1
A getMinItems() 0 4 1
A getLocation() 0 4 1
A getSentAs() 0 4 1
A getData() 0 12 4
A isStatic() 0 4 1
A getFilters() 0 4 2
A getProperties() 0 11 3
A getProperty() 0 16 3
A getAdditionalProperties() 0 11 2
A getItems() 0 11 2
A getEnum() 0 4 1
A getPattern() 0 4 1
A getFormat() 0 4 1
A setFilters() 0 9 2
A addFilter() 0 18 4
A has() 0 7 4

How to fix   Complexity   

Complex Class

Complex classes like Parameter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Parameter, and based on these observations, apply Extract Interface, too.

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
    private $originalData;
12
13
    /** @var string $name */
14
    private $name;
15
16
    /** @var string $description */
17
    private $description;
18
19
    /** @var string|array $type */
20
    private $type;
21
22
    /** @var bool $required*/
23
    private $required;
24
25
    /** @var array|null $enum */
26
    private $enum;
27
28
    /** @var string $pattern */
29
    private $pattern;
30
31
    /** @var int $minimum*/
32
    private $minimum;
33
34
    /** @var int $maximum */
35
    private $maximum;
36
37
    /** @var int $minLength */
38
    private $minLength;
39
40
    /** @var int $maxLength */
41
    private $maxLength;
42
43
    /** @var int $minItems */
44
    private $minItems;
45
46
    /** @var int $maxItems */
47
    private $maxItems;
48
49
    /** @var mixed $default */
50
    private $default;
51
52
    /** @var bool $static */
53
    private $static;
54
55
    /** @var array $filters */
56
    private $filters;
57
58
    /** @var string $location */
59
    private $location;
60
61
    /** @var string $sentAs */
62
    private $sentAs;
63
64
    /** @var array $data */
65
    private $data;
66
67
    /** @var array $properties */
68
    private $properties = [];
69
70
    /** @var array|bool|Parameter $additionalProperties */
71
    private $additionalProperties;
72
73
    /** @var array|Parameter $items */
74
    private $items;
75
76
    /** @var string $format */
77
    private $format;
78
79
    private $propertiesCache = null;
80
81
    /** @var Description */
82
    private $serviceDescription;
83
84
    /**
85
     * Create a new Parameter using an associative array of data.
86
     *
87
     * The array can contain the following information:
88
     *
89
     * - name: (string) Unique name of the parameter
90
     *
91
     * - type: (string|array) Type of variable (string, number, integer,
92
     *   boolean, object, array, numeric, null, any). Types are used for
93
     *   validation and determining the structure of a parameter. You can use a
94
     *   union type by providing an array of simple types. If one of the union
95
     *   types matches the provided value, then the value is valid.
96
     *
97
     * - required: (bool) Whether or not the parameter is required
98
     *
99
     * - default: (mixed) Default value to use if no value is supplied
100
     *
101
     * - static: (bool) Set to true to specify that the parameter value cannot
102
     *   be changed from the default.
103
     *
104
     * - description: (string) Documentation of the parameter
105
     *
106
     * - location: (string) The location of a request used to apply a parameter.
107
     *   Custom locations can be registered with a command, but the defaults
108
     *   are uri, query, header, body, json, xml, formParam, multipart.
109
     *
110
     * - sentAs: (string) Specifies how the data being modeled is sent over the
111
     *   wire. For example, you may wish to include certain headers in a
112
     *   response model that have a normalized casing of FooBar, but the actual
113
     *   header is x-foo-bar. In this case, sentAs would be set to x-foo-bar.
114
     *
115
     * - filters: (array) Array of static method names to run a parameter
116
     *   value through. Each value in the array must be a string containing the
117
     *   full class path to a static method or an array of complex filter
118
     *   information. You can specify static methods of classes using the full
119
     *   namespace class name followed by '::' (e.g. Foo\Bar::baz). Some
120
     *   filters require arguments in order to properly filter a value. For
121
     *   complex filters, use a hash containing a 'method' key pointing to a
122
     *   static method, and an 'args' key containing an array of positional
123
     *   arguments to pass to the method. Arguments can contain keywords that
124
     *   are replaced when filtering a value: '@value' is replaced with the
125
     *   value being validated, '@api' is replaced with the Parameter object.
126
     *
127
     * - properties: When the type is an object, you can specify nested parameters
128
     *
129
     * - additionalProperties: (array) This attribute defines a schema for all
130
     *   properties that are not explicitly defined in an object type
131
     *   definition. If specified, the value MUST be a schema or a boolean. If
132
     *   false is provided, no additional properties are allowed beyond the
133
     *   properties defined in the schema. The default value is an empty schema
134
     *   which allows any value for additional properties.
135
     *
136
     * - items: This attribute defines the allowed items in an instance array,
137
     *   and MUST be a schema or an array of schemas. The default value is an
138
     *   empty schema which allows any value for items in the instance array.
139
     *   When this attribute value is a schema and the instance value is an
140
     *   array, then all the items in the array MUST be valid according to the
141
     *   schema.
142
     *
143
     * - pattern: When the type is a string, you can specify the regex pattern
144
     *   that a value must match
145
     *
146
     * - enum: When the type is a string, you can specify a list of acceptable
147
     *   values.
148
     *
149
     * - minItems: (int) Minimum number of items allowed in an array
150
     *
151
     * - maxItems: (int) Maximum number of items allowed in an array
152
     *
153
     * - minLength: (int) Minimum length of a string
154
     *
155
     * - maxLength: (int) Maximum length of a string
156
     *
157
     * - minimum: (int) Minimum value of an integer
158
     *
159
     * - maximum: (int) Maximum value of an integer
160
     *
161
     * - data: (array) Any additional custom data to use when serializing,
162
     *   validating, etc
163
     *
164
     * - format: (string) Format used to coax a value into the correct format
165
     *   when serializing or unserializing. You may specify either an array of
166
     *   filters OR a format, but not both. Supported values: date-time, date,
167
     *   time, timestamp, date-time-http, and boolean-string.
168
     *
169
     * - $ref: (string) String referencing a service description model. The
170
     *   parameter is replaced by the schema contained in the model.
171
     *
172
     * @param array $data    Array of data as seen in service descriptions
173
     * @param array $options Options used when creating the parameter. You can
174
     *     specify a Guzzle service description in the 'description' key.
175
     *
176
     * @throws \InvalidArgumentException
177
     */
178 34
    public function __construct(array $data = [], array $options = [])
179
    {
180 34
        $this->originalData = $data;
181
182 34
        if (isset($options['description'])) {
183 7
            $this->serviceDescription = $options['description'];
184 7
            if (!($this->serviceDescription instanceof DescriptionInterface)) {
185 1
                throw new \InvalidArgumentException('description must be a Description');
186
            }
187 6
            if (isset($data['$ref'])) {
188 1
                if ($model = $this->serviceDescription->getModel($data['$ref'])) {
189 1
                    $name = isset($data['name']) ? $data['name'] : null;
190 1
                    $data = $model->toArray() + $data;
191 1
                    if ($name) {
192
                        $data['name'] = $name;
193
                    }
194 1
                }
195 6
            } elseif (isset($data['extends'])) {
196
                // If this parameter extends from another parameter then start
197
                // with the actual data union in the parent's data (e.g. actual
198
                // supersedes parent)
199 1
                if ($extends = $this->serviceDescription->getModel($data['extends'])) {
200 1
                    $data += $extends->toArray();
201 1
                }
202 1
            }
203 6
        }
204
205
        // Pull configuration data into the parameter
206 33
        foreach ($data as $key => $value) {
207 31
            $this->{$key} = $value;
208 33
        }
209
210 33
        $this->required = (bool) $this->required;
211 33
        $this->data = (array) $this->data;
212
213 33
        if ($this->filters) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->filters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
214 10
            $this->setFilters((array) $this->filters);
215 9
        }
216
217 32
        if ($this->type == 'object' && $this->additionalProperties === null) {
218 2
            $this->additionalProperties = true;
219 2
        }
220 32
    }
221
222
    /**
223
     * Convert the object to an array
224
     *
225
     * @return array
226
     */
227 7
    public function toArray()
228
    {
229 7
        return $this->originalData;
230
    }
231
232
    /**
233
     * Get the default or static value of the command based on a value
234
     *
235
     * @param string $value Value that is currently set
236
     *
237
     * @return mixed Returns the value, a static value if one is present, or a default value
238
     */
239 4
    public function getValue($value)
240
    {
241 4
        if ($this->static || ($this->default !== null && $value === null)) {
242 2
            return $this->default;
243
        }
244
245 2
        return $value;
246
    }
247
248
    /**
249
     * Run a value through the filters OR format attribute associated with the
250
     * parameter.
251
     *
252
     * @param mixed $value Value to filter
253
     *
254
     * @return mixed Returns the filtered value
255
     * @throws \RuntimeException when trying to format when no service
256
     *     description is available.
257
     */
258 8
    public function filter($value)
259
    {
260
        // Formats are applied exclusively and supersed filters
261 8
        if ($this->format) {
262 5
            if (!$this->serviceDescription) {
263 1
                throw new \RuntimeException('No service description was set so '
264 1
                    . 'the value cannot be formatted.');
265
            }
266 4
            return $this->serviceDescription->format($this->format, $value);
267
        }
268
269
        // Convert Boolean values
270 3
        if ($this->type == 'boolean' && !is_bool($value)) {
271 1
            $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
272 1
        }
273
274
        // Apply filters to the value
275 3
        if ($this->filters) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->filters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
276 2
            foreach ($this->filters as $filter) {
277 2
                if (is_array($filter)) {
278
                    // Convert complex filters that hold value place holders
279 1
                    foreach ($filter['args'] as &$data) {
280 1
                        if ($data == '@value') {
281 1
                            $data = $value;
282 1
                        } elseif ($data == '@api') {
283 1
                            $data = $this;
284 1
                        }
285 1
                    }
286 1
                    $value = call_user_func_array(
287 1
                        $filter['method'],
288 1
                        $filter['args']
289 1
                    );
290 1
                } else {
291 1
                    $value = call_user_func($filter, $value);
292
                }
293 2
            }
294 2
        }
295
296 3
        return $value;
297
    }
298
299
    /**
300
     * Get the name of the parameter
301
     *
302
     * @return string
303
     */
304 1
    public function getName()
305
    {
306 1
        return $this->name;
307
    }
308
309
    /**
310
     * Set the name of the parameter
311
     *
312
     * @param string $name Name to set
313
     */
314 1
    public function setName($name)
315
    {
316 1
        $this->name = $name;
317 1
    }
318
319
    /**
320
     * Get the key of the parameter, where sentAs will supersede name if it is
321
     * set.
322
     *
323
     * @return string
324
     */
325 1
    public function getWireName()
326
    {
327 1
        return $this->sentAs ?: $this->name;
328
    }
329
330
    /**
331
     * Get the type(s) of the parameter
332
     *
333
     * @return string|array
334
     */
335 2
    public function getType()
336
    {
337 2
        return $this->type;
338
    }
339
340
    /**
341
     * Get if the parameter is required
342
     *
343
     * @return bool
344
     */
345 1
    public function isRequired()
346
    {
347 1
        return $this->required;
348
    }
349
350
    /**
351
     * Get the default value of the parameter
352
     *
353
     * @return string|null
354
     */
355 1
    public function getDefault()
356
    {
357 1
        return $this->default;
358
    }
359
360
    /**
361
     * Get the description of the parameter
362
     *
363
     * @return string|null
364
     */
365 1
    public function getDescription()
366
    {
367 1
        return $this->description;
368
    }
369
370
    /**
371
     * Get the minimum acceptable value for an integer
372
     *
373
     * @return int|null
374
     */
375 1
    public function getMinimum()
376
    {
377 1
        return $this->minimum;
378
    }
379
380
    /**
381
     * Get the maximum acceptable value for an integer
382
     *
383
     * @return int|null
384
     */
385 1
    public function getMaximum()
386
    {
387 1
        return $this->maximum;
388
    }
389
390
    /**
391
     * Get the minimum allowed length of a string value
392
     *
393
     * @return int
394
     */
395 1
    public function getMinLength()
396
    {
397 1
        return $this->minLength;
398
    }
399
400
    /**
401
     * Get the maximum allowed length of a string value
402
     *
403
     * @return int|null
404
     */
405 1
    public function getMaxLength()
406
    {
407 1
        return $this->maxLength;
408
    }
409
410
    /**
411
     * Get the maximum allowed number of items in an array value
412
     *
413
     * @return int|null
414
     */
415 1
    public function getMaxItems()
416
    {
417 1
        return $this->maxItems;
418
    }
419
420
    /**
421
     * Get the minimum allowed number of items in an array value
422
     *
423
     * @return int
424
     */
425 1
    public function getMinItems()
426
    {
427 1
        return $this->minItems;
428
    }
429
430
    /**
431
     * Get the location of the parameter
432
     *
433
     * @return string|null
434
     */
435 2
    public function getLocation()
436
    {
437 2
        return $this->location;
438
    }
439
440
    /**
441
     * Get the sentAs attribute of the parameter that used with locations to
442
     * sentAs an attribute when it is being applied to a location.
443
     *
444
     * @return string|null
445
     */
446 1
    public function getSentAs()
447
    {
448 1
        return $this->sentAs;
449
    }
450
451
    /**
452
     * Retrieve a known property from the parameter by name or a data property
453
     * by name. When no specific name value is passed, all data properties
454
     * will be returned.
455
     *
456
     * @param string|null $name Specify a particular property name to retrieve
457
     *
458
     * @return array|mixed|null
459
     */
460 1
    public function getData($name = null)
461
    {
462 1
        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...
463 1
            return $this->data;
464 1
        } elseif (isset($this->data[$name])) {
465 1
            return $this->data[$name];
466 1
        } elseif (isset($this->{$name})) {
467 1
            return $this->{$name};
468
        }
469
470 1
        return null;
471
    }
472
473
    /**
474
     * Get whether or not the default value can be changed
475
     *
476
     * @return bool
477
     */
478 1
    public function isStatic()
479
    {
480 1
        return $this->static;
481
    }
482
483
    /**
484
     * Get an array of filters used by the parameter
485
     *
486
     * @return array
487
     */
488 2
    public function getFilters()
489
    {
490 2
        return $this->filters ?: [];
491
    }
492
493
    /**
494
     * Get the properties of the parameter
495
     *
496
     * @return Parameter[]
497
     */
498 1
    public function getProperties()
499
    {
500 1
        if (!$this->propertiesCache) {
501 1
            $this->propertiesCache = [];
502 1
            foreach (array_keys($this->properties) as $name) {
503 1
                $this->propertiesCache[$name] = $this->getProperty($name);
504 1
            }
505 1
        }
506
507 1
        return $this->propertiesCache;
508
    }
509
510
    /**
511
     * Get a specific property from the parameter
512
     *
513
     * @param string $name Name of the property to retrieve
514
     *
515
     * @return null|Parameter
516
     */
517 1
    public function getProperty($name)
518
    {
519 1
        if (!isset($this->properties[$name])) {
520 1
            return null;
521
        }
522
523 1
        if (!($this->properties[$name] instanceof self)) {
524 1
            $this->properties[$name]['name'] = $name;
525 1
            $this->properties[$name] = new static(
526 1
                $this->properties[$name],
527 1
                ['description' => $this->serviceDescription]
528 1
            );
529 1
        }
530
531 1
        return $this->properties[$name];
532
    }
533
534
    /**
535
     * Get the additionalProperties value of the parameter
536
     *
537
     * @return bool|Parameter|null
538
     */
539 1
    public function getAdditionalProperties()
540
    {
541 1
        if (is_array($this->additionalProperties)) {
542 1
            $this->additionalProperties = new static(
543 1
                $this->additionalProperties,
544 1
                ['description' => $this->serviceDescription]
545 1
            );
546 1
        }
547
548 1
        return $this->additionalProperties;
549
    }
550
551
    /**
552
     * Get the item data of the parameter
553
     *
554
     * @return Parameter
555
     */
556 1
    public function getItems()
557
    {
558 1
        if (is_array($this->items)) {
559 1
            $this->items = new static(
560 1
                $this->items,
561 1
                ['description' => $this->serviceDescription]
562 1
            );
563 1
        }
564
565 1
        return $this->items;
566
    }
567
568
    /**
569
     * Get the enum of strings that are valid for the parameter
570
     *
571
     * @return array|null
572
     */
573 1
    public function getEnum()
574
    {
575 1
        return $this->enum;
576
    }
577
578
    /**
579
     * Get the regex pattern that must match a value when the value is a string
580
     *
581
     * @return string
582
     */
583 1
    public function getPattern()
584
    {
585 1
        return $this->pattern;
586
    }
587
588
    /**
589
     * Get the format attribute of the schema
590
     *
591
     * @return string
592
     */
593 4
    public function getFormat()
594
    {
595 4
        return $this->format;
596
    }
597
598
    /**
599
     * Set the array of filters used by the parameter
600
     *
601
     * @param array $filters Array of functions to use as filters
602
     *
603
     * @return self
604
     */
605 10
    private function setFilters(array $filters)
606
    {
607 10
        $this->filters = [];
608 10
        foreach ($filters as $filter) {
609 10
            $this->addFilter($filter);
610 9
        }
611
612 9
        return $this;
613
    }
614
615
    /**
616
     * Add a filter to the parameter
617
     *
618
     * @param string|array $filter Method to filter the value through
619
     *
620
     * @return self
621
     * @throws \InvalidArgumentException
622
     */
623 10
    private function addFilter($filter)
624
    {
625 10
        if (is_array($filter)) {
626 2
            if (!isset($filter['method'])) {
627 1
                throw new \InvalidArgumentException(
628
                    'A [method] value must be specified for each complex filter'
629 1
                );
630
            }
631 1
        }
632
633 9
        if (!$this->filters) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->filters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
634 9
            $this->filters = [$filter];
635 9
        } else {
636 7
            $this->filters[] = $filter;
637
        }
638
639 9
        return $this;
640
    }
641
642
    /**
643
     * Check if a parameter has a specific variable and if it set.
644
     *
645
     * @param string $var
646
     * @return bool
647
     */
648 3
    public function has($var)
649
    {
650 3
        if (!is_string($var)) {
651 1
            throw new \InvalidArgumentException('Expected a string. Got: ' . (is_object($var) ? get_class($var) : gettype($var)));
652
        }
653 2
        return isset($this->{$var}) && !empty($this->{$var});
654
    }
655
}
656