Completed
Push — develop ( ddfffb...aebfcb )
by Mikaël
27:09
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 146
cts 146
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
A purgeUniqueNames() 0 4 1
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 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 684
    public function __construct(Generator $generator, $name)
65
    {
66 684
        parent::__construct($generator);
67 684
        $this->setName($name);
68 684
    }
69
    /**
70
     * @return string
71
     */
72 184
    public function getExtendsClassName()
73
    {
74 184
        $extends = '';
75 184
        if (($model = $this->getInheritedMoel()) instanceof Struct && $model->getIsStruct()) {
76 12
            $extends = $model->getPackagedName();
77 9
        }
78 184
        if (empty($extends)) {
79 180
            $extends = $this->getExtends(true);
80 135
        }
81 184
        return $extends;
82
    }
83
    /**
84
     * Returns the name of the class the current class inherits from
85
     * @return string
86
     */
87 236
    public function getInheritance()
88
    {
89 236
        return $this->inheritance;
90
    }
91
    /**
92
     * Sets the name of the class the current class inherits from
93
     * @param AbstractModel
94
     */
95 148
    public function setInheritance($inheritance = '')
96
    {
97 148
        $this->inheritance = $inheritance;
98 148
        return $this;
99
    }
100
    /**
101
     * @return Struct
102
     */
103 184
    public function getInheritedMoel()
104
    {
105 184
        return $this->getGenerator()->getStruct($this->getInheritance());
106
    }
107
    /**
108
     * Returns the meta
109
     * @return string[]
110
     */
111 256
    public function getMeta()
