Skip to content

Commit c0b5431

Browse files
committed
XMLReaderIterator v0.1.12. (prepare)
- fixed XMLChildElementIterator with named child elements - fixed missing NULL return on XMLReaderIterator::current() - added XMLChildElementIterator regression test - added XMLChildElementIterator test for getNodePath()
1 parent 4e3e6d3 commit c0b5431

4 files changed

Lines changed: 233 additions & 52 deletions

File tree

build/include/xmlreader-iterators.php

Lines changed: 92 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ public function valid()
448448
#[\ReturnTypeWillChange]
449449
public function current()
450450
{
451-
return new XMLReaderNode($this->reader);
451+
return $this->lastRead ? new XMLReaderNode($this->reader) : null;
452452
}
453453

454454
#[\ReturnTypeWillChange]
@@ -466,11 +466,7 @@ public function next()
466466
$this->skipNextRead = false;
467467
$this->lastRead = $this->reader->nodeType !== XMLReader::NONE;
468468
} elseif ($this->lastRead = $this->reader->read() and $this->reader->nodeType === XMLReader::ELEMENT) {
469-
$depth = $this->reader->depth;
470-
$this->elementStack[$depth] = new XMLReaderElement($this->reader);
471-
if (count($this->elementStack) !== $depth + 1) {
472-
$this->elementStack = array_slice($this->elementStack, 0, $depth + 1);
473-
}
469+
$this->touchElementStack();
474470
}
475471
}
476472

@@ -499,6 +495,22 @@ public function getNodeTree()
499495
return $buffer;
500496
}
501497

498+
/**
499+
* touch the internal element-stack
500+
*
501+
* update the element-stack for the current reader node - which must be
502+
* of type XMLReader::ELEMENT otherwise undefined.
503+
*
504+
* @return void
505+
*/
506+
protected function touchElementStack()
507+
{
508+
$depth = $this->reader->depth;
509+
$this->elementStack[$depth] = new XMLReaderElement($this->reader);
510+
if (count($this->elementStack) !== $depth + 1) {
511+
$this->elementStack = array_slice($this->elementStack, 0, $depth + 1);
512+
}
513+
}
502514
}
503515

504516
/**
@@ -1565,14 +1577,20 @@ class XMLChildElementIterator extends XMLElementIterator
15651577
*/
15661578
private $index;
15671579

1580+
/**
1581+
* @var string|null
1582+
*/
1583+
private $name;
1584+
15681585
/**
15691586
* @inheritdoc
15701587
*
15711588
* @param bool $descendantAxis traverse children of children
15721589
*/
15731590
public function __construct(XMLReader $reader, $name = null, $descendantAxis = false)
15741591
{
1575-
parent::__construct($reader, $name);
1592+
parent::__construct($reader);
1593+
$this->name = $name;
15761594
$this->descendTree = $descendantAxis;
15771595
}
15781596

