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 ( cbbbe0...88bf3c )
by Orlando
01:22
created

Node::isGlobalDefinition()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 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 ($this->hasSchemaDefinition($node) &&
87
            $this->isGlobalDefinition($node)) {
88
            $this->setSchemaDefinition(
89
                $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...
90
            );
91
92
            $this->setNamespace($node);
93
        }
94
95
        $nodeElement = $this->document->createElement($node->getNodeName());
96
        $this->setAttr($nodeElement, $node->getAttr());
97
98
        foreach ($node->element->childNodes as $child) {
99
            $nodeElement->appendChild(
100
                $this->document->importNode($child, true)
101
            );
102
        }
103
104
        if ($wrapperName = $node->getWrapperNodeName()) {
105
            $wrapperElement = $this->getDirectChildElementByName(
106
                $this->element->childNodes,
107
                $wrapperName
108
            );
109
110
            if (!$wrapperElement) {
111
                $wrapperElement = $this->document->createElement($wrapperName);
112
                $this->element->appendChild($wrapperElement);
113
            }
114
115
            $this->setAttr($wrapperElement, $node->getAttr('wrapper'));
116
        }
117
118
        if ($parentName = $node->getParentNodeName()) {
119
            $currentElement = ($wrapperElement) ? $wrapperElement : $this->element;
120
121
            $parentNode = $this->getDirectChildElementByName(
122
                $currentElement->childNodes,
123
                $parentName
124
            );
125
126
            if (!$parentNode) {
127
                $parentElement = $this->document->createElement($parentName);
128
                $currentElement->appendChild($parentElement);
129
                $parentElement->appendChild($nodeElement);
130
                $this->setAttr($parentElement, $node->getAttr('parent'));
131
            } else {
132
                $parentNode->appendChild($nodeElement);
133
            }
134
        } else {
135
            $this->element->appendChild($nodeElement);
136
        }
137
    }
138
139
    /**
140
     * Search the direct child of an element.
141
     *
142
     * @param DOMNodeList $children
143
     * @param string $find
144
     *
145
     * @return DOMElement|null
146
     */
147
    protected function getDirectChildElementByName(DOMNodeList $children, string $find)
148
    {
149
        foreach ($children as $child) {
150
            if ($child->nodeName == $find) {
151
                return $child;
152
            }
153
        }
154
155
        return null;
156
    }
157
158
    /**
159
     * @param  \Kinedu\CfdiXML\Common\Node  $node
160
     * @return void
161
     */
162
    public function setNamespace(Node $node)
163
    {
164
        $element = $this->element;
165
166
        $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...
167
        $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...
168
169
        $elementAttr = [];
170
171
        if ($element->hasAttributes()) {
172
            $lastAttrName = null;
173
174
            foreach (iterator_to_array($element->attributes) as $attr) {
175
                if ((substr($lastAttrName, 0, 5) == 'xmlns') &&
176
                    (substr($attr->name, 0, 5) != 'xmlns')) {
177
                    $elementAttr[$namespaceKey] = $namespaceValue;
178
                }
179
180
                $elementAttr[$lastAttrName = $attr->name] = $attr->value;
181
182
                $element->removeAttributeNode($attr);
183
            }
184
        }
185
186
        $this->setAttr($element, $elementAttr);
187
    }
188
189
    /**
190
     * @param  string  $schemaDefinition
191
     * @return void
192
     */
193
    public function setSchemaDefinition(string $schemaDefinition)
194
    {
195
        $attrName = 'xsi:schemaLocation';
196
197
        $node = $this->element;
198
199
        if ($node->hasAttribute($attrName)) {
200
            $value = $node->getAttribute($attrName);
201
            $value = "{$value} {$schemaDefinition}";
202
203
            $node->setAttribute($attrName, $value);
204
        }
205
    }
206
207
    /**
208
     * Get node attributes.
209
     *
210
     * @param string $index
211
     *
212
     * @return array|null
213
     */
214
    public function getAttr(string $index = 'node')
215
    {
216
        $attrIndex = ['node', 'parent', 'wrapper'];
217
218
        $index = (in_array($index, $attrIndex))
219
            ? array_search($index, $attrIndex)
220
            : 0;
221
222
        return $this->attr[$index] ?? null;
223
    }
224
225
    /**
226
     * Adds attributes to an element.
227
     *
228
     * @param DOMElement $element
229
     * @param array $attr
230
     *
231
     * @return void
232
     */
233
    public function setAttr(DOMElement $element, array $attr = null)
234
    {
235
        if (!is_null($attr)) {
236
            foreach ($attr as $key => $value) {
237
                $element->setAttribute($key, $value);
238
            }
239
        }
240
    }
241
242
    /**
243
     * Get element.
244
     *
245
     * @return DOMElement
246
     */
247
    public function getElement(): DOMElement
248
    {
249
        return $this->element;
250
    }
251
252
    /**
253
     * Get document.
254
     *
255
     * @return DOMDocument
256
     */
257
    public function getDocument(): DOMDocument
258
    {
259
        return $this->document;
260
    }
261
262
    /**
263
     * Get wrapper node name.
264
     *
265
     * @return string|null
266
     */
267
    public function getWrapperNodeName()
268
    {
269
        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...
270
    }
271
272
    /**
273
     * Get parent node name.
274
     *
275
     * @return string|null
276
     */
277
    public function getParentNodeName()
278
    {
279
        return $this->parentNodeName ?? null;
280
    }
281
282
    /**
283
     * Get node name.
284
     *
285
     * @return string
286
     */
287
    public function getNodeName()
288
    {
289
        return $this->nodeName ?? null;
290
    }
291
292
    public function hasSchemaDefinition(Node $node): bool
293
    {
294
        return isset($node->schemaDefinition) && $node->schemaDefinition;
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...
295
    }
296
297
    public function isGlobalDefinition(Node $node): bool
298
    {
299
        return $this->hasSchemaDefinition() && $node->globalSchemaLocation;
0 ignored issues
show
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...
Bug introduced by
The call to hasSchemaDefinition() misses a required argument $node.

This check looks for function calls that miss required arguments.

Loading history...
300
    }
301
}
302