112
    {
113 256
        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 332
    public function addMeta($metaName, $metaValue)
134
    {
135 332
        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 324
        $metaValue = is_scalar($metaValue) ? trim($metaValue) : $metaValue;
139 324
        if ((is_scalar($metaValue) && $metaValue !== '') || is_array($metaValue)) {
140 324
            if (!array_key_exists($metaName, $this->meta)) {
141 320
                $this->meta[$metaName] = $metaValue;
142 262
            } elseif (is_array($this->meta[$metaName]) && is_array($metaValue)) {
143 56
                $this->meta[$metaName] = array_merge($this->meta[$metaName], $metaValue);
144 65
            } elseif (is_array($this->meta[$metaName])) {
145 4
                array_push($this->meta[$metaName], $metaValue);
146 3
            } else {
147 28
                $this->meta[$metaName] = $metaValue;
148
            }
149 324
            ksort($this->meta);
150 243
        }
151 324
        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 96
    public function setDocumentation($documentation)
162
    {
163 96
        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 208
    public function getMetaValue($metaName, $fallback = null)
173
    {
174 208
        $meta = $this->getMeta();
175 208
        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 588
    public function getName()
198
    {
199 588
        return $this->name;
200
    }
201
    /**
202
     * Sets the original name extracted from the WSDL
203
     * @param string $name
204
     * @return AbstractModel
205
     */
206 684
    public function setName($name)
207
    {
208 684
        $this->name = $name;
209 684
        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 480
    public function getCleanName($keepMultipleUnderscores = true)
219
    {
220 480
        return self::cleanString($this->getName(), $keepMultipleUnderscores);
221
    }
222
    /**
223
     * Returns the owner model object
224
     * @return AbstractModel
225
     */
226 476
    public function getOwner()
227
    {
228 476
        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 520
    public function setOwner(AbstractModel $owner)
236
    {
237 520
        $this->owner = $owner;
238 520
        return $this;
239
    }
240
    /**
241
     * @return bool
242
     */
243 188
    public function getIsAbstract()
244
    {
245 188
        return $this->isAbstract;
246
    }
247
    /**
248
     * @param bool $isAbstract
249
     * @return AbstractModel
250
     */
251 16
    public function setIsAbstract($isAbstract)
252
    {
253 16
        $this->isAbstract = $isAbstract;
254 16
        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 156
    public function nameIsClean()
263
    {
264 156
        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 468
    public function getPackagedName($namespaced = false)
275
    {
276 468
        $nameParts = array();
277 468
        if ($namespaced && $this->getNamespace() !== '') {
278 172
            $nameParts[] = sprintf('\%s\\', $this->getNamespace());
279 129
        }
280 468
        $cleanName = $this->getCleanName();
281 468
        if ($this->getGenerator()->getOptionPrefix() !== '') {
282 244
            $nameParts[] = $this->getGenerator()->getOptionPrefix();
283 183
        } else {
284 248
            $cleanName = self::replaceReservedPhpKeyword($cleanName);
285
        }
286
287 468
        $nameParts[] = ucfirst(self::uniqueName($cleanName, $this->getContextualPart()));
288 468
        if ($this->getGenerator()->getOptionSuffix() !== '') {
289 24
            $nameParts[] = $this->getGenerator()->getOptionSuffix();
290 18
        }
291 468
        return implode('', $nameParts);
292
    }
293
    /**
294
     * Allows to define the contextual part of the class name for the package
295
     * @return string
296
     */
297 64
    public function getContextualPart()
298
    {
299 64
        return '';
300
    }
301
    /**
302
     * Allows to define from which class the curent model extends
303
     * @param bool $short
304
     * @return string|null
305
     */
306 32
    public function getExtends($short = false)
307
    {
308 32
        return '';
309
    }
310
    /**
311
     * @return string
312
     */
313 212
    public function getNamespace()
314
    {
315 212
        $namespaces = array();
316 212
        $namespace = $this->getGenerator()->getOptionNamespacePrefix();
317 212
        if (empty($namespace)) {
318 204
            if ($this->getGenerator()->getOptionPrefix() !== '') {
319 164
                $namespaces[] = $this->getGenerator()->getOptionPrefix();
320 163
            } elseif ($this->getGenerator()->getOptionSuffix() !== '') {
321 57
                $namespaces[] = $this->getGenerator()->getOptionSuffix();
322 6
            }
323 153
        } else {
324 8
            $namespaces[] = $namespace;
325
        }
326 212
        if ($this->getSubDirectory() !== '') {
327 208
            $namespaces[] = $this->getSubDirectory();
328 156
        }
329 212
        return implode('\\', $namespaces);
330
    }
331
    /**
332
     * Returns directory where to store class and create it if needed
333
     * @return string
334
     */
335 236
    public function getSubDirectory()
336
    {
337 236
        $subDirectory = '';
338 236
        if ($this->getGenerator()->getOptionCategory() === GeneratorOptions::VALUE_CAT) {
339 236
            $subDirectory = $this->getContextualPart();
340 177
        }
341 236
        return $subDirectory;
342
    }
343
    /**
344
     * Returns the sub package name which the model belongs to
345
     * Must be overridden by sub classes
346
     * @return array
347
     */
348 4
    public function getDocSubPackages()
349
    {
350 4
        return array();
351
    }
352
    /**
353
     * Clean a string to make it valid as PHP variable
354
     * @param string $string the string to clean
355
     * @param bool $keepMultipleUnderscores optional, allows to keep the multiple consecutive underscores
356
     * @return string
357
     */
358 484
    public static function cleanString($string, $keepMultipleUnderscores = true)
359
    {
360 484
        return GeneratorUtils::cleanString($string, $keepMultipleUnderscores);
361
    }
362
    /**
363
     * Returns a usable keyword for a original keyword
364
     * @param string $keyword the keyword
365
     * @param string $context the context
366
     * @return string
367
     */
368 464
    public static function replaceReservedPhpKeyword($keyword, $context = null)
369
    {
370 464
        $phpReservedKeywordFound = '';
371 464
        if (ReservedKeywords::instance()->is($keyword)) {
372 128
            if ($context !== null) {
373 60
                $keywordKey = $phpReservedKeywordFound . '_' . $context;
374 60
                if (!array_key_exists($keywordKey, self::$replacedReservedPhpKeywords)) {
375 36
                    self::$replacedReservedPhpKeywords[$keywordKey] = 0;
376 27
                } else {
377 24
                    self::$replacedReservedPhpKeywords[$keywordKey]++;
378
                }
379 60
                return '_' . $keyword . (self::$replacedReservedPhpKeywords[$keywordKey] ? '_' . self::$replacedReservedPhpKeywords[$keywordKey] : '');
380
            } else {
381 100
                return '_' . $keyword;
382
            }
383
        } else {
384 464
            return $keyword;
385
        }
386
    }
387
    /**
388
     * Static method wich returns a unique name case sensitively
389
     * Useful to name methods case sensitively distinct, see http://the-echoplex.net/log/php-case-sensitivity
390
     * @param string $name the original name
391
     * @param string $context the context where the name is needed unique
392
     * @return string
393
     */
394 472
    protected static function uniqueName($name, $context)
395
    {
396 472
        $insensitiveKey = strtolower($name . '_' . $context);
397 472
        $sensitiveKey = $name . '_' . $context;
398 472
        if (array_key_exists($sensitiveKey, self::$uniqueNames)) {
399 468
            return self::$uniqueNames[$sensitiveKey];
400 400
        } elseif (!array_key_exists($insensitiveKey, self::$uniqueNames)) {
401 396
            self::$uniqueNames[$insensitiveKey] = 0;
402 297
        } else {
403 32
            self::$uniqueNames[$insensitiveKey]++;
404
        }
405 400
        $uniqueName = $name . (self::$uniqueNames[$insensitiveKey] ? '_' . self::$uniqueNames[$insensitiveKey] : '');
406 400
        self::$uniqueNames[$sensitiveKey] = $uniqueName;
407 400
        return $uniqueName;
408
    }
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 260
    public static function purgeUniqueNames()
414
    {
415 260
        self::$uniqueNames = array();
416 260
    }
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 236
    public static function purgeReservedKeywords()
422
    {
423 236
        self::$replacedReservedPhpKeywords = array();
424 236
    }
425
}
426