Completed
Branch master (1f16ca)
by
unknown
08:52
created

Container   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 322
Duplicated Lines 2.8 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 41
lcom 1
cbo 3
dl 9
loc 322
ccs 102
cts 102
cp 1
rs 8.2769
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
D register() 6 35 9
A registerArray() 0 6 2
A getSchema() 0 6 1
B getSchemaByType() 3 27 5
C getSchemaByResourceType() 0 28 7
A getFactory() 0 4 1
A getProviderMappings() 0 4 1
A hasProviderMapping() 0 4 1
A getProviderMapping() 0 4 1
A setProviderMapping() 0 4 1
A hasCreatedProvider() 0 4 1
A getCreatedProvider() 0 4 1
A setCreatedProvider() 0 4 1
A hasResourceToJsonTypeMapping() 0 4 1
A getJsonType() 0 4 1
A setResourceToJsonTypeMapping() 0 4 1
A getResourceType() 0 4 1
A createSchemaFromClosure() 0 6 1
A createSchemaFromCallable() 0 7 2
A createSchemaFromClassName() 0 6 1

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 Container 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 Container, and based on these observations, apply Extract Interface, too.

1
<?php namespace Neomerx\JsonApi\Schema;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use \Closure;
20
use \InvalidArgumentException;
21
use \Psr\Log\LoggerAwareTrait;
22
use \Psr\Log\LoggerAwareInterface;
23
use \Neomerx\JsonApi\Factories\Exceptions;
24
use \Neomerx\JsonApi\I18n\Translator as T;
25
use \Neomerx\JsonApi\Contracts\Schema\ContainerInterface;
26
use \Neomerx\JsonApi\Contracts\Schema\SchemaFactoryInterface;
27
use \Neomerx\JsonApi\Contracts\Schema\SchemaProviderInterface;
28
29
/**
30
 * @package Neomerx\JsonApi
31
 */
