Completed
Push — master ( a48167...1d59e1 )
by Mikaël
100:27 queued 66:25
created

AbstractModel::addMeta()   C

Complexity

Conditions 15
Paths 49

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 15

Importance

Changes 0
Metric Value
cc 15
eloc 18
nc 49
nop 2
dl 0
loc 24
ccs 20
cts 20
cp 1
crap 15
rs 5.9166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace WsdlToPhp\PackageGenerator\Model;
4
5
use WsdlToPhp\PackageGenerator\ConfigurationReader\GeneratorOptions;
6
use WsdlToPhp\PackageGenerator\ConfigurationReader\PhpReservedKeyword;
7
use WsdlToPhp\PackageGenerator\ConfigurationReader\AbstractReservedWord;
8
use WsdlToPhp\PackageGenerator\Generator\Generator;
9
use WsdlToPhp\PackageGenerator\Generator\Utils as GeneratorUtils;
10
use WsdlToPhp\PackageGenerator\Generator\AbstractGeneratorAware;
11
12
/**
13
 * Class AbstractModel defines the basic properties and methods to operations and structs extracted from the WSDL
14
 */
15
abstract class AbstractModel extends AbstractGeneratorAware implements \JsonSerializable
16
{
17
    /**
18
     * Constant used to define the key to store documentation value in meta
19
     * @var string
20
     */
21
    const META_DOCUMENTATION = 'documentation';
22
    /**
23
     * Original name od the element
24
     * @var string
25
     */
26
    protected $name = '';
27
    /**
28
     * Values associated to the operation
29
     * @var string[]
30
     */
31
    protected $meta = [];
32
    /**
33
     * Define the inheritance of a struct by the name of the parent struct or type
34
     * @var string
35
     */
36
    protected $inheritance = '';
37
    /**
38
     * Store the object which owns the current model
39
     * @var AbstractModel
40
     */
41
    protected $owner = null;
42
    /**
43
     * Indicates that the current element is an abstract element.
44
     * It allows to generated an abstract class.
45
     * This will happen for element/complexType that are defined with abstract="true"
46
     * @var bool
47
     */
48
    protected $isAbstract = false;
49
    /**
50
     * Replaced keywords time in order to generate unique new keyword
51
     * @var array
52
     */
53
    protected static $replacedPhpReservedKeywords = [];
54
    /**
55
     * Replaced methods time in order to generate unique new method
56
     * @var array
57
     */
58
    protected $replacedReservedMethods = [];
59
    /**
60
     * Unique name generated in order to ensure unique naming (for struct constructor and setters/getters even for different case attribute name with same value)
61
     * @var array
62
     */
63
    protected static $uniqueNames = [];
64
    /**
65
     * Main constructor
66
     * @uses AbstractModel::setName()
67
     * @param Generator $generator
68
     * @param string $name the original name
69
     */
70 1600
    public function __construct(Generator $generator, $name)
71
    {
72 1600
        parent::__construct($generator);
73 1600
        $this->setName($name);
74 1600
    }
75
    /**
76
     * @uses AbstractModel::getInheritedModel()
77
     * @uses AbstractModel::getPackagedName()
78
     * @uses AbstractModel::getExtends()
79
     * @uses Struct::isStruct()
80
     * @return string
81
     */
82 480
    public function getExtendsClassName()
83
    {
84 480
        $extends = '';
85 480
        if (($model = $this->getInheritedModel()) instanceof Struct && $model->isStruct()) {
86 36
            $extends = $model->getPackagedName($model->isRestriction());
87 18
        }
88 480
        if (empty($extends)) {
89 456
            $extends = $this->getExtends(true);
90 228
        }
91 480
        return $extends;
92
    }
93
    /**
94
     * Returns the name of the class the current class inherits from
95
     * @return string
96
     */
97 1126
    public function getInheritance()
98
    {
99 1126
        return $this->inheritance;
100
    }
101
    /**
102
     * Sets the name of the class the current class inherits from
103
     * @param string $inheritance
104
     * @return AbstractModel
105
     */
106 888
    public function setInheritance($inheritance = '')
107
    {
108 888
        $this->inheritance = $inheritance;
109 888
        return $this;
110
    }
111
    /**
112
     * @uses AbstractGeneratorAware::getGenerator()
113
     * @uses Generator::getStructByName()
114
     * @uses AbstractModel::getInheritance()
115
     * @return Struct
116
     */
117 480
    public function getInheritedModel()
118
    {
119 480
        return $this->getGenerator()->getStructByName($this->getInheritance());
120
    }
121
    /**
122
     * Returns the meta
123
     * @return string[]
124
     */
125 678
    public function getMeta()
126
    {
127 678
        return $this->meta;
128
    }
129
    /**
130
     * Sets the meta
131
     * @param string[] $meta
132
     * @return AbstractModel
133
     */
134 558
    public function setMeta(array $meta = [])
135
    {
136 558
        $this->meta = $meta;
137 558
        return $this;
138
    }
139
    /**
140
     * Add meta information to the operation
141
     * @uses AbstractModel::getMeta()
142
     * @throws \InvalidArgumentException
143
     * @param string $metaName
144
     * @param mixed $metaValue
145
     * @return AbstractModel
146
     */
147 228
    public function addMeta($metaName, $metaValue)
148
    {
149 228
        if (!is_scalar($metaName) || (!is_scalar($metaValue) && !is_array($metaValue))) {
0 ignored issues
show
introduced by
The condition is_scalar($metaName) is always true.
Loading history...
150 12
            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__);
151
        }
152 216
        $metaValue = is_scalar($metaValue) ? ((is_numeric($metaValue) || is_bool($metaValue) ? $metaValue : trim($metaValue))) : $metaValue;
153 216
        if (is_scalar($metaValue) || is_array($metaValue)) {
154 216
            if (!array_key_exists($metaName, $this->meta)) {
155 216
                $this->meta[$metaName] = $metaValue;
156 138
            } elseif (is_array($this->meta[$metaName]) && is_array($metaValue)) {
0 ignored issues
show
introduced by
The condition is_array($this->meta[$metaName]) is always false.
Loading history...
157 30
                $this->meta[$metaName] = array_merge($this->meta[$metaName], $metaValue);
158 45
            } elseif (is_array($this->meta[$metaName])) {
0 ignored issues
show
introduced by
The condition is_array($this->meta[$metaName]) is always false.
Loading history...
159 18
                array_push($this->meta[$metaName], $metaValue);
160 27
            } elseif (array_key_exists($metaName, $this->meta) && $metaValue !== $this->meta[$metaName]) {
161 12
                $this->meta[$metaName] = [
162 15
                    $this->meta[$metaName],
163 12
                    $metaValue,
164
                ];
165 6
            } else {
166 18
                $this->meta[$metaName] = $metaValue;
167
            }
168 216
            ksort($this->meta);
169 108
        }
170 216
        return $this;
171
    }
