Completed
Push — develop ( 722f70...af048b )
by Jaap
15:12 queued 05:04
created

Xml::buildClass()   F

Complexity

Conditions 20
Paths 17496

Size

Total Lines 85
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 46
nc 17496
nop 3
dl 0
loc 85
rs 2.0338
c 0
b 0
f 0

How to fix   Long Method    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
 * phpDocumentor
4
 *
5
 * PHP Version 5.3
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\Plugin\Core\Transformer\Writer;
13
14
use phpDocumentor\Descriptor\Validator\Error;
15
use phpDocumentor\Plugin\Core\Transformer\Writer\Xml\ArgumentConverter;
16
use phpDocumentor\Plugin\Core\Transformer\Writer\Xml\ConstantConverter;
17
use phpDocumentor\Plugin\Core\Transformer\Writer\Xml\DocBlockConverter;
18
use phpDocumentor\Plugin\Core\Transformer\Writer\Xml\InterfaceConverter;
19
use phpDocumentor\Plugin\Core\Transformer\Writer\Xml\MethodConverter;
20
use phpDocumentor\Plugin\Core\Transformer\Writer\Xml\PropertyConverter;
21
use phpDocumentor\Plugin\Core\Transformer\Writer\Xml\TagConverter;
22
use phpDocumentor\Plugin\Core\Transformer\Writer\Xml\TraitConverter;
23
use phpDocumentor\Transformer\Router\RouterAbstract;
24
use phpDocumentor\Transformer\Writer\WriterAbstract;
25
use phpDocumentor\Transformer\Writer\Translatable;
26
use phpDocumentor\Application;
27
use phpDocumentor\Descriptor\ClassDescriptor;
28
use phpDocumentor\Descriptor\ConstantDescriptor;
29
use phpDocumentor\Descriptor\FileDescriptor;
30
use phpDocumentor\Descriptor\FunctionDescriptor;
31
use phpDocumentor\Descriptor\InterfaceDescriptor;
32
use phpDocumentor\Descriptor\ProjectDescriptor;
33
use phpDocumentor\Descriptor\TraitDescriptor;
34
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\AuthorTag;
35
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\CoversTag;
36
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\IgnoreTag;
37
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\InternalTag;
38
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\LicenseTag;
39
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\MethodTag;
40
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\ParamTag;
41
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\PropertyTag;
42
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\ReturnTag;
43
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\UsesTag;
44
use phpDocumentor\Plugin\Core\Transformer\Behaviour\Tag\VarTag;
45
use phpDocumentor\Transformer\Transformation;
46
use phpDocumentor\Transformer\Transformer;
47
use phpDocumentor\Translator\Translator;
48
49
/**
50
 * Converts the structural information of phpDocumentor into an XML file.
51
 */
