Completed
Push — develop ( af2f24...b1074b )
by Mikaël
85:27 queued 45:11
created

AbstractModel::setDocumentation()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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