GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( c26b16...cbbbe0 )
by Orlando
01:53
created

Node   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 0
dl 0
loc 273
c 0
b 0
f 0
rs 9.92

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 2
B add() 0 55 9
A getDirectChildElementByName() 0 10 3
A setNamespace() 0 26 5
A setSchemaDefinition() 0 13 2
A getAttr() 0 10 2
A setAttr() 0 8 3
A getElement() 0 4 1
A getDocument() 0 4 1
A getWrapperNodeName() 0 4 1
A getParentNodeName() 0 4 1
A getNodeName() 0 4 1
1
<?php
2
3
/*
4
 * This file is part of the cfdi-xml project.
5
 *
6
 * (c) Kinedu
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Kinedu\CfdiXML\Common;
13
14
use DOMElement;
15
use DOMDocument;
16
use DOMNodeList;
17
18
class Node
19
{
20
    /**
21
     * Node document.
22
     *
23
     * @var DOMDocument
24
     */
25
    protected $document;
26
27
    /**
28
     * Node element.
29
     *
30
     * @var DOMElement
31
     */
32
    protected $element;
33
34
    /**
35
     * Define the parent node name.
36
     *
37
     * @var string|null
38
     */
39
    protected $parentNodeName = null;
40
41
    /**
42
     * Define the node name.
43
     *
44
     * @var string
45
     */
46
    protected $nodeName;
47
48
    /**
49
     * Node attributes.
50
     *
51
     * @var array
52
     */
53
    protected $attr = [];
54
55
    /**
56
     * Create a new node instance.
57
     *
58
     * @param array $attr
59
     */
60
    public function __construct(array ...$attr)
61
    {
62
        $this->attr = $attr;
63
64
        $this->document = new DOMDocument('1.0', 'UTF-8');
65
        $this->document->preserveWhiteSpace = false;
66
        $this->document->formatOutput = true;
67
68
        if ($nodeName = $this->getNodeName()) {
69
            $this->element = $this->document->createElement($nodeName);
70
            $this->document->appendChild($this->element);
71
            $this->setAttr($this->element, $this->getAttr());
72
        }
73
    }
74
75
    /**
76
     * Add a new node.
77
     *
78
     * @param \Kinedu\CfdiXML\Common\Node $node
79
     *
80
     * @return void
81
     */
82
    public function add(Node $node)
83
    {
84
        $wrapperElement = null;
85
86
        if ($node->schemaDefinition && $node->globalSchemaLocation) {
0 ignored issues
show
Bug introduced by
The property schemaDefinition does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property globalSchemaLocation does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
87
            $this->setSchemaDefinition(
88
                $node->getSchemaLocation()
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Kinedu\CfdiXML\Common\Node as the method getSchemaLocation() does only exist in the following sub-classes of Kinedu\CfdiXML\Common\Node: Kinedu\CfdiXML\Common\Complemento, Kinedu\CfdiXML\Common\ComplementoBase, Kinedu\CfdiXML\Common\ComplementoConcepto, Kinedu\CfdiXML\Common\Complemento\IEDU, Kinedu\CfdiXML\Common\Complemento\Pago, Kinedu\CfdiXML\Common\Co...nto\TimbreFiscalDigital. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
89
            );
90
91
            $this->setNamespace($node);
92
        }
93
94
        $nodeElement = $this->document->createElement($node->getNodeName());
95
        $this->setAttr($nodeElement, $node->getAttr());
96
97
        foreach ($node->element->childNodes as $child) {
98
            $nodeElement->appendChild(
99
                $this->document->importNode($child, true)
100
            );
101
        }
102
103
        if ($wrapperName = $node->getWrapperNodeName()) {
104
            $wrapperElement = $this->getDirectChildElementByName(
105
                $this->element->childNodes,
106
                $wrapperName
107
            );
108
109
            if (!$wrapperElement) {
110
                $wrapperElement = $this->document->createElement($wrapperName);
111
                $this->element->appendChild($wrapperElement);
112
            }
113
114
            $this->setAttr($wrapperElement, $node->getAttr('wrapper'));
115
        }
116
117
        if ($parentName = $node->getParentNodeName()) {
118
            $currentElement = ($wrapperElement) ? $wrapperElement : $this->element;
119
120
            $parentNode = $this->getDirectChildElementByName(
121
                $currentElement->childNodes,
122
                $parentName
123
            );
124
125
            if (!$parentNode) {
126
                $parentElement = $this->document->createElement($parentName);
127
                $currentElement->appendChild($parentElement);
128
                $parentElement->appendChild($nodeElement);
129
                $this->setAttr($parentElement, $node->getAttr('parent'));
130
            } else {
131
                $parentNode->appendChild($nodeElement);
132
            }
133
        } else {
134
            $this->element->appendChild($nodeElement);
135
        }
136
    }
