XMLIterator::next()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 8
cts 8
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 3
nop 0
crap 4
1
<?php
2
3
namespace Thruster\Component\XMLIterator;
4
5
use Iterator as IteratorInterface;
6
7
/**
8
 * Class XMLIterator
9
 *
10
 * @package Thruster\Component\XMLIterator
11
 * @author  Aurimas Niekis <[email protected]>
12
 */
13
class XMLIterator implements IteratorInterface
14
{
15
    /**
16
     * @var XMLReader
17
     */
18
    protected $reader;
19
20
    /**
21
     * @var int
22
     */
23
    private $index;
24
25
    /**
26
     * @var bool
27
     */
28
    private $lastRead;
29
30
    /**
31
     * @var array
32
     */
33
    private $elementStack;
34
35 16
    public function __construct(XMLReader $reader)
36
    {
37 16
        $this->reader = $reader;
38 16
    }
39
40
    /**
41
     * @return XMLReader
42
     */
43
    public function getReader() : XMLReader
44
    {
45
        return $this->reader;
46
    }
47
48
    /**
49
     * @param string $name
50
     *
51
     * @return bool|Node
52
     */
53 8
    public function moveToNextElementByName($name = null)
54
    {
55 8
        while (self::moveToNextElement()) {
56 6
            if (!$name || $name === $this->reader->name) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $name of type string|null is loosely compared to false; 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...
57 6
                break;
58
            }
59
60 2
            self::next();
61
        };
62
63 8
        return self::valid() ? self::current() : false;
64
    }
65
66 8
    public function moveToNextElement()
67
    {
68 8
        return $this->moveToNextByNodeType(XMLReader::ELEMENT);
69
    }
70
71
    /**
72
     * @param int $nodeType
73
     *
74
     * @return bool|Node
75
     */
76 8
    public function moveToNextByNodeType($nodeType)
77
    {
78 8
        if (null === self::valid()) {
79 1
            self::rewind();
80 8
        } elseif (self::valid()) {
81 8
            self::next();
82
        }
83
84 8
        while (self::valid()) {
85 8
            if ($this->reader->nodeType === $nodeType) {
86 6
                break;
87
            }
88
89 7
            self::next();
90
        }
91
92 8
        return self::valid() ? self::current() : false;
93
    }
94
95
    /**
96
     * @inheritdoc
97
     */
98 16
    public function rewind()
99
    {
100
        // this iterator can not really rewind
101 16
        if ($this->reader->nodeType === XMLREADER::NONE) {
102 16
            self::next();
103 2
        } elseif ($this->lastRead === null) {
104
            $this->lastRead = true;
105
        }
106
107 16
        $this->index = 0;
108 16
    }
109
110
    /**
111
     * @inheritdoc
112
     */
113 16
    public function valid()
114
    {
115 16
        return $this->lastRead;
116
    }
117
118
    /**
119
     * @return Node
120
     */
121 13
    public function current()
122
    {
123 13
        return new Node($this->reader);
124
    }
125
126
    /**
127
     * @inheritdoc
128
     */
129 7
    public function key()
130
    {
131 7
        return $this->index;
132
    }
133
134
    /**
135
     * @inheritdoc
136
     */
137 16
    public function next()
138
    {
139 16
        if ($this->lastRead = $this->reader->read() and $this->reader->nodeType === XMLReader::ELEMENT) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
140 16
            $depth                      = $this->reader->depth;
141 16
            $this->elementStack[$depth] = new Element($this->reader);
142
143 16
            if (count($this->elementStack) !== $depth + 1) {
144 7
                $this->elementStack = array_slice($this->elementStack, 0, $depth + 1);
145
            }
146
        }
147
148 16
        $this->index++;
149 16
    }
150
151
    /**
152
     * @return string
153
     */
154
    public function getNodePath() : string
155
    {
156
        return '/' . implode('/', $this->elementStack);
157
    }
158
159
    /**
160
     * @return string
161
     */
162
    public function getNodeTree() : string
163
    {
164
        $stack  = $this->elementStack;
165
        $buffer = '';
166
167
        /* @var $element Element */
168
        while ($element = array_pop($stack)) {
169
            $buffer = $element->getXMLElementAround($buffer);
170
        }
171
172
        return $buffer;
173
    }
174
}
175