Configuration   C
last analyzed

Complexity

Total Complexity 70

Size/Duplication

Total Lines 555
Duplicated Lines 1.98 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 91.35%

Importance

Changes 0
Metric Value
wmc 70
lcom 1
cbo 3
dl 11
loc 555
ccs 169
cts 185
cp 0.9135
rs 5.6163
c 0
b 0
f 0

43 Methods

Rating   Name   Duplication   Size   Complexity  
A getExposureDepth() 0 4 1
A addRouteBasePath() 0 7 2
A getDefaultErrorHandlerClass() 0 4 1
B __construct() 0 34 1
A setDebugMode() 0 4 1
A setDefaultRepresentations() 0 4 1
A setMetadataDriverClass() 0 3 1
A getMetadataDriverClass() 0 3 1
A registerRequestAdapterClasses() 0 6 2
A registerRequestAdapterClass() 0 6 2
A containsRequestAdapterClass() 0 4 1
A searchForAdapterClass() 0 7 2
A registerResponseAdapterClasses() 0 6 2
A registerResponseAdapterClass() 0 6 2
A containsResponseAdapterClass() 0 4 1
A setExposureDepth() 0 4 1
B setExposureRelationsFetchType() 0 13 5
A setAllowOptionsRequest() 0 4 1
A set415ForNoMediaMatch() 0 4 1
A setMetadataCacheImpl() 0 4 1
A setDetectContentOption() 0 8 2
A getDetectContentOptions() 0 4 1
A setDetectContentOptions() 0 7 2
A get415ForNoMediaMatchSetting() 0 4 1
A setExposeRequestOption() 0 8 2
A getExposeRequestOptions() 0 4 1
A setExposeRequestOptions() 0 7 2
A getExposureRelationsFetchType() 0 8 2
A unregisterResponseAdapterClass() 0 6 2
A getRegisteredResponseAdapterClasses() 0 4 1
A unregisterRequestAdapterClass() 0 6 2
A getRegisteredRequestAdapterClasses() 0 4 1
A getAllowOptionsRequest() 0 4 1
A addPathsToConfigFiles() 0 7 2
A removePathsToConfigFiles() 6 11 3
A getPathsToConfigFiles() 0 4 1
A removeRouteBasePath() 5 14 3
A hasRouteBasePaths() 0 10 3
A getRouteBasePaths() 0 4 1
A getDefaultRepresentations() 0 4 1
A ensureProductionSettings() 0 10 3
A inDebugMode() 0 4 1
A getMetadataCacheImpl() 0 6 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Configuration 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 Configuration, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * This file is part of the Drest package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author Lee Davis
9
 * @copyright Copyright (c) Lee Davis <@leedavis81>
10
 * @link https://github.com/leedavis81/drest/blob/master/LICENSE
11
 * @license http://opensource.org/licenses/MIT The MIT X License (MIT)
12
 */