@@ -1583,54 +1601,63 @@ public function __construct(XMLReader $reader, $name = null, $descendantAxis = f
15831601
public function rewind()
15841602
{
15851603
// this iterator can not really rewind. instead it places itself onto the
1586-
// first children.
1604+
// first child element - if any.
1605+
if ($this->didRewind) {
1606+
return;
1607+
}
15871608

15881609
if ($this->reader->nodeType === XMLReader::NONE) {
1589-
$this->moveToNextElement();
1610+
!$this->moveToNextByNodeType(XMLReader::ELEMENT);
15901611
}
15911612

15921613
if ($this->stopDepth === null) {
15931614
$this->stopDepth = $this->reader->depth;
15941615
}
15951616

1596-
// move to first child - if any
1597-
parent::next();
1598-
parent::rewind();
1617+
// move to first child element - if any
1618+
$result = $this->nextChildElementByName($this->name);
15991619

1600-
$this->index = 0;
1620+
$this->index = $result ? 0 : null;
16011621
$this->didRewind = true;
16021622
}
16031623

16041624
public function next()
16051625
{
1606-
if ($this->valid()) {
1607-
$this->index++;
1626+
if (!$this->valid()) {
1627+
return;
16081628
}
16091629

1610-
while ($this->valid()) {
1611-
parent::next();
1612-
if ($this->descendTree || $this->reader->depth === $this->stopDepth + 1) {
1613-
break;
1614-
}
1615-
};
1630+
$this->index++;
1631+
$this->nextChildElementByName($this->name);
16161632
}
16171633

16181634
public function valid()
16191635
{
1620-
if (!($valid = parent::valid())) {
1621-
return $valid;
1636+
if (!$this->didRewind) {
1637+
return null; // FIXME: this needs to be false, iternal interfaces in PHP now adhere to this since 8.x (8.1 specifically ?)
1638+
}
1639+
1640+
$depth = $this->reader->depth;
1641+
if ($depth <= $this->stopDepth) {
1642+
return false;
1643+
}
1644+
if (!$this->descendTree && $depth !== $this->stopDepth + 1) {
1645+
return false;
1646+
}
1647+
if ($this->name === null || $this->reader->name === $this->name) {
1648+
return $this->reader->nodeType === XMLReader::ELEMENT; // always true here if reader in sync with $this
16221649
}
16231650

1624-
return $this->reader->depth > $this->stopDepth;
1651+
return false;
16251652
}
16261653

16271654
/**
16281655
* @return XMLReaderNode|null
16291656
*/
16301657
public function current()
16311658
{
1632-
$this->didRewind || self::rewind();
1633-
return parent::current();
1659+
$this->didRewind || $this->rewind();
1660+
return $this->valid() ? parent::current() : null;
16341661
}
16351662

16361663
/**
@@ -1640,6 +1667,45 @@ public function key()
16401667
{
16411668
return $this->index;
16421669
}
1670+
1671+
/**
1672+
* move to next child element by name
1673+
*
1674+
* @param string|null $name
1675+
* @return bool
1676+
*/
1677+
private function nextChildElementByName($name = null)
1678+
{
1679+
while ($next = $this->nextElement()) {
1680+
$depth = $this->reader->depth;
1681+
if ($depth <= $this->stopDepth) {
1682+
return false;
1683+
}
1684+
if (!$this->descendTree && $depth !== $this->stopDepth + 1) {
1685+
continue;
1686+
}
1687+
if ($name === null || $this->reader->name === $name) {
1688+
break;
1689+
}
1690+
}
1691+
1692+
return (bool)$next;
1693+
}
1694+
1695+
/**
1696+
* @return bool
1697+
*/
1698+
private function nextElement()
1699+
{
1700+
while ($this->reader->read()) {
1701+
if (XMLReader::ELEMENT !== $this->reader->nodeType) {
1702+
continue;
1703+
}
1704+
$this->touchElementStack();
1705+
return true;
1706+
}
1707+
return false;
1708+
}
16431709
}
16441710

16451711
/**

src/XMLChildElementIterator.php

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,20 @@ class XMLChildElementIterator extends XMLElementIterator
4848
*/
4949
private $index;
5050

51+
/**
52+
* @var string|null
53+
*/
54+
private $name;
55+
5156
/**
5257
* @inheritdoc
5358
*
5459
* @param bool $descendantAxis traverse children of children
5560
*/
5661
public function __construct(XMLReader $reader, $name = null, $descendantAxis = false)
5762
{
58-
parent::__construct($reader, $name);
63+
parent::__construct($reader);
64+
$this->name = $name;
5965
$this->descendTree = $descendantAxis;
6066
}
6167

@@ -66,54 +72,63 @@ public function __construct(XMLReader $reader, $name = null, $descendantAxis = f
6672
public function rewind()
6773
{
6874
// this iterator can not really rewind. instead it places itself onto the
69-
// first children.
75+
// first child element - if any.
76+
if ($this->didRewind) {
77+
return;
78+
}
7079

7180
if ($this->reader->nodeType === XMLReader::NONE) {
72-
$this->moveToNextElement();
81+
!$this->moveToNextByNodeType(XMLReader::ELEMENT);
7382
}
7483

7584
if ($this->stopDepth === null) {
7685
$this->stopDepth = $this->reader->depth;
7786
}
7887

79-
// move to first child - if any
80-
parent::next();
81-
parent::rewind();
88+
// move to first child element - if any
89+
$result = $this->nextChildElementByName($this->name);
8290

83-
$this->index = 0;
91+
$this->index = $result ? 0 : null;
8492
$this->didRewind = true;
8593
}
8694

8795
public function next()
8896
{
89-
if ($this->valid()) {
90-
$this->index++;
97+
if (!$this->valid()) {
98+
return;
9199
}
92100

93-
while ($this->valid()) {
94-
parent::next();
95-
if ($this->descendTree || $this->reader->depth === $this->stopDepth + 1) {
96-
break;
97-
}
98-
};
101+
$this->index++;
102+
$this->nextChildElementByName($this->name);
99103
}
100104

101105
public function valid()
102106
{
103-
if (!($valid = parent::valid())) {
104-
return $valid;
107+
if (!$this->didRewind) {
108+
return null; // FIXME: this needs to be false, iternal interfaces in PHP now adhere to this since 8.x (8.1 specifically ?)
105109
}
106110

107-
return $this->reader->depth > $this->stopDepth;
111+
$depth = $this->reader->depth;
112+
if ($depth <= $this->stopDepth) {
113+
return false;
114+
}
115+
if (!$this->descendTree && $depth !== $this->stopDepth + 1) {
116+
return false;
117+
}
118+
if ($this->name === null || $this->reader->name === $this->name) {
119+
return $this->reader->nodeType === XMLReader::ELEMENT; // always true here if reader in sync with $this
120+
}
121+
122+
return false;
108123
}
109124

110125
/**
111126
* @return XMLReaderNode|null
112127
*/
113128
public function current()
114129
{
115-
$this->didRewind || self::rewind();
116-
return parent::current();
130+
$this->didRewind || $this->rewind();
131+
return $this->valid() ? parent::current() : null;
117132
}
118133

119134
/**
@@ -123,4 +138,43 @@ public function key()
123138
{
124139
return $this->index;
125140
}
141+
142+
/**
143+
* move to next child element by name
144+
*
145+
* @param string|null $name
146+
* @return bool
147+
*/
148+
private function nextChildElementByName($name = null)
149+
{
150+
while ($next = $this->nextElement()) {
151+
$depth = $this->reader->depth;
152+
if ($depth <= $this->stopDepth) {
153+
return false;
154+
}
155+
if (!$this->descendTree && $depth !== $this->stopDepth + 1) {
156+
continue;
157+
}
158+
if ($name === null || $this->reader->name === $name) {
159+
break;
160+
}
161+
}
162+
163+
return (bool)$next;
164+
}
165+
166+
/**
167+
* @return bool
168+
*/
169+
private function nextElement()
170+
{
171+
while ($this->reader->read()) {
172+
if (XMLReader::ELEMENT !== $this->reader->nodeType) {
173+
continue;
174+
}
175+
$this->touchElementStack();
176+
return true;
177+
}
178+
return false;
179+
}
126180
}

src/XMLReaderIterator.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public function valid()
150150
#[\ReturnTypeWillChange]
151151
public function current()
152152
{
153-
return new XMLReaderNode($this->reader);
153+
return $this->lastRead ? new XMLReaderNode($this->reader) : null;
154154
}
155155

156156
#[\ReturnTypeWillChange]
@@ -168,11 +168,7 @@ public function next()
168168
$this->skipNextRead = false;
169169
$this->lastRead = $this->reader->nodeType !== XMLReader::NONE;
170170
} elseif ($this->lastRead = $this->reader->read() and $this->reader->nodeType === XMLReader::ELEMENT) {
171-
$depth = $this->reader->depth;
172-
$this->elementStack[$depth] = new XMLReaderElement($this->reader);
173-
if (count($this->elementStack) !== $depth + 1) {
174-
$this->elementStack = array_slice($this->elementStack, 0, $depth + 1);
175-
}
171+
$this->touchElementStack();
176172
}
177173
}
178174

@@ -201,4 +197,20 @@ public function getNodeTree()
201197
return $buffer;
202198
}
203199

200+
/**
201+
* touch the internal element-stack
202+
*
203+
* update the element-stack for the current reader node - which must be
204+
* of type XMLReader::ELEMENT otherwise undefined.
205+
*
206+
* @return void
207+
*/
208+
protected function touchElementStack()
209+
{
210+
$depth = $this->reader->depth;
211+
$this->elementStack[$depth] = new XMLReaderElement($this->reader);
212+
if (count($this->elementStack) !== $depth + 1) {
213+
$this->elementStack = array_slice($this->elementStack, 0, $depth + 1);
214+
}
215+
}
204216
}

0 commit comments

Comments
 (0)