172
173
    /**
174
     * Allows to merge meta from different sources and ensure consistency of their order
175
     * Must be passed as less important (at first position) to most important (last position)
176
     * @param array $meta
177
     * @param array $meta
178
     * @param array $meta
179
     * @param array ...
180
     * @return array
181
     */
182 540
    protected function mergeMeta()
183
    {
184 540
        $meta = func_get_args();
185 540
        $mergedMeta = [];
186 540
        $metaDocumentation = [];
187
        // gather meta
188 540
        foreach ($meta as $metaItem) {
189 540
            foreach ($metaItem as $metaName => $metaValue) {
190 432
                if (self::META_DOCUMENTATION === $metaName) {
191 120
                    $metaDocumentation = array_merge($metaDocumentation, $metaValue);
192 420
                } elseif (!array_key_exists($metaName, $mergedMeta)) {
193 408
                    $mergedMeta[$metaName] = $metaValue;
194 219
                } elseif (is_array($mergedMeta[$metaName]) && is_array($metaValue)) {
195
                    $mergedMeta[$metaName] = array_merge($mergedMeta[$metaName], $metaValue);
196 30
                } elseif (is_array($mergedMeta[$metaName])) {
197
                    $mergedMeta[$metaName][] = $metaValue;
198
                } else {
199 200
                    $mergedMeta[$metaName] = $metaValue;
200
                }
201 270
            }
202 270
        }
203
204
        // sort by key
205 540
        ksort($mergedMeta);
206
207
        // add documentation if any at first position
208 540
        if (!empty($metaDocumentation)) {
209
            $definitiveMeta = [
210 120
                self::META_DOCUMENTATION => array_unique(array_reverse($metaDocumentation)),
211 60
            ];
212 120
            foreach ($mergedMeta as $metaName => $metaValue) {
213 96
                $definitiveMeta[$metaName] = $metaValue;
214 60
            }
215 120
            $mergedMeta = $definitiveMeta;
216 120
            unset($definitiveMeta);
217 60
        }
218 540
        unset($meta, $metaDocumentation);
219 540
        return $mergedMeta;
220
    }
