Completed
Push — next ( c05e12...5d0fe3 )
by Mathias
04:06
created

XmlBuilder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 1
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 1
eloc 1
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * YAWIK
4
 *
5
 * @filesource
6
 * @license MIT
7
 * @copyright  2013 - 2017 Cross Solution <http://cross-solution.de>
8
 */
9
  
10
/** */
11
namespace StackoverflowApi\Utils;
12
13
/**
14
 * Util to create XML documents/strings from an array specification.
15
 * 
16
 * @author Mathias Gelhausen <[email protected]>
17
 */
18
class XmlBuilder
19
{
20
    /**
21
     * static class
22
     */
23
    private function __construct() {}
24
25
    /**
26
     * Creates a DOMDocument
27
     *
28
     * @param array $specs
29
     *
30
     * @return \DOMDocument
31
     */
32 1
    public static function createDocument(array $specs)
33
    {
34 1
        $xml = new \DOMDocument('1.0', 'utf-8');
35
36 1
        self::build($xml, $specs);
37
38 1
        return $xml;
39
    }
40
41
    /**
42
     * Creates a XML string
43
     *
44
     * @param array $specs
45
     *
46
     * @return string
47
     */
48 1
    public static function createXml(array $specs)
49
    {
50 1
        $doc = self::createDocument($specs);
51 1
        return $doc->saveXML();
52
    }
53
54
    /**
55
     * Builds the DOMDocument.
56
     *
57
     * Format of specs:
58
     *
59
     *
60
     * [
61
     *  'node' => 'textNode',       #create the node 'node' with textNode 'textNode' (=> <node>textNode</node>)
62
     *  ':node' => 'cdataNode',     #create the node 'node' with CDATA (=> <node><![CDATA[cdataNode]]></node>)
63
     *  '@attr' => 'value',         # adds an attribute to the node (=> <node attr="value">)
64
     *  'node' => [                 # Add multiple nodes when provide enumerated array
65
     *      [ 'subNode' => [
66
     *          [ '@attr' => 'value',
67
     *            'text',           # Creates a textNode and append it to current node
68
     *          ],
69
     *          [ '@attr' => 'value',
70
     *            ':cdata'          # Create a cdata node and append it to current node
71
     *          ]
72
     *      ],
73
     *      [
74
     *         '@attr' => popel',
75
     *          'subSubNode' => [
76
     *          ]
77
     *      ],
78
     *  'single' => [
79
     *      'maybe some text',
80
     *      'child' => 'text',
81
     *      'some more text',
82
     *   ],
83
     *  ]
84
     *
85
     * @param \DOMDocument|\DOMElement $node
86
     * @param array $specs
87
     */
88 1
    private static function build($node, array $specs)
89
    {
90 1
        $doc = $node->ownerDocument ?: $node;
91
92 1
        foreach ($specs as $name => $spec) {
93 1
            if (0 === strpos($name, '@')) {
94 1
                $node->setAttribute(substr($name, 1), $spec);
0 ignored issues
show
Bug introduced by
The method setAttribute does only exist in DOMElement, but not in DOMDocument.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
95 1
                continue;
96
            }
97
98 1
            if (is_string($spec) && 0 !== strpos($name, '_')) {
99 1
                if (is_numeric($name)) {
100 1 View Code Duplication
                    if (0 === strpos($spec, ':')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

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

Loading history...
101 1
                        $name = '_cdata';
102 1
                        $spec = substr($spec, 1);
103 1
                    } else {
104 1
                        $name = '_text';
105
                    }
106 1
                } else if (0 === strpos($name, ':')) {
107 1
                    $spec = ['_cdata' => $spec];
108 1
                    $name = substr($name, 1);
109 1 View Code Duplication
                } else if (0 === strpos($spec, ':')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

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

Loading history...
110 1
                    $spec = ['_cdata' => substr($spec, 1)];
111 1
                } else {
112 1
                    $spec = ['_text' => $spec];
113
                }
114 1
            }
115
116 1
            if (is_array($spec)) {
117 1
                if (isset($spec[ 0 ]) && !is_string($spec[ 0 ])) {
118 1
                    foreach ($spec as $s) {
119 1
                        self::build($node, [$name => $s]);
120 1
                    }
121 1
                    continue;
122
                }
123
124 1
                $child = $doc->createElement($name);
125 1
                $node->appendChild($child);
126 1
                self::build($child, $spec);
127 1
                continue;
128
            }
129
130 1
            $content = '_text' == $name ? $doc->createTextNode($spec) : $doc->createCdataSection($spec);
131 1
            $node->appendChild($content);
132 1
        }
133 1
    }
134
}
135