13
namespace Drest;
14
15
use Doctrine\Common\Cache\Cache;
16
use Doctrine\ORM\Mapping\ClassMetadataInfo as ORMClassMetaDataInfo;
17
use DrestCommon\Request\Request;
18
use DrestCommon\Response\Response;
19
20
class Configuration
21
{
22
    const DETECT_CONTENT_HEADER = 1;
23
    const DETECT_CONTENT_EXTENSION = 2;
24
    const DETECT_CONTENT_PARAM = 3;
25
    const EXPOSE_REQUEST_HEADER = 1;
26
    const EXPOSE_REQUEST_PARAM = 2;
27
    const EXPOSE_REQUEST_PARAM_GET = 3;
28
    const EXPOSE_REQUEST_PARAM_POST = 4;
29
30
    // any alteration will have an effect on drest-common (AbstractRepresentation.php)
31
    public static $detectContentOptions = [
32
        self::DETECT_CONTENT_HEADER => 'Header',
33
        self::DETECT_CONTENT_EXTENSION => 'Extension',
34
        self::DETECT_CONTENT_PARAM => 'Parameter'
35
    ];
36
    public static $exposeRequestOptions = [
37
        self::EXPOSE_REQUEST_HEADER => 'X-Expose',
38
        self::EXPOSE_REQUEST_PARAM => 'Parameter',
39
        self::EXPOSE_REQUEST_PARAM_GET => 'Get Parameter',
40
        self::EXPOSE_REQUEST_PARAM_POST => 'Post Parameter'
41
    ];
42
    /**
43
     * Configuration attributes
44
     * @var array
45
     */
46
    protected $_attributes = [];
47
48
    /**
49
     * Set configuration defaults
50
     */
51 47
    public function __construct()
52
    {
53
        // Turn off debug mode
54 47
        $this->setDebugMode(false);
55
        // Allow content detection using the Accept header
56 47
        $this->setDetectContentOptions([self::DETECT_CONTENT_HEADER => 'Accept']);
57
        // Use Json and XML as the default representations
58
        // @todo: This probably should be registered in this way. Use a similar method as the adapter classes
59 47
        $this->setDefaultRepresentations(array('Json', 'Xml'));
60
        // Set the default method for retrieving class metadata.
61 47
        $this->setMetadataDriverClass('\Drest\Mapping\Driver\AnnotationDriver');
62
        // register the default request adapter classes
63 47
        $this->_attributes['requestAdapterClasses'] = [];
64 47
        $this->registerRequestAdapterClasses(Request::$defaultAdapterClasses);
65
        // register the default response adapter classes
66 47
        $this->_attributes['responseAdapterClasses'] = [];
67 47
        $this->registerResponseAdapterClasses(Response::$defaultAdapterClasses);
68
        // Depth of exposure on entity fields => relations
69 47
        $this->setExposureDepth(2);
70
        // Don't follow any relation type
71 47
        $this->setExposureRelationsFetchType(null);
72
        // Don't set any expose request options
73 47
        $this->setExposeRequestOptions([]);
74
        // Allow OPTIONS request on resources
75 47
        $this->setAllowOptionsRequest(true);
76
        // Set the route base paths to be an empty array
77 47
        $this->_attributes['routeBasePaths'] = [];
78
        // Set the paths to the config files as an empty array
79 47
        $this->_attributes['pathsToConfigFiles'] = [];
80
        // Don't send a 415 if we don't match a representation class, default to first available one
81 47
        $this->set415ForNoMediaMatch(false);
82
        // Set the default error handler class (immutable)
83 47
        $this->_attributes['defaultErrorHandlerClass'] = 'DrestCommon\\Error\\Handler\\DefaultHandler';
84 47
    }
85
86
    /**
87
     * Set the debug mode - when on all DrestExceptions are rethrown,
88
     * otherwise 500 errors are returned from the REST service
89
     * Should be switched off in production
90
     * @param boolean $setting
91
     */
92 47
    public function setDebugMode($setting)
93
    {
94 47
        $this->_attributes['debugMode'] = (bool) $setting;
95 47
    }
96
97
    /**
98
     * Set the default representation classes to be used across the entire API.
99
     * Any representations defined locally on a resource will take precedence
100
     * @param string[] $representations
101
     */
102 47
    public function setDefaultRepresentations(array $representations)
103
    {
104 47
        $this->_attributes['defaultRepresentations'] = $representations;
105 47
    }
106
107
    /**
108
     * Sets the class name of the metadata driver for instantiation.
109
     *
110
     * @param string $driver
111
     */
112 47
    public function setMetadataDriverClass($driver) {
113 47
        $this->_attributes['metaDataDriver'] = $driver;
114 47
    }
115
116
    /**
117
     * Returns the class name of the metadata driver.
118
     * @return string The namespaced class name.
119
     */
120 31
    public function getMetadataDriverClass() {
121 31
        return $this->_attributes['metaDataDriver'];
122
    }
123
124
125
    /**
126
     * Register an array of request adapter classes
127
     * @param array $classes
128
     */
129 47
    public function registerRequestAdapterClasses(array $classes)
130
    {
131 47
        foreach ($classes as $class) {
132 47
            $this->registerRequestAdapterClass($class);
133 47
        }
134 47
    }
135
136
    /**
137
     * Register a class name to be used as a request adapter
138
     * @param string $class
139
     */
140 47
    public function registerRequestAdapterClass($class)
141
    {
142 47
        if ($this->containsRequestAdapterClass($class) === false) {
143 47
            $this->_attributes['requestAdapterClasses'][] = $class;
144 47
        }
145 47
    }
146
147
    /**
148
     * Does this configuration contain a request adapter class by name
149
     * @param  string          $className
150
     * @return boolean|integer returns the offset position if it exists (can be zero, do type check)
151
     */
152 47
    public function containsRequestAdapterClass($className)
153
    {
154 47
        return $this->searchForAdapterClass($className, 'requestAdapterClasses');
155
    }
156
157
    /**
158
     * Search for a request/response adapter class by name
159
     * @param $className
160
     * @param $adapterType
161
     * @return bool|integer returns the offset position if it exists
162
     */
163 47
    protected function searchForAdapterClass($className, $adapterType)
164
    {
165 47
        if (($offset = array_search($className, $this->_attributes[$adapterType])) !== false) {
166 2
            return $offset;
167
        }
168 47
        return false;
169
    }
170
171
    /**
172
     * Register an array of response adapter classes
173
     * @param array $classes
174
     */
175 47
    public function registerResponseAdapterClasses(array $classes)
176
    {
177 47
        foreach ($classes as $class) {
178 47
            $this->registerResponseAdapterClass($class);
179 47
        }
180 47
    }
181
182
    /**
183
     * Register a class name to be used as a response adapter
184
     * @param string $class
185
     */
186 47
    public function registerResponseAdapterClass($class)
187
    {
188 47
        if ($this->containsResponseAdapterClass($class) === false) {
189 47
            $this->_attributes['responseAdapterClasses'][] = $class;
190 47
        }
191 47
    }
192
193
    /**
194
     * Does this configuration contain a response adapter class by name
195
     * @param  string          $className
196
     * @return boolean|integer returns the offset position if it exists (can be zero, do type check)
197
     */
198 47
    public function containsResponseAdapterClass($className)
199
    {
200 47
        return $this->searchForAdapterClass($className, 'responseAdapterClasses');
201
    }
202
203
    /**
204
     * Set the default depth of columns to expose to client
205
     * @param integer $depth
206
     */
207 47
    public function setExposureDepth($depth)
208
    {
209 47
        $this->_attributes['defaultExposureDepth'] = (int) $depth;
210 47
    }
211
212
    /**
213
     * Set the exposure fields by following relations that have the a certain fetch type.
214
     * This is useful if you only want to display fields that are loaded eagerly.
215
     * eg ->setExposureRelationsFetchType(ORMClassMetaDataInfo::FETCH_EAGER)
216
     * @param  integer        $fetch
217
     * @throws DrestException
218
     */
219 47
    public function setExposureRelationsFetchType($fetch)
220
    {
221
        switch ($fetch) {
222 47
            case ORMClassMetaDataInfo::FETCH_EAGER:
223 47
            case ORMClassMetaDataInfo::FETCH_LAZY:
224 47
            case ORMClassMetaDataInfo::FETCH_EXTRA_LAZY:
225 47
            case null:
226 47
                $this->_attributes['defaultExposureRelationsFetchType'] = $fetch;
227 47
                break;
228 1
            default:
229 1
                throw DrestException::invalidExposeRelationFetchType();
230 1
        }
231 47
    }
232
233
    /**
234
     * A setting to generically allow OPTIONS requests across the entire API.
235
     * This can be overridden by using the @Route\Metadata $allowOptions parameter
236
     * @param boolean $value
237
     */
238 47
    public function setAllowOptionsRequest($value)
239
    {
240 47
        $this->_attributes['allowOptionsRequest'] = (bool) $value;
241 47
    }
242
243
    /**
244
     * When no content type is detected, the response will default to using the first available.
245
     * To switch this feature off and send a 415 error, call this configuration function
246
     * @param boolean $value
247
     */
248 47
    public function set415ForNoMediaMatch($value = true)
249
    {
250 47
        $this->_attributes['send415ForNoMediaMatch'] = (bool) $value;
251 47
    }
252
253
    /**
254
     * Sets the cache driver implementation that is used for metadata caching.
255
     *
256
     * @param \Doctrine\Common\Cache\Cache $cacheImpl
257
     */
258 33
    public function setMetadataCacheImpl(Cache $cacheImpl)
259
    {
260 33
        $this->_attributes['metadataCacheImpl'] = $cacheImpl;
261 33
    }
262
263
    /**
264
     * Set a content option for detecting the media type to be used. To unset pass null as a value
265
     * For any options that don't required a value, set them to true to activate them
266
     * @param  integer        $option
267
     * @param  string         $value
268
     * @throws DrestException
269
     */
270 47
    public function setDetectContentOption($option, $value)
271
    {
272 47
        if (array_key_exists($option, self::$detectContentOptions)) {
273 47
            $this->_attributes['detectContentOptions'][$option] = $value;
274 47
        } else {
275 1
            throw DrestException::unknownDetectContentOption();
276
        }
277 47
    }
278
279
    /**
280
     * Get detect content options. Returns an array indexed using constants as array key
281
     * (value will be the value to be used for the content options)
282
     * Eg array(self::DETECT_CONTENT_HEADER => 'Accept')
283
     * @return array
284
     */
285 27
    public function getDetectContentOptions()
286
    {
287 27
        return $this->_attributes['detectContentOptions'];
288
    }
289
290
    /**
291
     * Set the methods to be used for detecting content type to be used to pull requests, overwrites previous settings
292
     * Eg ->setDetectContentOptions(array(self::DETECT_CONTENT_HEADER => $headerName))
293
     * self::DETECT_CONTENT_HEADER           = Uses the a header to detect the required content (typically use Accept)
294
     * self::DETECT_CONTENT_EXTENSION        = Uses an extension on the url eg .xml
295
     * self::DETECT_CONTENT_PARAM            = Uses a the "format" parameter
296
     * @param array - pass either a single array value using the constant value as a key, or a multi-dimensional array.
297
     */
298 47
    public function setDetectContentOptions(array $options)
299
    {
300 47
        $this->_attributes['detectContentOptions'] = [];
301 47
        foreach ($options as $key => $value) {
302 47
            $this->setDetectContentOption($key, $value);
303 47
        }
304 47
    }
305
306
    /**
307
     * Get the 415 for no representation match setting
308
     * @return boolean
309
     */
310 4
    public function get415ForNoMediaMatchSetting()
311
    {
312 4
        return $this->_attributes['send415ForNoMediaMatch'];
313
    }
314
315
    /**
316
     * Method used to retrieve the required expose contents from the client. To unset pass null as value
317
     * @param  integer        $option
318
     * @param  string         $value
319
     * @throws DrestException
320
     */
321 2
    public function setExposeRequestOption($option, $value)
322
    {
323 2
        if (array_key_exists($option, self::$exposeRequestOptions)) {
324 1
            $this->_attributes['exposeRequestOptions'][$option] = $value;
325 1
        } else {
326 1
            throw DrestException::unknownExposeRequestOption();
327
        }
328 1
    }
329
330
    /**
331
     * Get the expose request options
332
     * @return array $options
333
     */
334 19
    public function getExposeRequestOptions()
335
    {
336 19
        return $this->_attributes['exposeRequestOptions'];
337
    }
338
339
    /**
340
     * Set the methods to be used for detecting the expose content from the client. Overwrites any previous value
341
     * Eg ->setExposeRequestOptions(array(self::EXPOSE_REQUEST_HEADER => $headerName))
342
     * @param array $options
343
     */
344 47
    public function setExposeRequestOptions(array $options)
345
    {
346 47
        $this->_attributes['exposeRequestOptions'] = [];
347 47
        foreach ($options as $key => $value) {
348 2
            $this->setExposeRequestOption($key, $value);
349 47
        }
350 47
    }
351
352
    /**
353
     * Get the default exposure depth
354
     * @return integer $depth
355
     */
356 22
    public function getExposureDepth()
357
    {
358 22
        return (int) $this->_attributes['defaultExposureDepth'];
359
    }
360
361
    /**
362
     * Gets the configured expose relations fetch type - returns null if not set
363
     * @return integer|null $result
364
     */
365 23
    public function getExposureRelationsFetchType()
366
    {
367 23
        if (isset($this->_attributes['defaultExposureRelationsFetchType'])) {
368 1
            return $this->_attributes['defaultExposureRelationsFetchType'];
369
        }
370
371 22
        return null;
372
    }
373
374
    /**
375
     * Un-register an adapter class name entry
376
     * @param string $class
377
     */
378 1
    public function unregisterResponseAdapterClass($class)
379
    {
380 1
        if (($offset = $this->containsResponseAdapterClass($class)) !== false) {
381 1
            unset($this->_attributes['responseAdapterClasses'][$offset]);
382 1
        }
383 1
    }
384
385
    /**
386
     * get the registered response adapted classes
387
     * @return array $class_name - a string array of class names
388
     */
389 32
    public function getRegisteredResponseAdapterClasses()
390
    {
391 32
        return $this->_attributes['responseAdapterClasses'];
392
    }
393
394
    /**
395
     * Un-register an adapter class name entry
396
     * @param string $class
397
     */
398 1
    public function unregisterRequestAdapterClass($class)
399
    {
400 1
        if (($offset = $this->containsRequestAdapterClass($class)) !== false) {
401 1
            unset($this->_attributes['requestAdapterClasses'][$offset]);
402 1
        }
403 1
    }
404
405
    /**
406
     * get the registered request adapted classes
407
     * @return array $class_name - a string array of class names
408
     */
409 32
    public function getRegisteredRequestAdapterClasses()
410
    {
411 32
        return $this->_attributes['requestAdapterClasses'];
412
    }
413
414
    /**
415
     * Are we globally allowing OPTIONS requests across all routes
416
     * @return boolean $value
417
     */
418 2
    public function getAllowOptionsRequest()
419
    {
420 2
        return $this->_attributes['allowOptionsRequest'];
421
    }
422
423
    /**
424
     * Register paths to your configuration files. This will typically be where your entities live
425
     * This will overwrite any previously registered paths. To add new one use addPathsToConfigFiles($paths)
426
     * @param array $paths
427
     */
428 31
    public function addPathsToConfigFiles($paths = [])
429
    {
430 31
        if (!isset($this->_attributes['pathsToConfigFiles'])) {
431
            $this->_attributes['pathsToConfigFiles'] = [];
432
        }
433 31
        $this->_attributes['pathsToConfigFiles'] = array_merge($this->_attributes['pathsToConfigFiles'], (array) $paths);
434 31
    }
435
436
    /**
437
     * Remove all the registered paths to config files, or just a specific entry $path
438
     * @param string $path
439
     */
440
    public function removePathsToConfigFiles($path = null)
441
    {
442
        if (is_null($path)) {
443
            $this->_attributes['pathsToConfigFiles'] = [];
444 View Code Duplication
        } else {
445
            if (($offset = array_search($path, $this->_attributes['pathsToConfigFiles'])) !== false)
446
            {
447
                unset($this->_attributes['pathsToConfigFiles'][$offset]);
448
            }
449
        }
450
    }
451
452
    /**
453
     * Get the paths to the drest configuration files
454
     * @return array $paths
455
     */
456 31
    public function getPathsToConfigFiles()
457
    {
458 31
        return $this->_attributes['pathsToConfigFiles'];
459
    }
460
461
    /**
462
     * Add a base path to be used when matching routes. Eg /v1 would be useful IF you want versioning in the URL
463
     * @param  string         $basePath
464
     * @throws DrestException
465
     */
466 3
    public function addRouteBasePath($basePath)
467
    {
468 3
        if (!is_string($basePath)) {
469 1
            throw DrestException::basePathMustBeAString();
470
        }
471 2
        $this->_attributes['routeBasePaths'][] = trim($basePath, '/');
472 2
    }
473
474
    /**
475
     * Remove a route base path (if it has been registered)
476
     * @param  string  $basePath
477
     * @return boolean true if $basePath was unset
478
     */
479 1
    public function removeRouteBasePath($basePath)
480
    {
481 1
        $basePath = trim($basePath, '/');
482 1
        if (!is_string($basePath)) {
483
            return false;
484
        }
485 1 View Code Duplication
        if (($offset = array_search($basePath, $this->_attributes['routeBasePaths'])) !== false) {
486 1
            unset($this->_attributes['routeBasePaths'][$offset]);
487
488 1
            return true;
489
        }
490
491
        return false;
492
    }
493
494
    /**
495
     * Have base paths been registered - or look for a specific entry
496
     * @param  string  $basePath - optional, has a specific route path been registered
497
     * @return boolean true if route base paths have been registered
498
     */
499 28
    public function hasRouteBasePaths($basePath = null)
500
    {
501 28
        if (!is_null($basePath)) {
502
            $basePath = trim($basePath, '/');
503
504
            return in_array($basePath, $this->_attributes['routeBasePaths']);
505
        }
506
507 28
        return (sizeof($this->_attributes['routeBasePaths']) > 0) ? true : false;
508
    }
509
510
    /**
511
     * Get all registered base path or a specific entry
512
     * @return array $basePaths
513
     */
514 2
    public function getRouteBasePaths()
515
    {
516 2
        return (array) $this->_attributes['routeBasePaths'];
517
    }
518
519
    /**
520
     * Get the default representation classes to be used across the entire API
521
     * @return array representation classes
522
     */
523 2
    public function getDefaultRepresentations()
524
    {
525 2
        return (array) $this->_attributes['defaultRepresentations'];
526
    }
527
528
    /**
529
     * Get the default error handler class
530
     * @return string $className
531
     */
532 26
    public function getDefaultErrorHandlerClass()
533
    {
534 26
        return $this->_attributes['defaultErrorHandlerClass'];
535
    }
536
537
    /**
538
     * Ensures that this Configuration instance contains settings that are
539
     * suitable for a production environment.
540
     *
541
     * @throws DrestException If a configuration setting has a value that is not suitable for a production.
542
     */
543 2
    public function ensureProductionSettings()
544
    {
545 2
        if ($this->inDebugMode()) {
546 1
            throw DrestException::currentlyRunningDebugMode();
547
        }
548
549 1
        if (!$this->getMetadataCacheImpl()) {
550 1
            throw DrestException::metadataCacheNotConfigured();
551
        }
552
    }
553
554
    /**
555
     * Are we in debug mode?
556
     * @return boolean
557
     */
558 7
    public function inDebugMode()
559
    {
560 7
        return $this->_attributes['debugMode'];
561
    }
562
563
    /**
564
     * Gets the cache driver implementation that is used for metadata caching.
565
     *
566
     * @return \Doctrine\Common\Cache\Cache
567
     */
568 33
    public function getMetadataCacheImpl()
569
    {
570 33
        return isset($this->_attributes['metadataCacheImpl'])
571 33
            ? $this->_attributes['metadataCacheImpl']
572 33
            : null;
573
    }
574
}
575