221
    /**
222
     * Sets the documentation meta value.
223
     * Documentation is set as an array so if multiple documentation nodes are set for an unique element, it will gather them.
224
     * @uses AbstractModel::META_DOCUMENTATION
225
     * @uses AbstractModel::addMeta()
226
     * @param string $documentation the documentation from the WSDL
227
     * @return AbstractModel
228
     */
229 36
    public function setDocumentation($documentation)
230
    {
231 36
        return $this->addMeta(self::META_DOCUMENTATION, is_array($documentation) ? $documentation : [
0 ignored issues
show
introduced by
The condition is_array($documentation) is always false.
Loading history...
232 36
            $documentation,
233 18
        ]);
234
    }
235
    /**
236
     * Returns a meta value according to its name
237
     * @uses AbstractModel::getMeta()
238
     * @param string $metaName the meta information name
239
     * @param mixed $fallback the fallback value if unset
240
     * @return mixed the meta information value
241
     */
242 528
    public function getMetaValue($metaName, $fallback = null)
243
    {
244 528
        $meta = $this->getMeta();
245 528
        return array_key_exists($metaName, $meta) ? $meta[$metaName] : $fallback;
246
    }
247
    /**
248
     * Returns the value of the first meta value assigned to the name
249
     * @param string[] $names the meta names to check
250
     * @param mixed $fallback the fallback value if anyone is set
251
     * @return mixed the meta information value
252
     */
253 348
    public function getMetaValueFirstSet(array $names, $fallback = null)
254
    {
255 348
        $meta = $this->getMeta();
256 348
        foreach ($names as $name) {
257 348
            if (array_key_exists($name, $meta)) {
258 288
                return $meta[$name];
259
            }
260 153
        }
261 288
        return $fallback;
262
    }
263
    /**
264
     * Returns the original name extracted from the WSDL
265
     * @return string
266
     */
267 1540
    public function getName()
268
    {
269 1540
        return $this->name;
270
    }
271
    /**
272
     * Sets the original name extracted from the WSDL
273
     * @param string $name
274
     * @return AbstractModel
275
     */
276 1600
    public function setName($name)
277
    {
278 1600
        $this->name = $name;
279 1600
        return $this;
280
    }
281
    /**
282
     * Returns a valid clean name for PHP
283
     * @uses AbstractModel::getName()
284
     * @uses AbstractModel::cleanString()
285
     * @param bool $keepMultipleUnderscores optional, allows to keep the multiple consecutive underscores
286
     * @return string
287
     */
288 1170
    public function getCleanName($keepMultipleUnderscores = true)
289
    {
290 1170
        return self::cleanString($this->getName(), $keepMultipleUnderscores);
291
    }
292
    /**
293
     * Returns the owner model object
294
     * @return AbstractModel
295
     */
296 1224
    public function getOwner()
297
    {
298 1224
        return $this->owner;
299
    }
300
    /**
301
     * Sets the owner model object
302
     * @param AbstractModel $owner object the owner of the current model
303
     * @return AbstractModel
304
     */
305 1254
    public function setOwner(AbstractModel $owner = null)
306
    {
307 1254
        $this->owner = $owner;
308 1254
        return $this;
309
    }
