Completed
Push — feature/issue-45 ( d6e400...7692eb )
by Mikaël
56:27 queued 25:12
created

AbstractModel   C

Complexity

Total Complexity 65

Size/Duplication

Total Lines 412
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 65
c 4
b 0
f 1
lcom 2
cbo 5
dl 0
loc 412
ccs 142
cts 142
cp 1
rs 5.7894

30 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getInheritance() 0 4 1
A setInheritance() 0 5 1
A getInheritedMoel() 0 4 1
A getMeta() 0 4 1
A setMeta() 0 5 1
B addMeta() 0 20 12
A setDocumentation() 0 4 2
A getMetaValue() 0 5 2
A getMetaValueFirstSet() 0 10 3
A getName() 0 4 1
A setName() 0 5 1
A getCleanName() 0 4 1
A getOwner() 0 4 1
A setOwner() 0 5 1
A getIsAbstract() 0 4 1
A setIsAbstract() 0 5 1
A nameIsClean() 0 4 2
A getExtendsClassName() 0 11 4
B getPackagedName() 0 19 5
A getContextualPart() 0 4 1
A getExtends() 0 4 1
B getNamespace() 0 18 5
A getSubDirectory() 0 8 2
A getDocSubPackages() 0 4 1
A cleanString() 0 4 1
B replaceReservedPhpKeyword() 0 19 5
A uniqueName() 0 15 4
A purgeUniqueNames() 0 4 1
A purgeReservedKeywords() 0 4 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace WsdlToPhp\PackageGenerator\Model;
4
5
use WsdlToPhp\PackageGenerator\ConfigurationReader\GeneratorOptions;
6
use WsdlToPhp\PackageGenerator\ConfigurationReader\ReservedKeywords;
7
use WsdlToPhp\PackageGenerator\Generator\Generator;
8
use WsdlToPhp\PackageGenerator\Generator\Utils as GeneratorUtils;
9
use WsdlToPhp\PackageGenerator\Generator\AbstractGeneratorAware;
10
11
/**
12
 * Class AbstractModel defines the basic properties and methods to operations and structs extracted from the WSDL
13
 */
