Completed
Push — refactor-to-hexagonal-and-tigh... ( a5838a...fd9b77 )
by Mike
09:10
created

StructureXmlRenderer   F

Complexity

Total Complexity 58

Size/Duplication

Total Lines 501
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 22

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 58
c 1
b 0
f 0
lcom 1
cbo 22
dl 0
loc 501
rs 1.7098

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 1
A render() 0 19 2
F buildFile() 0 81 13
A createErrorEntry() 0 11 1
B buildFunction() 0 22 4
F buildClass() 0 85 20
B finalize() 0 31 1
A buildPackageTree() 0 16 3
A buildNamespaceTree() 0 16 3
A buildDeprecationList() 0 8 1
A getNodeListForTagBasedQuery() 0 17 1
B generateNamespaceTree() 0 24 5
A generateNamespaceElements() 0 13 3

How to fix   Complexity   

Complex Class

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

1
<?php
2
/**
3
 * phpDocumentor
4
 *
5
 * PHP Version 5.4
6
 *
7
 * @copyright 2010-2014 Mike van Riel / Naenius (http://www.naenius.com)
8
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
9
 * @link      http://phpdoc.org
10
 */
11
12
namespace phpDocumentor\Application\Renderer;
13
14
use DomainModel\Renderer;
15
use phpDocumentor\DomainModel\Path;
16
use phpDocumentor\Application\Renderer\Template\Action;
17
use phpDocumentor\Application\Renderer\Template\Action\Xml;
18
use phpDocumentor\Application\Renderer\StructureXmlRenderer\ArgumentConverter;
19
use phpDocumentor\Application\Renderer\StructureXmlRenderer\ConstantConverter;
20
use phpDocumentor\Application\Renderer\StructureXmlRenderer\DocBlockConverter;
21
use phpDocumentor\Application\Renderer\StructureXmlRenderer\InterfaceConverter;
22
use phpDocumentor\Application\Renderer\StructureXmlRenderer\MethodConverter;
23
use phpDocumentor\Application\Renderer\StructureXmlRenderer\PropertyConverter;
24
use phpDocumentor\Application\Renderer\StructureXmlRenderer\TagConverter;
25
use phpDocumentor\Application\Renderer\StructureXmlRenderer\TraitConverter;
26
use phpDocumentor\DomainModel\Renderer\Router\ForFileProxy;
27
use phpDocumentor\DomainModel\Renderer\Router\RouterAbstract;
28
use phpDocumentor\Application\Application;
29
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\AuthorTag;
30
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\CoversTag;
31
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\IgnoreTag;
32
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\InternalTag;
33
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\LicenseTag;
34
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\MethodTag;
35
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\ParamTag;
36
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\PropertyTag;
37
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\ReturnTag;
38
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\UsesTag;
39
use phpDocumentor\Application\Renderer\StructureXmlRenderer\Tag\VarTag;
40
use phpDocumentor\DomainModel\Views\View;
41
42
/**
43
 * Converts the structural information of phpDocumentor into an XML file.
44
 */
45
final class StructureXmlRenderer implements Renderer
0 ignored issues
show
Complexity introduced by
This class has a complexity of 58 which exceeds the configured maximum of 50.

The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.

Some resources for further reading:

You can also find more detailed suggestions for refactoring in the “Code” section of your repository.