310
    /**
311
     * @return bool
312
     */
313 486
    public function isAbstract()
314
    {
315 486
        return $this->isAbstract;
316
    }
317
    /**
318
     * @param bool $isAbstract
319
     * @return AbstractModel
320
     */
321 552
    public function setAbstract($isAbstract)
322
    {
323 552
        $this->isAbstract = $isAbstract;
324 552
        return $this;
325
    }
326
    /**
327
     * Returns true if the original name is safe to use as a PHP property, variable name or class name
328
     * @uses AbstractModel::getName()
329
     * @uses AbstractModel::getCleanName()
330
     * @return bool
331
     */
332 312
    public function nameIsClean()
333
    {
334 312
        return ($this->getName() !== '' && $this->getName() === $this->getCleanName());
335
    }
336
    /**
337
     * Returns the packaged name
338
     * @uses AbstractModel::getNamespace()
339
     * @uses AbstractModel::getCleanName()
340
     * @uses AbstractModel::getContextualPart()
341
     * @uses AbstractModel::uniqueName()
342
     * @uses AbstractModel::replacePhpReservedKeyword()
343
     * @uses AbstractGeneratorAware::getGenerator()
344
     * @uses Generator::getOptionPrefix()
345
     * @uses Generator::getOptionSuffix()
346
     * @uses AbstractModel::uniqueName() to ensure unique naming of struct case sensitively
347
     * @param bool $namespaced
348
     * @return string
349
     */
350 1146
    public function getPackagedName($namespaced = false)
351
    {
352 1146
        $nameParts = [];
353 1146
        if ($namespaced && $this->getNamespace() !== '') {
354 450
            $nameParts[] = sprintf('\%s\\', $this->getNamespace());
355 225
        }
356 1146
        $cleanName = $this->getCleanName();
357 1146
        if ($this->getGenerator()->getOptionPrefix() !== '') {
358 786
            $nameParts[] = $this->getGenerator()->getOptionPrefix();
359 393
        } else {
360 414
            $cleanName = self::replacePhpReservedKeyword($cleanName);
361
        }
362 1146
        $nameParts[] = ucfirst(self::uniqueName($cleanName, $this->getContextualPart()));
363 1146
        if ($this->getGenerator()->getOptionSuffix() !== '') {
364 36
            $nameParts[] = $this->getGenerator()->getOptionSuffix();
365 18
        }
366 1146
        return implode('', $nameParts);
367
    }
368
    /**
369
     * Allows to define the contextual part of the class name for the package
370
     * @return string
371
     */
372 138
    public function getContextualPart()
373
    {
374 138
        return '';
375
    }
376
    /**
377
     * Allows to define from which class the current model extends
378
     * @param bool $short
379
     * @return string|null
380
     */
381 66
    public function getExtends($short = false)
382
    {
383 66
        return '';
384
    }
385
    /**
386
     * @uses AbstractGeneratorAware::getGenerator()
387
     * @uses Generator::getOptionNamespacePrefix()
388
     * @uses Generator::getOptionPrefix()
389
     * @uses Generator::getOptionSuffix()
390
     * @uses AbstractModel::getSubDirectory()
391
     * @return string
392
     */
393 624
    public function getNamespace()
394
    {
395 624
        $namespaces = [];
396 624
        $namespace = $this->getGenerator()->getOptionNamespacePrefix();
397 624
        if (empty($namespace)) {
398 612
            if ($this->getGenerator()->getOptionPrefix() !== '') {
399 528
                $namespaces[] = $this->getGenerator()->getOptionPrefix();
400 348
            } elseif ($this->getGenerator()->getOptionSuffix() !== '') {
401 312
                $namespaces[] = $this->getGenerator()->getOptionSuffix();
402 6
            }
403 306
        } else {
404 12
            $namespaces[] = $namespace;
405
        }
406 624
        if ($this->getSubDirectory() !== '') {
407 594
            $namespaces[] = $this->getSubDirectory();
408 297
        }
409 624
        return implode('\\', $namespaces);
410
    }