14
abstract class AbstractModel extends AbstractGeneratorAware
15
{
16
    /**
17
     * Constant used to define the key to store documentation value in meta
18
     * @var string
19
     */
20
    const META_DOCUMENTATION = 'documentation';
21
    /**
22
     * Original name od the element
23
     * @var string
24
     */
25
    private $name = '';
26
    /**
27
     * Values associated to the operation
28
     * @var string[]
29
     */
30
    private $meta = array();
31
    /**
32
     * Define the inheritance of a struct by the name of the parent struct or type
33
     * @var string
34
     */
35
    private $inheritance = '';
36
    /**
37
     * Store the object which owns the current model
38
     * @var AbstractModel
39
     */
40
    private $owner = null;
41
    /**
42
     * Indicates that the current elemen is an abstract element.
43
     * It allows to generated an abstract class.
44
     * This will happen for element/complexType that are defined with abstract="true"
45
     * @var bool
46
     */
47
    private $isAbstract = false;
48
    /**
49
     * Replaced keywords time in order to generate unique new keyword
50
     * @var array
51
     */
52
    private static $replacedReservedPhpKeywords = array();
53
    /**
54
     * Unique name generated in order to ensure unique naming (for struct constructor and setters/getters even for different case attribute name whith same value)
55
     * @var array
56
     */
57
    private static $uniqueNames = array();
58
    /**
59
     * Main constructor
60
     * @uses AbstractModel::setName()
61
     * @param Generator $generator
62
     * @param string $name the original name
63
     */
64 676
    public function __construct(Generator $generator, $name)
65
    {
66 676
        parent::__construct($generator);
67 676
        $this->setName($name);
68 676
    }
69
    /**
70
     * @return string
71
     */
72 176
    public function getExtendsClassName()
73
    {
74 176
        $extends = '';
75 176
        if (($model = $this->getInheritedMoel()) instanceof Struct && $model->getIsStruct()) {
76 12
            $extends = $model->getPackagedName();
77 9
        }
78 176
        if (empty($extends)) {
79 172
            $extends = $this->getExtends(true);
80 129
        }
81 176
        return $extends;
82
    }
83
    /**
84
     * Returns the name of the class the current class inherits from
85
     * @return string
86
     */
87 228
    public function getInheritance()
88
    {
89 228
        return $this->inheritance;
90
    }
91
    /**
92
     * Sets the name of the class the current class inherits from
93
     * @param AbstractModel
94
     */
95 140
    public function setInheritance($inheritance = '')
96
    {
97 140
        $this->inheritance = $inheritance;
98 140
        return $this;
99
    }
100
    /**
101
     * @return Struct
102
     */
103 176
    public function getInheritedMoel()
104
    {
105 176
        return $this->getGenerator()->getStruct($this->getInheritance());
106
    }
107
    /**
108
     * Returns the meta
109
     * @return string[]
110
     */
111 248
    public function getMeta()
112
    {
113 248
        return $this->meta;
114
    }
115
    /**
116
     * Sets the meta
117
     * @param string[] $meta
118
     * @return AbstractModel
119
     */
120 4
    public function setMeta(array $meta = array())
121
    {
122 4
        $this->meta = $meta;
123 4
        return $this;
124
    }
125
    /**
126
     * Add meta information to the operation
127
     * @uses AbstractModel::getMeta()
128
     * @throws \InvalidArgumentException
129
     * @param string $metaName
130
     * @param mixed $metaValue
131
     * @return AbstractModel
132
     */
133 324
    public function addMeta($metaName, $metaValue)
134
    {
135 324
        if (!is_scalar($metaName) || (!is_scalar($metaValue) && !is_array($metaValue))) {
136 8
            throw new \InvalidArgumentException(sprintf('Invalid meta name "%s" or value "%s". Please provide scalar meta name and scalar or array meta value.', gettype($metaName), gettype($metaValue)), __LINE__);
137
        }
138 316
        $metaValue = is_scalar($metaValue) ? trim($metaValue) : $metaValue;
139 316
        if ((is_scalar($metaValue) && $metaValue !== '') || is_array($metaValue)) {
140 316
            if (!array_key_exists($metaName, $this->meta)) {
141 312
                $this->meta[$metaName] = $metaValue;
142 254
            } elseif (is_array($this->meta[$metaName]) && is_array($metaValue)) {
143 48
                $this->meta[$metaName] = array_merge($this->meta[$metaName], $metaValue);
144 58
            } elseif (is_array($this->meta[$metaName])) {
145 4
                array_push($this->meta[$metaName], $metaValue);
146 3
            } else {
147 24
                $this->meta[$metaName] = $metaValue;
148
            }
149 316
            ksort($this->meta);
150 237
        }
151 316
        return $this;
152
    }
153
    /**
154
     * Sets the documentation meta value.
155
     * Documentation is set as an array so if multiple documentation nodes are set for an unique element, it will gather them.
156
     * @uses AbstractModel::META_DOCUMENTATION
157
     * @uses AbstractModel::addMeta()
158
     * @param string $documentation the documentation from the WSDL
159
     * @return AbstractModel
160
     */
161 88
    public function setDocumentation($documentation)
162
    {
163 88
        return $this->addMeta(self::META_DOCUMENTATION, is_array($documentation) ? $documentation : array($documentation));
164
    }
165
    /**
166
     * Returns a meta value according to its name
167
     * @uses AbstractModel::getMeta()
168
     * @param string $metaName the meta information name
169
     * @param mixed $fallback the fallback value if unset
170
     * @return mixed the meta information value
171
     */
172 200
    public function getMetaValue($metaName, $fallback = null)
173
    {
174 200
        $meta = $this->getMeta();
175 200
        return array_key_exists($metaName, $meta) ? $meta[$metaName] : $fallback;
176
    }
177
    /**
178
     * Returns the value of the first meta value assigned to the name
179
     * @param array $names the meta names to check
180
     * @param mixed $fallback the fallback value if anyone is set
181
     * @return mixed the meta information value
182
     */
183 84
    public function getMetaValueFirstSet(array $names, $fallback = null)
184
    {
185 84
        $meta = $this->getMeta();
186 84
        foreach ($names as $name) {
187 84
            if (array_key_exists($name, $meta)) {
188 75
                return $meta[$name];
189
            }
190 42
        }
191 56
        return $fallback;
192
    }
193
    /**
194
     * Returns the original name extracted from the WSDL
195
     * @return string
196
     */
197 580
    public function getName()
198
    {
199 580
        return $this->name;
200
    }
201
    /**
202
     * Sets the original name extracted from the WSDL
203
     * @param string $name
204
     * @return AbstractModel
205
     */
206 676
    public function setName($name)
207
    {
208 676
        $this->name = $name;
209 676
        return $this;
210
    }
211
    /**
212
     * Returns a valid clean name for PHP
213
     * @uses AbstractModel::getName()
214
     * @uses AbstractModel::cleanString()
215
     * @param bool $keepMultipleUnderscores optional, allows to keep the multiple consecutive underscores
216
     * @return string
217
     */
218 472
    public function getCleanName($keepMultipleUnderscores = true)
219
    {
220 472
        return self::cleanString($this->getName(), $keepMultipleUnderscores);
221
    }
222
    /**
223
     * Returns the owner model object
224
     * @return AbstractModel
225
     */
226 468
    public function getOwner()
227
    {
228 468
        return $this->owner;
229
    }
230
    /**
231
     * Sets the owner model object
232
     * @param AbstractModel $owner object the owner of the current model
233
     * @return AbstractModel
234
     */
235 512
    public function setOwner(AbstractModel $owner)
236
    {
237 512
        $this->owner = $owner;
238 512
        return $this;
239
    }
240
    /**
241
     * @return bool
242
     */
243 180
    public function getIsAbstract()
244
    {
245 180
        return $this->isAbstract;
246
    }
247
    /**
248
     * @param bool $isAbstract
249
     * @return AbstractModel
250
     */
251 12
    public function setIsAbstract($isAbstract)
252
    {
253 12
        $this->isAbstract = $isAbstract;
254 12
        return $this;
255
    }
256
    /**
257
     * Returns true if the original name is safe to use as a PHP property, variable name or class name
258
     * @uses AbstractModel::getName()
259
     * @uses AbstractModel::getCleanName()
260
     * @return bool
261
     */
262 148
    public function nameIsClean()
263
    {
264 148
        return ($this->getName() != '' && $this->getName() == $this->getCleanName());
265
    }
266
    /**
267
     * Returns the packaged name
268
     * @uses Generator::getPackageName()
269
     * @uses AbstractModel::getCleanName()
270
     * @uses AbstractModel::getContextualPart()
271
     * @uses AbstractModel::uniqueName() to ensure unique naming of struct case sensitively
272
     * @return string
273
     */
274 460
    public function getPackagedName($namespaced = false)
275
    {
276 460
        $nameParts = array();
277 460
        if ($namespaced && $this->getNamespace() !== '') {
278 164
            $nameParts[] = sprintf('\%s\\', $this->getNamespace());
279 123
        }
280 460
        $cleanName = $this->getCleanName();
281 236
        if ($this->getGenerator()->getOptionPrefix() !== '') {
282 177
            $nameParts[] = $this->getGenerator()->getOptionPrefix();
283 460
        } else {
284 460
            $cleanName = self::replaceReservedPhpKeyword($cleanName, null);
285 24
        }
286 18
287 460
        $nameParts[] = ucfirst(self::uniqueName($cleanName, $this->getContextualPart()));
288
        if ($this->getGenerator()->getOptionSuffix() !== '') {
289
            $nameParts[] = $this->getGenerator()->getOptionSuffix();
290
        }
291
        return implode('', $nameParts);
292
    }
293 64
    /**
294
     * Allows to define the contextual part of the class name for the package
295 64
     * @return string
296
     */
297
    public function getContextualPart()
298
    {
299
        return '';
300
    }
301
    /**
302 32
     * Allows to define from which class the curent model extends
303
     * @param bool $short
304 32
     * @return string|null
305
     */
306
    public function getExtends($short = false)
307
    {
308
        return '';
309 204
    }
310
    /**
311 204
     * @return string
312 204
     */
313 204
    public function getNamespace()
314 196
    {
315 164
        $namespaces = array();
316 155
        $namespace = $this->getGenerator()->getOptionNamespacePrefix();
317 55
        if (empty($namespace)) {
318 6
            if ($this->getGenerator()->getOptionPrefix() !== '') {
319 147
                $namespaces[] = $this->getGenerator()->getOptionPrefix();
320 8
            } elseif ($this->getGenerator()->getOptionSuffix() !== '') {
321
                $namespaces[] = $this->getGenerator()->getOptionSuffix();
322 204
            }
323 200
        } else {
324 150
            $namespaces[] = $namespace;
325 204
        }
326
        if ($this->getSubDirectory() !== '') {
327
            $namespaces[] = $this->getSubDirectory();
328
        }
329
        return implode('\\', $namespaces);
330
    }
331 228
    /**
332
     * Returns directory where to store class and create it if needed
333 228
     * @return string
334 228
     */
335 228
    public function getSubDirectory()
336 171
    {
337 228
        $subDirectory = '';
338
        if ($this->getGenerator()->getOptionCategory() === GeneratorOptions::VALUE_CAT) {
339
            $subDirectory = $this->getContextualPart();
340
        }
341
        return $subDirectory;
342
    }
343
    /**
344 4
     * Returns the sub package name which the model belongs to
345
     * Must be overridden by sub classes
346 4
     * @return array
347
     */
348
    public function getDocSubPackages()
349
    {
350
        return array();
351
    }
352
    /**
353
     * Clean a string to make it valid as PHP variable
354 476
     * @param string $string the string to clean
355
     * @param bool $keepMultipleUnderscores optional, allows to keep the multiple consecutive underscores
356 476
     * @return string
357
     */
358
    public static function cleanString($string, $keepMultipleUnderscores = true)
359
    {
360
        return GeneratorUtils::cleanString($string, $keepMultipleUnderscores);
361
    }
362
    /**
363
     * Returns a usable keyword for a original keyword
364 456
     * @param string $keyword the keyword
365
     * @param string $context the context
366 456
     * @return string
367 456
     */
368 60
    public static function replaceReservedPhpKeyword($keyword, $context = null)
369 60
    {
370 36
        $phpReservedKeywordFound = '';
371 27
        if (ReservedKeywords::instance()->is($keyword)) {
372 24
            if ($context !== null) {
373
                $keywordKey = $phpReservedKeywordFound . '_' . $context;
374 60
                if (!array_key_exists($keywordKey, self::$replacedReservedPhpKeywords)) {
375
                    self::$replacedReservedPhpKeywords[$keywordKey] = 0;
376 456
                } else {
377
                    self::$replacedReservedPhpKeywords[$keywordKey]++;
378
                }
379
                return '_' . $keyword . (self::$replacedReservedPhpKeywords[$keywordKey] ? '_' . self::$replacedReservedPhpKeywords[$keywordKey] : '');
380
            } else {
381
                return '_' . $keyword;
382
            }
383
        } else {
384
            return $keyword;
385
        }
386 464
    }
387
    /**
388 464
     * Static method wich returns a unique name case sensitively
389 464
     * Useful to name methods case sensitively distinct, see http://the-echoplex.net/log/php-case-sensitivity
390 464
     * @param string $name the original name
391 460
     * @param string $context the context where the name is needed unique
392 392
     * @return string
393 388
     */
394 291
    protected static function uniqueName($name, $context)
395 32
    {
396
        $insensitiveKey = strtolower($name . '_' . $context);
397 392
        $sensitiveKey = $name . '_' . $context;
398 392
        if (array_key_exists($sensitiveKey, self::$uniqueNames)) {
399 392
            return self::$uniqueNames[$sensitiveKey];
400
        } elseif (!array_key_exists($insensitiveKey, self::$uniqueNames)) {
401
            self::$uniqueNames[$insensitiveKey] = 0;
402
        } else {
403
            self::$uniqueNames[$insensitiveKey]++;
404
        }
405 252
        $uniqueName = $name . (self::$uniqueNames[$insensitiveKey] ? '_' . self::$uniqueNames[$insensitiveKey] : '');
406
        self::$uniqueNames[$sensitiveKey] = $uniqueName;
407 252
        return $uniqueName;
408 252
    }
409
    /**
410
     * Gives the availability for test purpose and multiple package generation to purge unique names
411
     * @todo see if it can be removed by reviewing how unique names are generated
412
     */
413 228
    public static function purgeUniqueNames()
414
    {
415 228
        self::$uniqueNames = array();
416 228
    }
417
    /**
418
     * Gives the availability for test purpose and multiple package generation to purge reserved keywords usage
419
     * @todo see if it can be removed by reviewing how reserved keywords are generated
420
     */
421
    public static function purgeReservedKeywords()
422
    {
423
        self::$replacedReservedPhpKeywords = array();
424
    }
425
}
426