137
138
    /**
139
     * Search the direct child of an element.
140
     *
141
     * @param DOMNodeList $children
142
     * @param string $find
143
     *
144
     * @return DOMElement|null
145
     */
146
    protected function getDirectChildElementByName(DOMNodeList $children, string $find)
147
    {
148
        foreach ($children as $child) {
149
            if ($child->nodeName == $find) {
150
                return $child;
151
            }
152
        }
153
154
        return null;
155
    }
156
157
    /**
158
     * @param  \Kinedu\CfdiXML\Common\Node  $node
159
     * @return void
160
     */
161
    public function setNamespace(Node $node)
162
    {
163
        $element = $this->element;
164
165
        $namespaceKey = "xmlns:{$node->namespaceKey}";
0 ignored issues
show
Bug introduced by
The property namespaceKey does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
166
        $namespaceValue = $node->getNamespace();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Kinedu\CfdiXML\Common\Node as the method getNamespace() does only exist in the following sub-classes of Kinedu\CfdiXML\Common\Node: Kinedu\CfdiXML\Common\Complemento, Kinedu\CfdiXML\Common\ComplementoBase, Kinedu\CfdiXML\Common\ComplementoConcepto, Kinedu\CfdiXML\Common\Complemento\IEDU, Kinedu\CfdiXML\Common\Complemento\Pago, Kinedu\CfdiXML\Common\Co...nto\TimbreFiscalDigital. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
167
168
        $elementAttr = [];
169
170
        if ($element->hasAttributes()) {
171
            $lastAttrName = null;
172
173
            foreach (iterator_to_array($element->attributes) as $attr) {
174
                if ((substr($lastAttrName, 0, 5) == 'xmlns') &&
175
                    (substr($attr->name, 0, 5) != 'xmlns')) {
176
                    $elementAttr[$namespaceKey] = $namespaceValue;
177
                }
178
179
                $elementAttr[$lastAttrName = $attr->name] = $attr->value;
180
181
                $element->removeAttributeNode($attr);
182
            }
183
        }
184
185
        $this->setAttr($element, $elementAttr);
186
    }
187
188
    /**
189
     * @param  string  $schemaDefinition
190
     * @return void
191
     */
192
    public function setSchemaDefinition(string $schemaDefinition)
193
    {
194
        $attrName = 'xsi:schemaLocation';
195
196
        $node = $this->element;
197
198
        if ($node->hasAttribute($attrName)) {
199
            $value = $node->getAttribute($attrName);
200
            $value = "{$value} {$schemaDefinition}";
201
202
            $node->setAttribute($attrName, $value);
203
        }
204
    }
205
206
    /**
207
     * Get node attributes.
208
     *
209
     * @param string $index
210
     *
211
     * @return array|null
212
     */
213
    public function getAttr(string $index = 'node')
214
    {
215
        $attrIndex = ['node', 'parent', 'wrapper'];
216
217
        $index = (in_array($index, $attrIndex))
218
            ? array_search($index, $attrIndex)
219
            : 0;
220
221
        return $this->attr[$index] ?? null;
222
    }
223
224
    /**
225
     * Adds attributes to an element.
226
     *
227
     * @param DOMElement $element
228
     * @param array $attr
229
     *
230
     * @return void
231
     */
232
    public function setAttr(DOMElement $element, array $attr = null)
233
    {
234
        if (!is_null($attr)) {
235
            foreach ($attr as $key => $value) {
236
                $element->setAttribute($key, $value);
237
            }
238
        }
239
    }
240
241
    /**
242
     * Get element.
243
     *
244
     * @return DOMElement
245
     */
246
    public function getElement(): DOMElement
247
    {
248
        return $this->element;
249
    }
250
251
    /**
252
     * Get document.
253
     *
254
     * @return DOMDocument
255
     */
256
    public function getDocument(): DOMDocument
257
    {
258
        return $this->document;
259
    }
260
261
    /**
262
     * Get wrapper node name.
263
     *
264
     * @return string|null
265
     */
266
    public function getWrapperNodeName()
267
    {
268
        return $this->wrapperNodeName ?? null;
0 ignored issues
show
Bug introduced by
The property wrapperNodeName does not seem to exist. Did you mean nodeName?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
269
    }
270
271
    /**
272
     * Get parent node name.
273
     *
274
     * @return string|null
275
     */
276
    public function getParentNodeName()
277
    {
278
        return $this->parentNodeName ?? null;
279
    }
280
281
    /**
282
     * Get node name.
283
     *
284
     * @return string
285
     */
286
    public function getNodeName()
287
    {
288
        return $this->nodeName ?? null;
289
    }
290
}
291