411
    /**
412
     * Returns directory where to store class and create it if needed
413
     * @uses AbstractGeneratorAware::getGenerator()
414
     * @uses AbstractModel::getOptionCategory()
415
     * @uses AbstractGeneratorAware::getContextualPart()
416
     * @uses GeneratorOptions::VALUE_CAT
417
     * @return string
418
     */
419 660
    public function getSubDirectory()
420
    {
421 660
        $subDirectory = '';
422 660
        if ($this->getGenerator()->getOptionCategory() === GeneratorOptions::VALUE_CAT) {
423 654
            $subDirectory = $this->getContextualPart();
424 327
        }
425 660
        return $subDirectory;
426
    }
427
    /**
428
     * Returns the sub package name which the model belongs to
429
     * Must be overridden by sub classes
430
     * @return array
431
     */
432 6
    public function getDocSubPackages()
433
    {
434 6
        return [];
435
    }
436
    /**
437
     * Clean a string to make it valid as PHP variable
438
     * @uses GeneratorUtils::cleanString()
439
     * @param string $string the string to clean
440
     * @param bool $keepMultipleUnderscores optional, allows to keep the multiple consecutive underscores
441
     * @return string
442
     */
443 1176
    public static function cleanString($string, $keepMultipleUnderscores = true)
444
    {
445 1176
        return GeneratorUtils::cleanString($string, $keepMultipleUnderscores);
446
    }
447
    /**
448
     * Returns a usable keyword for a original keyword
449
     * @uses PhpReservedKeyword::instance()
450
     * @uses PhpReservedKeyword::is()
451
     * @param string $keyword the keyword
452
     * @param string $context the context
453
     * @return string
454
     */
455 996
    public static function replacePhpReservedKeyword($keyword, $context = null)
456
    {
457 996
        if (PhpReservedKeyword::instance()->is($keyword)) {
458 222
            if ($context !== null) {
459 114
                $keywordKey = $keyword . '_' . $context;
460 114
                if (!array_key_exists($keywordKey, self::$replacedPhpReservedKeywords)) {
461 72
                    self::$replacedPhpReservedKeywords[$keywordKey] = 0;
462 36
                } else {
463 42
                    self::$replacedPhpReservedKeywords[$keywordKey]++;
464
                }
465 114
                return '_' . $keyword . (self::$replacedPhpReservedKeywords[$keywordKey] ? '_' . self::$replacedPhpReservedKeywords[$keywordKey] : '');
466
            } else {
467 174
                return '_' . $keyword;
468
            }
469
        } else {
470 996
            return $keyword;
471
        }
472
    }
473
    /**
474
     * @throws \InvalidArgumentException
475
     * @param $filename
476
     * @return AbstractReservedWord
477
     */
478 6
    public function getReservedMethodsInstance($filename = null)
479
    {
480 6
        throw new \InvalidArgumentException(sprintf('The method %s should be defined in the class %s', __FUNCTION__, get_called_class(), __LINE__));
481
    }
482
    /**
483
     * Returns a usable method for a original method
484
     * @uses PhpReservedKeywords::instance()
485
     * @uses PhpReservedKeywords::is()
486
     * @param string $methodName the method name
487
     * @param string $context the context
488
     * @return string
489
     */
490 1062
    public function replaceReservedMethod($methodName, $context = null)
491
    {
492 1062
        if ($this->getReservedMethodsInstance()->is($methodName)) {
493 18
            if ($context !== null) {
494 18
                $methodKey = $methodName . '_' . $context;
495 18
                if (!array_key_exists($methodKey, $this->replacedReservedMethods)) {
496 18
                    $this->replacedReservedMethods[$methodKey] = 0;
497 9
                } else {
498
                    $this->replacedReservedMethods[$methodKey]++;
499
                }
500 18
                return '_' . $methodName . ($this->replacedReservedMethods[$methodKey] ? '_' . $this->replacedReservedMethods[$methodKey] : '');
501
            } else {
502 6
                return '_' . $methodName;
503
            }
504
        } else {
505 1050
            return $methodName;
506
        }
507
    }