52
class Xml extends WriterAbstract implements Translatable
0 ignored issues
show
Complexity introduced by
This class has a complexity of 64 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 Xml has a coupling between objects value of 35. Consider to reduce the number of dependencies under 13.
Loading history...
53
{
54
    /** @var \DOMDocument $xml */
55
    protected $xml;
56
57
    /** @var Translator $translator */
58
    protected $translator;
59
60
    protected $docBlockConverter;
61
62
    protected $argumentConverter;
63
64
    protected $methodConverter;
65
66
    protected $propertyConverter;
67
68
    protected $constantConverter;
69
70
    protected $interfaceConverter;
71
72
    protected $traitConverter;
73
74
    public function __construct(RouterAbstract $router)
75
    {
76
        $this->docBlockConverter  = new DocBlockConverter(new TagConverter(), $router);
77
        $this->argumentConverter  = new ArgumentConverter();
78
        $this->methodConverter    = new MethodConverter($this->argumentConverter, $this->docBlockConverter);
79
        $this->propertyConverter  = new PropertyConverter($this->docBlockConverter);
80
        $this->constantConverter  = new ConstantConverter($this->docBlockConverter);
81
        $this->interfaceConverter = new InterfaceConverter(
82
            $this->docBlockConverter,
83
            $this->methodConverter,
84
            $this->constantConverter
85
        );
86
        $this->traitConverter = new TraitConverter(
87
            $this->docBlockConverter,
88
            $this->methodConverter,
89
            $this->propertyConverter
90
        );
91
    }
92
93
    /**
94
     * Returns an instance of the object responsible for translating content.
95
     *
96
     * @return Translator
97
     */
98
    public function getTranslator()
99
    {
100
        return $this->translator;
101
    }
102
103
    /**
104
     * Sets a new object capable of translating strings on this writer.
105
     *
106
     * @param Translator $translator
107
     *
108
     * @return void
109
     */
110
    public function setTranslator(Translator $translator)
111
    {
112
        $this->translator = $translator;
113
    }
114
115
    /**
116
     * This method generates the AST output
117
     *
118
     * @param ProjectDescriptor $project        Document containing the structure.
119
     * @param Transformation    $transformation Transformation to execute.
120
     *
121
     * @return void
122
     */
123
    public function transform(ProjectDescriptor $project, Transformation $transformation)
124
    {
125
        $artifact = $this->getDestinationPath($transformation);
126
127
        $this->checkForSpacesInPath($artifact);
128
129
        $this->xml = new \DOMDocument('1.0', 'utf-8');
130
        $this->xml->formatOutput = true;
131
        $document_element = new \DOMElement('project');
132
        $this->xml->appendChild($document_element);
133
134
        $document_element->setAttribute('title', $project->getName());
135
        $document_element->setAttribute('version', Application::$VERSION);
136
137
        $this->buildPartials($document_element, $project);
138
139
        $transformer = $transformation->getTransformer();
140
141
        foreach ($project->getFiles() as $file) {
142
            $this->buildFile($document_element, $file, $transformer);
143
        }
144
145
        $this->finalize($project);
146
        file_put_contents($artifact, $this->xml->saveXML());
147
    }
148
149
    protected function buildPartials(\DOMElement $parent, ProjectDescriptor $project)
150
    {
151
        $child = new \DOMElement('partials');
152
        $parent->appendChild($child);
153
        foreach ($project->getPartials() as $name => $element) {
154
            $partial = new \DOMElement('partial');
155
            $child->appendChild($partial);
156
            $partial->setAttribute('name', $name);
157
            $partial->appendChild(new \DOMText($element));
158
        }
159
    }
160
161
    protected function buildFile(\DOMElement $parent, FileDescriptor $file, Transformer $transformer)
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...
162
    {
163
        $child = new \DOMElement('file');
164
        $parent->appendChild($child);
165
166
        $path = ltrim($file->getPath(), './');
167
        $child->setAttribute('path', $path);
168
        $child->setAttribute(
169
            'generated-path',
170
            $transformer->generateFilename($path)
171
        );
172
        $child->setAttribute('hash', $file->getHash());
173
174
        $this->docBlockConverter->convert($child, $file);
175
176
        // add namespace aliases
177
        foreach ($file->getNamespaceAliases() as $alias => $namespace) {
178
            $alias_obj = new \DOMElement('namespace-alias', $namespace);
179
            $child->appendChild($alias_obj);
180
            $alias_obj->setAttribute('name', $alias);
181
        }
182
183
        /** @var ConstantDescriptor $constant */
184
        foreach ($file->getConstants() as $constant) {
185
            $this->constantConverter->convert($child, $constant);
186
        }
187
188
        /** @var FunctionDescriptor $function */
189
        foreach ($file->getFunctions() as $function) {
190
            $this->buildFunction($child, $function);
191
        }
192
193
        /** @var InterfaceDescriptor $interface */
194
        foreach ($file->getInterfaces() as $interface) {
195
            $this->interfaceConverter->convert($child, $interface);
196
        }
197
198
        /** @var ClassDescriptor $class */
199
        foreach ($file->getClasses() as $class) {
200
            $this->buildClass($child, $class);
201
        }
202
203
        /** @var TraitDescriptor $class */
204
        foreach ($file->getTraits() as $trait) {
205
            $this->traitConverter->convert($child, $trait);
206
        }
207
208
        // add markers
209
        if (count($file->getMarkers()) > 0) {
210
            $markers = new \DOMElement('markers');
211
            $child->appendChild($markers);
212
213
            foreach ($file->getMarkers() as $marker) {
214
                if (! $marker['type']) {
215
                    continue;
216
                }
217
218
                $type = preg_replace('/[^A-Za-z0-9\-]/', '', $marker['type']);
219
                $marker_obj = new \DOMElement(strtolower($type));
220
                $markers->appendChild($marker_obj);
221
222
                $marker_obj->appendChild(new \DOMText(trim($marker['message'])));
223
                $marker_obj->setAttribute('line', $marker['line']);
224
            }
225
        }
226
227
        $errors = $file->getAllErrors();
228
        if (count($errors) > 0) {
229
            $parse_errors = new \DOMElement('parse_markers');
230
            $child->appendChild($parse_errors);
231
232
            /** @var Error $error */
233
            foreach ($errors as $error) {
234
                $this->createErrorEntry($error, $parse_errors);
235
            }
236
        }
237
238
        // if we want to include the source for each file; append a new
239
        // element 'source' which contains a compressed, encoded version
240
        // of the source
241
        if ($file->getSource()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $file->getSource() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
242
            $child->appendChild(new \DOMElement('source', base64_encode(gzcompress($file->getSource()))));
243
        }
244
    }
245
246
    /**
247
     * Creates an entry in the ParseErrors collection of a file for a given error.
248
     *
249
     * @param Error       $error
250
     * @param \DOMElement $parse_errors
251
     *
252
     * @return void
253
     */
254
    protected function createErrorEntry($error, $parse_errors)
255
    {
256
        $marker_obj = new \DOMElement(strtolower($error->getSeverity()));
257
        $parse_errors->appendChild($marker_obj);
258
259
        $message = ($this->getTranslator())
260
            ? vsprintf($this->getTranslator()->translate($error->getCode()), $error->getContext())
261
            : $error->getCode();
262
263
        $marker_obj->appendChild(new \DOMText($message));
264
        $marker_obj->setAttribute('line', $error->getLine());
265
        $marker_obj->setAttribute('code', $error->getCode());
266
    }
267
268
    /**
269
     * Retrieves the destination location for this artifact.
270
     *
271
     * @param Transformation $transformation
272
     *
273
     * @return string
274
     */
275
    protected function getDestinationPath(Transformation $transformation)
276
    {
277
        return $transformation->getTransformer()->getTarget()
278
            . DIRECTORY_SEPARATOR . $transformation->getArtifact();
279
    }
280
281
    /**
282
     * Export this function definition to the given parent DOMElement.
283
     *
284
     * @param \DOMElement        $parent   Element to augment.
285
     * @param FunctionDescriptor $function Element to export.
286
     * @param \DOMElement        $child    if supplied this element will be augmented instead of freshly added.
287
     *
288
     * @return void
289
     */
290
    public function buildFunction(\DOMElement $parent, FunctionDescriptor $function, \DOMElement $child = null)
291
    {
292
        if (!$child) {
293
            $child = new \DOMElement('function');
294
            $parent->appendChild($child);
295
        }
296
297
        $namespace = $function->getNamespace()
298
            ? $function->getNamespace()
299
            : $parent->getAttribute('namespace');
300
        $child->setAttribute('namespace', ltrim($namespace, '\\'));
301
        $child->setAttribute('line', $function->getLine());
302
303
        $child->appendChild(new \DOMElement('name', $function->getName()));
304
        $child->appendChild(new \DOMElement('full_name', $function->getFullyQualifiedStructuralElementName()));
305
306
        $this->docBlockConverter->convert($child, $function);
307
308
        foreach ($function->getArguments() as $argument) {
309
            $this->argumentConverter->convert($child, $argument);
310
        }
311
    }
312
313
    /**
314
     * Exports the given reflection object to the parent XML element.
315
     *
316
     * This method creates a new child element on the given parent XML element
317
     * and takes the properties of the Reflection argument and sets the
318
     * elements and attributes on the child.
319
     *
320
     * If a child DOMElement is provided then the properties and attributes are
321
     * set on this but the child element is not appended onto the parent. This
322
     * is the responsibility of the invoker. Essentially this means that the
323
     * $parent argument is ignored in this case.
324
     *
325
     * @param \DOMElement     $parent The parent element to augment.
326
     * @param ClassDescriptor $class  The data source.
327
     * @param \DOMElement     $child  Optional: child element to use instead of creating a
328
     *      new one on the $parent.
329
     *
330
     * @return void
331
     */
332
    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...
333
    {
334
        if (!$child) {
335
            $child = new \DOMElement('class');
336
            $parent->appendChild($child);
337
        }
338
339
        $child->setAttribute('final', $class->isFinal() ? 'true' : 'false');
340
        $child->setAttribute('abstract', $class->isAbstract() ? 'true' : 'false');
341
342
        $parentFqcn = is_string($class->getParent())
343
            ? $class->getParent()
344
            : $class->getParent()->getFullyQualifiedStructuralElementName();
345
        $child->appendChild(new \DOMElement('extends', $parentFqcn));
346
347
        /** @var InterfaceDescriptor $interface */
348
        foreach ($class->getInterfaces() as $interface) {
349
            $interfaceFqcn = is_string($interface)
350
                ? $interface
351
                : $interface->getFullyQualifiedStructuralElementName();
352
            $child->appendChild(new \DOMElement('implements', $interfaceFqcn));
353
        }
354
355
        if ($child === null) {
356
            $child = new \DOMElement('interface');
357
            $parent->appendChild($child);
358
        }
359
360
        $namespace = $class->getNamespace()->getFullyQualifiedStructuralElementName();
361
        $child->setAttribute('namespace', ltrim($namespace, '\\'));
362
        $child->setAttribute('line', $class->getLine());
363
364
        $child->appendChild(new \DOMElement('name', $class->getName()));
365
        $child->appendChild(new \DOMElement('full_name', $class->getFullyQualifiedStructuralElementName()));
366
367
        $this->docBlockConverter->convert($child, $class);
368
369
        foreach ($class->getConstants() as $constant) {
370
            // TODO #840: Workaround; for some reason there are NULLs in the constants array.
371
            if ($constant) {
372
                $this->constantConverter->convert($child, $constant);
373
            }
374
        }
375
376
        foreach ($class->getInheritedConstants() as $constant) {
377
            // TODO #840: Workaround; for some reason there are NULLs in the constants array.
378
            if ($constant) {
379
                $this->constantConverter->convert($child, $constant);
380
            }
381
        }
382
383
        foreach ($class->getProperties() as $property) {
384
            // TODO #840: Workaround; for some reason there are NULLs in the properties array.
385
            if ($property) {
386
                $this->propertyConverter->convert($child, $property);
387
            }
388
        }
389
390
        foreach ($class->getInheritedProperties() as $property) {
391
            // TODO #840: Workaround; for some reason there are NULLs in the properties array.
392
            if ($property) {
393
                $this->propertyConverter->convert($child, $property);
394
            }
395
        }
396
397
        foreach ($class->getMethods() as $method) {
398
            // TODO #840: Workaround; for some reason there are NULLs in the methods array.
399
            if ($method) {
400
                $this->methodConverter->convert($child, $method);
401
            }
402
        }
403
404
        foreach ($class->getInheritedMethods() as $method) {
405
            // TODO #840: Workaround; for some reason there are NULLs in the methods array.
406
            if ($method) {
407
                $methodElement = $this->methodConverter->convert($child, $method);
408
                $methodElement->appendChild(
409
                    new \DOMElement(
410
                        'inherited_from',
411
                        $method->getParent()->getFullyQualifiedStructuralElementName()
412
                    )
413
                );
414
            }
415
        }
416
    }
417
418
    /**
419
     * Finalizes the processing and executing all post-processing actions.
420
     *
421
     * This method is responsible for extracting and manipulating the data that
422
     * is global to the project, such as:
423
     *
424
     * - Package tree
425
     * - Namespace tree
426
     * - Marker list
427
     * - Deprecated elements listing
428
     * - Removal of objects related to visibility
429
     *
430
     * @param ProjectDescriptor $projectDescriptor
431
     *
432
     * @return void
433
     */
434
    protected function finalize(ProjectDescriptor $projectDescriptor)
435
    {
436
        // TODO: move all these behaviours to a central location for all template parsers
437
        $behaviour = new AuthorTag();
438
        $behaviour->process($this->xml);
439
        $behaviour = new CoversTag();
440
        $behaviour->process($this->xml);
441
        $behaviour = new IgnoreTag();
442
        $behaviour->process($this->xml);
443
        $behaviour = new InternalTag(
444
            $projectDescriptor->isVisibilityAllowed(ProjectDescriptor\Settings::VISIBILITY_INTERNAL)
445
        );
446
        $behaviour->process($this->xml);
447
        $behaviour = new LicenseTag();
448
        $behaviour->process($this->xml);
449
        $behaviour = new MethodTag();
450
        $behaviour->process($this->xml);
451
        $behaviour = new ParamTag();
452
        $behaviour->process($this->xml);
453
        $behaviour = new PropertyTag();
454
        $behaviour->process($this->xml);
455
        $behaviour = new ReturnTag();
456
        $behaviour->process($this->xml);
457
        $behaviour = new UsesTag();
458
        $behaviour->process($this->xml);
459
        $behaviour = new VarTag();
460
        $behaviour->process($this->xml);
461
        $this->buildPackageTree($this->xml);
462
        $this->buildNamespaceTree($this->xml);
463
        $this->buildDeprecationList($this->xml);
464
    }
465
466
    /**
467
     * Collects all packages and subpackages, and adds a new section in the
468
     * DOM to provide an overview.
469
     *
470
     * @param \DOMDocument $dom Packages are extracted and a summary inserted
471
     *     in this object.
472
     *
473
     * @return void
474
     */
475
    protected function buildPackageTree(\DOMDocument $dom)
476
    {
477
        $xpath = new \DOMXPath($dom);
478
        $packages = array('global' => true);
479
        $qry = $xpath->query('//@package');
480
        for ($i = 0; $i < $qry->length; $i++) {
481
            if (isset($packages[$qry->item($i)->nodeValue])) {
482
                continue;
483
            }
484
485
            $packages[$qry->item($i)->nodeValue] = true;
486
        }
487
488
        $packages = $this->generateNamespaceTree(array_keys($packages));
489
        $this->generateNamespaceElements($packages, $dom->documentElement, 'package');
490
    }
491
492
    /**
493
     * Collects all namespaces and sub-namespaces, and adds a new section in
494
     * the DOM to provide an overview.
495
     *
496
     * @param \DOMDocument $dom Namespaces are extracted and a summary inserted
497
     *     in this object.
498
     *
499
     * @return void
500
     */
501
    protected function buildNamespaceTree(\DOMDocument $dom)
502
    {
503
        $xpath = new \DOMXPath($dom);
504
        $namespaces = array();
505
        $qry = $xpath->query('//@namespace');
506
        for ($i = 0; $i < $qry->length; $i++) {
507
            if (isset($namespaces[$qry->item($i)->nodeValue])) {
508
                continue;
509
            }
510
511
            $namespaces[$qry->item($i)->nodeValue] = true;
512
        }
513
514
        $namespaces = $this->generateNamespaceTree(array_keys($namespaces));
515
        $this->generateNamespaceElements($namespaces, $dom->documentElement);
516
    }
517
518
    /**
519
     * Adds a node to the xml for deprecations and the count value
520
     *
521
     * @param \DOMDocument $dom Markers are extracted and a summary inserted in this object.
522
     *
523
     * @return void
524
     */
525
    protected function buildDeprecationList(\DOMDocument $dom)
526
    {
527
        $nodes = $this->getNodeListForTagBasedQuery($dom, 'deprecated');
528
529
        $node = new \DOMElement('deprecated');
530
        $dom->documentElement->appendChild($node);
531
        $node->setAttribute('count', $nodes->length);
532
    }
533
534
    /**
535
     * Build a tag based query string and return result
536
     *
537
     * @param \DOMDocument $dom    Markers are extracted and a summary inserted
538
     *      in this object.
539
     * @param string       $marker The marker we're searching for throughout xml
540
     *
541
     * @return \DOMNodeList
542
     */
543
    protected function getNodeListForTagBasedQuery($dom, $marker)
544
    {
545
        $xpath = new \DOMXPath($dom);
546
547
        $query = '/project/file/markers/'.$marker.'|';
548
        $query .= '/project/file/docblock/tag[@name="'.$marker.'"]|';
549
        $query .= '/project/file/class/docblock/tag[@name="'.$marker.'"]|';
550
        $query .= '/project/file/class/*/docblock/tag[@name="'.$marker.'"]|';
551
        $query .= '/project/file/interface/docblock/tag[@name="'.$marker.'"]|';
552
        $query .= '/project/file/interface/*/docblock/tag[@name="'.$marker.'"]|';
553
        $query .= '/project/file/function/docblock/tag[@name="'.$marker.'"]|';
554
        $query .= '/project/file/constant/docblock/tag[@name="'.$marker.'"]';
555
556
        $nodes = $xpath->query($query);
557
558
        return $nodes;
559
    }
560
561
    /**
562
     * Generates a hierarchical array of namespaces with their singular name
563
     * from a single level list of namespaces with their full name.
564
     *
565
     * @param array $namespaces the list of namespaces as retrieved from the xml.
566
     *
567
     * @return array
568
     */
569
    protected function generateNamespaceTree($namespaces)
570
    {
571
        sort($namespaces);
572
573
        $result = array();
574
        foreach ($namespaces as $namespace) {
575
            if (!$namespace) {
576
                $namespace = 'global';
577
            }
578
579
            $namespace_list = explode('\\', $namespace);
580
581
            $node = &$result;
582
            foreach ($namespace_list as $singular) {
583
                if (!isset($node[$singular])) {
584
                    $node[$singular] = array();
585
                }
586
587
                $node = &$node[$singular];
588
            }
589
        }
590
591
        return $result;
592
    }
593
594
    /**
595
     * Recursive method to create a hierarchical set of nodes in the dom.
596
     *
597
     * @param array[]     $namespaces     the list of namespaces to process.
598
     * @param \DOMElement $parent_element the node to receive the children of
599
     *                                    the above list.
600
     * @param string      $node_name      the name of the summary element.
601
     *
602
     * @return void
603
     */
604
    protected function generateNamespaceElements($namespaces, $parent_element, $node_name = 'namespace')
605
    {
606
        foreach ($namespaces as $name => $sub_namespaces) {
607
            $node = new \DOMElement($node_name);
608
            $parent_element->appendChild($node);
609
            $node->setAttribute('name', $name);
610
            $fullName = $parent_element->nodeName == $node_name
611
                ? $parent_element->getAttribute('full_name') . '\\' . $name
612
                : $name;
613
            $node->setAttribute('full_name', $fullName);
614
            $this->generateNamespaceElements($sub_namespaces, $node, $node_name);
615
        }
616
    }
617
}
618