32
class Container implements ContainerInterface, LoggerAwareInterface
33
{
34
    use LoggerAwareTrait;
35
36
    /**
37
     * @var array
38
     */
39
    private $providerMapping = [];
40
41
    /**
42
     * @var SchemaProviderInterface[]
43
     */
44
    private $createdProviders = [];
45
46
    /**
47
     * @var array
48
     */
49
    private $resType2JsonType = [];
50
51
    /**
52
     * @var SchemaFactoryInterface
53
     */
54
    private $factory;
55
56
    /**
57
     * @param SchemaFactoryInterface $factory
58
     * @param array                  $schemas
59
     */
60 88
    public function __construct(SchemaFactoryInterface $factory, array $schemas = [])
61
    {
62 88
        $this->factory = $factory;
63 88
        $this->registerArray($schemas);
64 86
    }
65
66
    /**
67
     * Register provider for resource type.
68
     *
69
     * @param string         $type
70
     * @param string|Closure $schema
71
     *
72
     * @return void
73
     */
74 77
    public function register($type, $schema)
75
    {
76
        // Type must be non-empty string
77 77
        $isOk = (is_string($type) === true && empty($type) === false);
78 77
        if ($isOk === false) {
79 1
            throw new InvalidArgumentException(T::t('Type must be non-empty string.'));
80
        }
81
82
        $isOk = (
83 76
            (is_string($schema) === true && empty($schema) === false) ||
84 36
            is_callable($schema) ||
85
            $schema instanceof SchemaProviderInterface
86 76
        );
87 76
        if ($isOk === false) {
88 1
            throw new InvalidArgumentException(T::t(
89 1
                'Schema for type \'%s\' must be non-empty string, callable or SchemaProviderInterface instance.',
90 1
                [$type]
91 1
            ));
92
        }
93
94 75 View Code Duplication
        if ($this->hasProviderMapping($type) === true) {
95 1
            throw new InvalidArgumentException(T::t(
96 1
                'Type should not be used more than once to register a schema (\'%s\').',
97 1
                [$type]
98 1
            ));
99 1
        }
100
101 75
        if ($schema instanceof SchemaProviderInterface) {
102 2
            $this->setProviderMapping($type, $jsonType = get_class($schema));
103 2
            $this->setResourceToJsonTypeMapping($schema->getResourceType(), $type);
104 2
            $this->setCreatedProvider($type, $schema);
105 2
        } else {
106 73
            $this->setProviderMapping($type, $schema);
107
        }
108 75
    }
109
110
    /**
111
     * Register providers for resource types.
112
     *
113
     * @param array $schemas
114
     *
115
     * @return void
116
     */
117 88
    public function registerArray(array $schemas)
118
    {
119 88
        foreach ($schemas as $type => $schema) {
120 76
            $this->register($type, $schema);
121 86
        }
122 86
    }
123
124
    /**
125
     * @inheritdoc
126
     */
127 66
    public function getSchema($resource)
128
    {
129 66
        $resourceType = $this->getResourceType($resource);
130
131 66
        return $this->getSchemaByType($resourceType);
132
    }
133
134
    /**
135
     * @inheritdoc
136
     */
137 68
    public function getSchemaByType($type)
138
    {
139 68
        is_string($type) === true ?: Exceptions::throwInvalidArgument('type', $type);
140
141 68
        if ($this->hasCreatedProvider($type) === true) {
142 59
            return $this->getCreatedProvider($type);
143
        }
144
145 66 View Code Duplication
        if ($this->hasProviderMapping($type) === false) {
146 2
            throw new InvalidArgumentException(T::t('Schema is not registered for type \'%s\'.', [$type]));
147
        }
148
149 66
        $classNameOrCallable = $this->getProviderMapping($type);
150 66
        if (is_string($classNameOrCallable) === true) {
151 44
            $schema = $this->createSchemaFromClassName($classNameOrCallable);
152 44
        } else {
153 32
            assert('is_callable($classNameOrCallable) === true');
154 32
            $schema = $this->createSchemaFromCallable($classNameOrCallable);
155
        }
156 66
        $this->setCreatedProvider($type, $schema);
157
158
        /** @var SchemaProviderInterface $schema */
159
160 66
        $this->setResourceToJsonTypeMapping($schema->getResourceType(), $type);
161
162 66
        return $schema;
163
    }
164
165
    /**
166
     * @inheritdoc
167
     */
168 49
    public function getSchemaByResourceType($resourceType)
169
    {
170
        // Schema is not found among instantiated schemas for resource type $resourceType
171 49
        $isOk = (is_string($resourceType) === true && $this->hasResourceToJsonTypeMapping($resourceType) === true);
172
173
        // Schema might not be found if it hasn't been searched by type (not resource type) before.
174
        // We instantiate all schemas and then find one.
175 49
        if ($isOk === false) {
176 1
            foreach ($this->getProviderMappings() as $type => $schema) {
177 1
                if ($this->hasCreatedProvider($type) === false) {
178
                    // it will instantiate the schema
179 1
                    $this->getSchemaByType($type);
180 1
                }
181 1
            }
182 1
        }
183
184
        // search one more time
185 49
        $isOk = (is_string($resourceType) === true && $this->hasResourceToJsonTypeMapping($resourceType) === true);
186
187 49
        if ($isOk === false) {
188 1
            throw new InvalidArgumentException(T::t(
189 1
                'Schema is not registered for resource type \'%s\'.',
190 1
                [$resourceType]
191 1
            ));
192
        }
193
194 49
        return $this->getSchemaByType($this->getJsonType($resourceType));
195
    }
196
197
    /**
198
     * @return SchemaFactoryInterface
199
     */
200 66
    protected function getFactory()
201
    {
202 66
        return $this->factory;
203
    }
204
205
    /**
206
     * @return array
207
     */
208 1
    protected function getProviderMappings()
209
    {
210 1
        return $this->providerMapping;
211
    }
212
213
    /**
214
     * @param string $type
215
     *
216
     * @return bool
217
     */
218 75
    protected function hasProviderMapping($type)
219
    {
220 75
        return array_key_exists($type, $this->providerMapping);
221
    }
222
223
    /**
224
     * @param string $type
225
     *
226
     * @return mixed
227
     */
228 66
    protected function getProviderMapping($type)
229
    {
230 66
        return $this->providerMapping[$type];
231
    }
232
233
    /**
234
     * @param string         $type
235
     * @param string|Closure $schema
236
     *
237
     * @return void
238
     */
239 75
    protected function setProviderMapping($type, $schema)
240
    {
241 75
        $this->providerMapping[$type] = $schema;
242 75
    }
243
244
    /**
245
     * @param string $type
246
     *
247
     * @return bool
248
     */
249 68
    protected function hasCreatedProvider($type)
250
    {
251 68
        return array_key_exists($type, $this->createdProviders);
252
    }
253
254
    /**
255
     * @param string $type
256
     *
257
     * @return SchemaProviderInterface
258
     */
259 59
    protected function getCreatedProvider($type)
260
    {
261 59
        return $this->createdProviders[$type];
262
    }
263
264
    /**
265
     * @param string                  $type
266
     * @param SchemaProviderInterface $provider
267
     *
268
     * @return void
269
     */
270 68
    protected function setCreatedProvider($type, SchemaProviderInterface $provider)
271
    {
272 68
        $this->createdProviders[$type] = $provider;
273 68
    }
274
275
    /**
276
     * @param string $resourceType
277
     *
278
     * @return bool
279
     */
280 49
    protected function hasResourceToJsonTypeMapping($resourceType)
281
    {
282 49
        return array_key_exists($resourceType, $this->resType2JsonType);
283
    }
284
285
    /**
286
     * @param string $resourceType
287
     *
288
     * @return string
289
     */
290 49
    protected function getJsonType($resourceType)
291
    {
292 49
        return $this->resType2JsonType[$resourceType];
293
    }
294
295
    /**
296
     * @param string $resourceType
297
     * @param string $jsonType
298
     *
299
     * @return void
300
     */
301 68
    protected function setResourceToJsonTypeMapping($resourceType, $jsonType)
302
    {
303 68
        $this->resType2JsonType[$resourceType] = $jsonType;
304 68
    }
305
306
    /**
307
     * @param object $resource
308
     *
309
     * @return string
310
     */
311 66
    protected function getResourceType($resource)
312
    {
313 66
        return get_class($resource);
314
    }
315
316
    /**
317
     * @deprecated Use `createSchemaFromCallable` method instead.
318
     * @param Closure $closure
319
     *
320
     * @return SchemaProviderInterface
321
     */
322
    protected function createSchemaFromClosure(Closure $closure)
323
    {
324
        $schema = $closure($this->getFactory());
325
326
        return $schema;
327
    }
328
329
    /**
330
     * @param callable $callable
331
     *
332
     * @return SchemaProviderInterface
333
     */
334 32
    protected function createSchemaFromCallable(callable $callable)
335
    {
336 32
        $schema = $callable instanceof Closure ?
337 32
            $this->createSchemaFromClosure($callable) : call_user_func($callable, $this->getFactory());
0 ignored issues
show
Deprecated Code introduced by
The method Neomerx\JsonApi\Schema\C...eateSchemaFromClosure() has been deprecated with message: Use `createSchemaFromCallable` method instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
338
339 32
        return $schema;
340
    }
341
342
    /**
343
     * @param string $className
344
     *
345
     * @return SchemaProviderInterface
346
     */
347 43
    protected function createSchemaFromClassName($className)
348
    {
349 43
        $schema = new $className($this->getFactory());
350
351 43
        return $schema;
352
    }
353
}
354