508
    /**
509
     * Static method which returns a unique name case sensitively
510
     * Useful to name methods case sensitively distinct, see http://the-echoplex.net/log/php-case-sensitivity
511
     * @param string $name the original name
512
     * @param string $context the context where the name is needed unique
513
     * @return string
514
     */
515 1158
    protected static function uniqueName($name, $context)
516
    {
517 1158
        $insensitiveKey = mb_strtolower($name . '_' . $context);
518 1158
        $sensitiveKey = $name . '_' . $context;
519 1158
        if (array_key_exists($sensitiveKey, self::$uniqueNames)) {
520 1068
            return self::$uniqueNames[$sensitiveKey];
521 990
        } elseif (!array_key_exists($insensitiveKey, self::$uniqueNames)) {
522 984
            self::$uniqueNames[$insensitiveKey] = 0;
523 492
        } else {
524 56
            self::$uniqueNames[$insensitiveKey]++;
525
        }
526 990
        $uniqueName = $name . (self::$uniqueNames[$insensitiveKey] ? '_' . self::$uniqueNames[$insensitiveKey] : '');
527 990
        self::$uniqueNames[$sensitiveKey] = $uniqueName;
528 990
        return $uniqueName;
529
    }
530
    /**
531
     * Gives the availability for test purpose and multiple package generation to purge unique names
532
     */
533 1122
    public static function purgeUniqueNames()
534
    {
535 1122
        self::$uniqueNames = [];
536 1122
    }
537
    /**
538
     * Gives the availability for test purpose and multiple package generation to purge reserved keywords usage
539
     */
540 1086
    public static function purgePhpReservedKeywords()
541
    {
542 1086
        self::$replacedPhpReservedKeywords = [];
543 1086
    }
544
    /**
545
     * Should return the properties of the inherited class
546
     * @return array
547
     */
548
    abstract protected function toJsonSerialize();
549
    /**
550
     * {@inheritDoc}
551
     * @see JsonSerializable::jsonSerialize()
552
     */
553 18
    public function jsonSerialize()
554
    {
555 18
        return array_merge($this->toJsonSerialize(), [
556 18
            'inheritance' => $this->inheritance,
557 18
            'abstract' => $this->isAbstract,
558 18
            'meta' => $this->meta,
559 18
            'name' => $this->name,
560 18
            '__CLASS__' => get_called_class(),
561 9
        ]);
562
    }
563
    /**
564
     * @param Generator $generator
565
     * @param array $args
566
     * @return AbstractModel
567
     */
568 546
    public static function instanceFromSerializedJson(Generator $generator, array $args)
569
    {
570 546
        self::checkSerializedJson($args);
571 546
        $class = $args['__CLASS__'];
572 546
        $instance = new $class($generator, $args['name']);
573 546
        unset($args['name'], $args['__CLASS__']);
574 546
        foreach ($args as $arg => $value) {
575 546
            $setFromSerializedJson = sprintf('set%sFromSerializedJson', ucfirst($arg));
576 546
            $set = sprintf('set%s', ucfirst($arg));
577 546
            if (method_exists($instance, $setFromSerializedJson)) {
578 546
                $instance->$setFromSerializedJson($value);
579 546
            } elseif (method_exists($instance, $set)) {
580 546
                $instance->$set($value);
581 273
            }
582 273
        }
583 546
        return $instance;
584
    }
585
    /**
586
     * @param array $args
587
     * @throws \InvalidArgumentException
588
     */
589 546
    protected static function checkSerializedJson(array $args)
590
    {
591 546
        if (!array_key_exists('__CLASS__', $args)) {
592 6
            throw new \InvalidArgumentException(sprintf('__CLASS__ key is missing from "%s"', var_export($args, true)));
593
        }
594 546
        if (!class_exists($args['__CLASS__'])) {
595 6
            throw new \InvalidArgumentException(sprintf('Class %s is unknown', $args['__CLASS__']));
596
        }
597 546
        if (!array_key_exists('name', $args)) {
598 6
            throw new \InvalidArgumentException(sprintf('name key is missing from %s', var_export($args, true)));
599
        }
600 546
    }
601
}
602