Loading history...
Complexity introduced by
The class StructureXmlRenderer has a coupling between objects value of 36. Consider to reduce the number of dependencies under 13.
Loading history...
46
{
47
    /** @var \DOMDocument $xml */
48
    protected $xml;
49
50
    protected $docBlockConverter;
51
52
    protected $argumentConverter;
53
54
    protected $methodConverter;
55
56
    protected $propertyConverter;
57
58
    protected $constantConverter;
59
60
    protected $interfaceConverter;
61
62
    protected $traitConverter;
63
64
    /** @var RouterAbstract */
65
    private $router;
66
67
    public function __construct(RouterAbstract $router)
68
    {
69
        $this->docBlockConverter  = new DocBlockConverter(new TagConverter(), $router);
70
        $this->argumentConverter  = new ArgumentConverter();
71
        $this->methodConverter    = new MethodConverter($this->argumentConverter, $this->docBlockConverter);
72
        $this->propertyConverter  = new PropertyConverter($this->docBlockConverter);
73
        $this->constantConverter  = new ConstantConverter($this->docBlockConverter);
74
        $this->interfaceConverter = new InterfaceConverter(
75
            $this->docBlockConverter,
76
            $this->methodConverter,
77
            $this->constantConverter
78
        );
79
        $this->traitConverter = new TraitConverter(
80
            $this->docBlockConverter,
81
            $this->methodConverter,
82
            $this->propertyConverter
83
        );
84
        $this->router = $router;
85
    }
86
87
    public function render(View $view, Path $destination, $template = null)
88
    {
89
        $artifact = (string)$destination;
90
91
        $this->xml = new \DOMDocument('1.0', 'utf-8');
92
        $this->xml->formatOutput = true;
93
        $document_element = new \DOMElement('project');
94
        $this->xml->appendChild($document_element);
95
96
        $document_element->setAttribute('title', $view()->getName());
97
        $document_element->setAttribute('version', Application::$VERSION);
98
99
        foreach ($view()->getFiles() as $file) {
100
            $this->buildFile($document_element, $file);
101
        }
102
103
        $this->finalize($view());
104
        file_put_contents($artifact, $this->xml->saveXML());
105
    }
106
107
    protected function buildFile(\DOMElement $parent, FileDescriptor $file)
0 ignored issues
show
Complexity introduced by
This operation has 1536 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
108
    {
109
        $child = new \DOMElement('file');
110
        $parent->appendChild($child);
111
112
        $router = new ForFileProxy($this->router->match($file));
0 ignored issues
show
Bug introduced by
It seems like $this->router->match($file) can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
113
        $path = ltrim($file->getPath(), './');
114
        $child->setAttribute('path', $path);
115
        $child->setAttribute('generated-path', $router->generate($file));
116
        $child->setAttribute('hash', $file->getHash());
117
118
        $this->docBlockConverter->convert($child, $file);
119
120
        // add namespace aliases
121
        foreach ($file->getNamespaceAliases() as $alias => $namespace) {
122
            $alias_obj = new \DOMElement('namespace-alias', $namespace);
123
            $child->appendChild($alias_obj);
124
            $alias_obj->setAttribute('name', $alias);
125
        }
126
127
        /** @var ConstantDescriptor $constant */
128
        foreach ($file->getConstants() as $constant) {
129
            $this->constantConverter->convert($child, $constant);
130
        }
131
132
        /** @var FunctionDescriptor $function */
133
        foreach ($file->getFunctions() as $function) {
134
            $this->buildFunction($child, $function);
135
        }
136
137
        /** @var InterfaceDescriptor $interface */
138
        foreach ($file->getInterfaces() as $interface) {
139
            $this->interfaceConverter->convert($child, $interface);
140
        }
141
142
        /** @var ClassDescriptor $class */
143
        foreach ($file->getClasses() as $class) {
144
            $this->buildClass($child, $class);
145
        }
146
147
        /** @var TraitDescriptor $class */
148
        foreach ($file->getTraits() as $trait) {
149
            $this->traitConverter->convert($child, $trait);
150
        }
151
152
        // add markers
153
        if (count($file->getMarkers()) > 0) {
154
            $markers = new \DOMElement('markers');
155
            $child->appendChild($markers);
156
157
            foreach ($file->getMarkers() as $marker) {
158
                if (! $marker['type']) {
159
                    continue;
160
                }
161
162
                $marker_obj = new \DOMElement(strtolower($marker['type']));
163
                $markers->appendChild($marker_obj);
164
165
                $marker_obj->appendChild(new \DOMText(trim($marker['message'])));
166
                $marker_obj->setAttribute('line', $marker['line']);
167
            }
168
        }
169
170
        $errors = $file->getAllErrors();
171
        if (count($errors) > 0) {
172
            $parse_errors = new \DOMElement('parse_markers');
173
            $child->appendChild($parse_errors);
174
175
            /** @var Error $error */
176
            foreach ($errors as $error) {
177
                $this->createErrorEntry($error, $parse_errors);
178
            }
179
        }
180
181
        // if we want to include the source for each file; append a new
182
        // element 'source' which contains a compressed, encoded version
183
        // of the source
184
        if ($file->getSource()) {
185
            $child->appendChild(new \DOMElement('source', base64_encode(gzcompress($file->getSource()))));
186
        }
187
    }
188
189
    /**
190
     * Creates an entry in the ParseErrors collection of a file for a given error.
191
     *
192
     * @param Error       $error
193
     * @param \DOMElement $parse_errors
194
     *
195
     * @return void
196
     */
197
    protected function createErrorEntry($error, $parse_errors)
198
    {
199
        $marker_obj = new \DOMElement(strtolower($error->getSeverity()));
200
        $parse_errors->appendChild($marker_obj);
201
202
        $message = vsprintf($error->getCode(), $error->getContext());
203
204
        $marker_obj->appendChild(new \DOMText($message));
205
        $marker_obj->setAttribute('line', $error->getLine());
206
        $marker_obj->setAttribute('code', $error->getCode());
207
    }
208
209
    /**
210
     * Export this function definition to the given parent DOMElement.
211
     *
212
     * @param \DOMElement        $parent   Element to augment.
213
     * @param FunctionDescriptor $function Element to export.
214
     * @param \DOMElement        $child    if supplied this element will be augmented instead of freshly added.
215
     *
216
     * @return void
217
     */
218
    public function buildFunction(\DOMElement $parent, FunctionDescriptor $function, \DOMElement $child = null)
219
    {
220
        if (!$child) {
221
            $child = new \DOMElement('function');
222
            $parent->appendChild($child);
223
        }
224
225
        $namespace = $function->getNamespace()
226
            ? $function->getNamespace()
227
            : $parent->getAttribute('namespace');
228
        $child->setAttribute('namespace', ltrim($namespace, '\\'));
229
        $child->setAttribute('line', $function->getLine());
230
231
        $child->appendChild(new \DOMElement('name', $function->getName()));
232
        $child->appendChild(new \DOMElement('full_name', $function->getFullyQualifiedStructuralElementName()));
233
234
        $this->docBlockConverter->convert($child, $function);
235
236
        foreach ($function->getArguments() as $argument) {
237
            $this->argumentConverter->convert($child, $argument);
238
        }
239
    }
240
241
    /**
242
     * Exports the given reflection object to the parent XML element.
243
     *
244
     * This method creates a new child element on the given parent XML element
245
     * and takes the properties of the Reflection argument and sets the
246
     * elements and attributes on the child.
247
     *
248
     * If a child DOMElement is provided then the properties and attributes are
249
     * set on this but the child element is not appended onto the parent. This
250
     * is the responsibility of the invoker. Essentially this means that the
251
     * $parent argument is ignored in this case.
252
     *
253
     * @param \DOMElement     $parent The parent element to augment.
254
     * @param ClassDescriptor $class  The data source.
255
     * @param \DOMElement     $child  Optional: child element to use instead of creating a
256
     *      new one on the $parent.
257
     *
258
     * @return void
259
     */
260
    public function buildClass(\DOMElement $parent, ClassDescriptor $class, \DOMElement $child = null)
0 ignored issues
show
Complexity introduced by
This operation has 2187000 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
261
    {
262
        if (!$child) {
263
            $child = new \DOMElement('class');
264
            $parent->appendChild($child);
265
        }
266
267
        $child->setAttribute('final', $class->isFinal() ? 'true' : 'false');
268
        $child->setAttribute('abstract', $class->isAbstract() ? 'true' : 'false');
269
270
        $parentFqcn = !$class->getParent() instanceof DescriptorAbstract
0 ignored issues
show
Bug introduced by
The class phpDocumentor\Applicatio...erer\DescriptorAbstract does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
271
            ? $class->getParent()
272
            : $class->getParent()->getFullyQualifiedStructuralElementName();
273
        $child->appendChild(new \DOMElement('extends', $parentFqcn));
274
275
        /** @var InterfaceDescriptor $interface */
276
        foreach ($class->getInterfaces() as $interface) {
277
            $interfaceFqcn = is_string($interface)
278
                ? $interface
279
                : $interface->getFullyQualifiedStructuralElementName();
280
            $child->appendChild(new \DOMElement('implements', $interfaceFqcn));
281
        }
282
283
        if ($child === null) {
284
            $child = new \DOMElement('interface');
285
            $parent->appendChild($child);
286
        }
287
288
        $namespace = $class->getNamespace()->getFullyQualifiedStructuralElementName();
289
        $child->setAttribute('namespace', ltrim($namespace, '\\'));
290
        $child->setAttribute('line', $class->getLine());
291
292
        $child->appendChild(new \DOMElement('name', $class->getName()));
293
        $child->appendChild(new \DOMElement('full_name', $class->getFullyQualifiedStructuralElementName()));
294
295
        $this->docBlockConverter->convert($child, $class);
296
297
        foreach ($class->getConstants() as $constant) {
298
            // TODO #840: Workaround; for some reason there are NULLs in the constants array.
299
            if ($constant) {
300
                $this->constantConverter->convert($child, $constant);
301
            }
302
        }
303
304
        foreach ($class->getInheritedConstants() as $constant) {
305
            // TODO #840: Workaround; for some reason there are NULLs in the constants array.
306
            if ($constant) {
307
                $this->constantConverter->convert($child, $constant);
308
            }
309
        }
310
311
        foreach ($class->getProperties() as $property) {
312
            // TODO #840: Workaround; for some reason there are NULLs in the properties array.
313
            if ($property) {
314
                $this->propertyConverter->convert($child, $property);
315
            }
316
        }
317
318
        foreach ($class->getInheritedProperties() as $property) {
319
            // TODO #840: Workaround; for some reason there are NULLs in the properties array.
320
            if ($property) {
321
                $this->propertyConverter->convert($child, $property);
322
            }
323
        }
324
325
        foreach ($class->getMethods() as $method) {
326
            // TODO #840: Workaround; for some reason there are NULLs in the methods array.
327
            if ($method) {
328
                $this->methodConverter->convert($child, $method);
329
            }
330
        }
331
332
        foreach ($class->getInheritedMethods() as $method) {
333
            // TODO #840: Workaround; for some reason there are NULLs in the methods array.
334
            if ($method) {
335
                $methodElement = $this->methodConverter->convert($child, $method);
336
                $methodElement->appendChild(
337
                    new \DOMElement(
338
                        'inherited_from',
339
                        $method->getParent()->getFullyQualifiedStructuralElementName()
340
                    )
341
                );
342
            }
343
        }
344
    }
345
346
    /**
347
     * Finalizes the processing and executing all post-processing actions.
348
     *
349
     * This method is responsible for extracting and manipulating the data that
350
     * is global to the project, such as:
351
     *
352
     * - Package tree
353
     * - Namespace tree
354
     * - Marker list
355
     * - Deprecated elements listing
356
     * - Removal of objects related to visibility
357
     *
358
     * @param ProjectInterface $projectDescriptor
359
     *
360
     * @return void
361
     */
362
    protected function finalize(ProjectInterface $projectDescriptor)
363
    {
364
        // TODO: move all these behaviours to a central location for all template parsers
365
        $behaviour = new AuthorTag();
366
        $behaviour->process($this->xml);
367
        $behaviour = new CoversTag();
368
        $behaviour->process($this->xml);
369
        $behaviour = new IgnoreTag();
370
        $behaviour->process($this->xml);
371
        $behaviour = new InternalTag(
372
            $projectDescriptor->isVisibilityAllowed(ProjectDescriptor\Settings::VISIBILITY_INTERNAL)
373
        );
374
        $behaviour->process($this->xml);
375
        $behaviour = new LicenseTag();
376
        $behaviour->process($this->xml);
377
        $behaviour = new MethodTag();
378
        $behaviour->process($this->xml);
379
        $behaviour = new ParamTag();
380
        $behaviour->process($this->xml);
381
        $behaviour = new PropertyTag();
382
        $behaviour->process($this->xml);
383
        $behaviour = new ReturnTag();
384
        $behaviour->process($this->xml);
385
        $behaviour = new UsesTag();
386
        $behaviour->process($this->xml);
387
        $behaviour = new VarTag();
388
        $behaviour->process($this->xml);
389
        $this->buildPackageTree($this->xml);
390
        $this->buildNamespaceTree($this->xml);
391
        $this->buildDeprecationList($this->xml);
392
    }
393
394
    /**
395
     * Collects all packages and subpackages, and adds a new section in the
396
     * DOM to provide an overview.
397
     *
398
     * @param \DOMDocument $dom Packages are extracted and a summary inserted
399
     *     in this object.
400
     *
401
     * @return void
402
     */
403
    protected function buildPackageTree(\DOMDocument $dom)
404
    {
405
        $xpath = new \DOMXPath($dom);
406
        $packages = array('global' => true);
407
        $qry = $xpath->query('//@package');
408
        for ($i = 0; $i < $qry->length; $i++) {
409
            if (isset($packages[$qry->item($i)->nodeValue])) {
410
                continue;
411
            }
412
413
            $packages[$qry->item($i)->nodeValue] = true;
414
        }
415
416
        $packages = $this->generateNamespaceTree(array_keys($packages));
417
        $this->generateNamespaceElements($packages, $dom->documentElement, 'package');
418
    }
419
420
    /**
421
     * Collects all namespaces and sub-namespaces, and adds a new section in
422
     * the DOM to provide an overview.
423
     *
424
     * @param \DOMDocument $dom Namespaces are extracted and a summary inserted
425
     *     in this object.
426
     *
427
     * @return void
428
     */
429
    protected function buildNamespaceTree(\DOMDocument $dom)
430
    {
431
        $xpath = new \DOMXPath($dom);
432
        $namespaces = array();
433
        $qry = $xpath->query('//@namespace');
434
        for ($i = 0; $i < $qry->length; $i++) {
435
            if (isset($namespaces[$qry->item($i)->nodeValue])) {
436
                continue;
437
            }
438
439
            $namespaces[$qry->item($i)->nodeValue] = true;
440
        }
441
442
        $namespaces = $this->generateNamespaceTree(array_keys($namespaces));
443
        $this->generateNamespaceElements($namespaces, $dom->documentElement);
444
    }
445
446
    /**
447
     * Adds a node to the xml for deprecations and the count value
448
     *
449
     * @param \DOMDocument $dom Markers are extracted and a summary inserted in this object.
450
     *
451
     * @return void
452
     */
453
    protected function buildDeprecationList(\DOMDocument $dom)
454
    {
455
        $nodes = $this->getNodeListForTagBasedQuery($dom, 'deprecated');
456
457
        $node = new \DOMElement('deprecated');
458
        $dom->documentElement->appendChild($node);
459
        $node->setAttribute('count', $nodes->length);
460
    }
461
462
    /**
463
     * Build a tag based query string and return result
464
     *
465
     * @param \DOMDocument $dom    Markers are extracted and a summary inserted
466
     *      in this object.
467
     * @param string       $marker The marker we're searching for throughout xml
468
     *
469
     * @return \DOMNodeList
470
     */
471
    protected function getNodeListForTagBasedQuery($dom, $marker)
472
    {
473
        $xpath = new \DOMXPath($dom);
474
475
        $query = '/project/file/markers/'.$marker.'|';
476
        $query .= '/project/file/docblock/tag[@name="'.$marker.'"]|';
477
        $query .= '/project/file/class/docblock/tag[@name="'.$marker.'"]|';
478
        $query .= '/project/file/class/*/docblock/tag[@name="'.$marker.'"]|';
479
        $query .= '/project/file/interface/docblock/tag[@name="'.$marker.'"]|';
480
        $query .= '/project/file/interface/*/docblock/tag[@name="'.$marker.'"]|';
481
        $query .= '/project/file/function/docblock/tag[@name="'.$marker.'"]|';
482
        $query .= '/project/file/constant/docblock/tag[@name="'.$marker.'"]';
483
484
        $nodes = $xpath->query($query);
485
486
        return $nodes;
487
    }
488
489
    /**
490
     * Generates a hierarchical array of namespaces with their singular name
491
     * from a single level list of namespaces with their full name.
492
     *
493
     * @param array $namespaces the list of namespaces as retrieved from the xml.
494
     *
495
     * @return array
496
     */
497
    protected function generateNamespaceTree($namespaces)
498
    {
499
        sort($namespaces);
500
501
        $result = array();
502
        foreach ($namespaces as $namespace) {
503
            if (!$namespace) {
504
                $namespace = 'global';
505
            }
506
507
            $namespace_list = explode('\\', $namespace);
508
509
            $node = &$result;
510
            foreach ($namespace_list as $singular) {
511
                if (!isset($node[$singular])) {
512
                    $node[$singular] = array();
513
                }
514
515
                $node = &$node[$singular];
516
            }
517
        }
518
519
        return $result;
520
    }
521
522
    /**
523
     * Recursive method to create a hierarchical set of nodes in the dom.
524
     *
525
     * @param array[]     $namespaces     the list of namespaces to process.
526
     * @param \DOMElement $parent_element the node to receive the children of
527
     *                                    the above list.
528
     * @param string      $node_name      the name of the summary element.
529
     *
530
     * @return void
531
     */
532
    protected function generateNamespaceElements($namespaces, $parent_element, $node_name = 'namespace')
533
    {
534
        foreach ($namespaces as $name => $sub_namespaces) {
535
            $node = new \DOMElement($node_name);
536
            $parent_element->appendChild($node);
537
            $node->setAttribute('name', $name);
538
            $fullName = $parent_element->nodeName == $node_name
539
                ? $parent_element->getAttribute('full_name') . '\\' . $name
540
                : $name;
541
            $node->setAttribute('full_name', $fullName);
542
            $this->generateNamespaceElements($sub_namespaces, $node, $node_name);
543
        }
544
    }
545
}
546