Skip to content

Commit 94b62a1

Browse files
authored
add skipNodes to XmlProcessor (#7)
* [bugfix] skipNode * [feature] add selfClosing to OpenContext and CloseContext
1 parent 4f03a1e commit 94b62a1

6 files changed

Lines changed: 133 additions & 9 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Netlogix\XmlProcessor\NodeProcessor\Context;
4+
5+
class AbstractElementContext extends NodeProcessorContext
6+
{
7+
private bool $selfClosing = false;
8+
9+
function setSelfClosing(bool $selfClosing): void
10+
{
11+
$this->selfClosing = $selfClosing;
12+
}
13+
14+
function getSelfClosing(): bool
15+
{
16+
return $this->selfClosing;
17+
}
18+
}

src/NodeProcessor/Context/CloseContext.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33

44
namespace Netlogix\XmlProcessor\NodeProcessor\Context;
55

6-
class CloseContext extends NodeProcessorContext
6+
class CloseContext extends AbstractElementContext
77
{
88
}

src/NodeProcessor/Context/OpenContext.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace Netlogix\XmlProcessor\NodeProcessor\Context;
55

6-
class OpenContext extends NodeProcessorContext
6+
class OpenContext extends AbstractElementContext
77
{
88
/**
99
* @var array<string>

src/XmlProcessor.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class XmlProcessor
2828
/** @var iterable<bool> */
2929
private iterable $parserProperties;
3030

31+
private bool $skipCurrentNode = false;
32+
private bool $selfClosing = false;
33+
3134
/**
3235
* @param iterable<NodeProcessorInterface> $processors
3336
* @param iterable<bool> $parserProperties
@@ -43,7 +46,7 @@ public function __construct(
4346
$this->context = new XmlProcessorContext(
4447
$this->xml,
4548
$this->processors,
46-
fn() => $this->skipNode()
49+
fn() => $this->skipCurrentNode = true
4750
);
4851
}
4952

@@ -75,13 +78,12 @@ public function processFile(string $filename): void
7578
$this->eventCloseElement();
7679
break;
7780
case \XMLReader::ELEMENT:
78-
$selfClosing = $this->xml->isEmptyElement;
81+
$this->selfClosing = $this->xml->isEmptyElement;
7982
$this->eventOpenElement();
80-
if ($this->shouldSkipNode()) {
81-
$this->skipNode();
82-
break;
83+
if ($skip = $this->shouldSkipNode()) {
84+
$this->xml->next();
8385
}
84-
if ($selfClosing) {
86+
if ($skip || $this->selfClosing) {
8587
$this->eventCloseElement();
8688
}
8789
break;
@@ -106,6 +108,10 @@ private function skipNode(): bool
106108

107109
private function shouldSkipNode(): bool
108110
{
111+
if ($this->skipCurrentNode) {
112+
$this->skipCurrentNode = false;
113+
return true;
114+
}
109115
if ($this->skipNodes === NULL) {
110116
return false;
111117
}
@@ -186,6 +192,9 @@ private function popNodePath(): void
186192
private function createContext(string $contextClass): NodeProcessorContext
187193
{
188194
$context = new $contextClass($this->context, $this->nodePath);
195+
if (method_exists($context, 'setSelfClosing')) {
196+
$context->setSelfClosing($this->selfClosing);
197+
}
189198
if (method_exists($context, 'setAttributes')) {
190199
$context->setAttributes($this->getAttributes());
191200
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Netlogix\XmlProcessor\Tests\Unit\NodeProcessor\Context;
5+
6+
use Netlogix\XmlProcessor\NodeProcessor\Context\AbstractElementContext;
7+
use Netlogix\XmlProcessor\XmlProcessorContext;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class AbstractElementContextTest extends TestCase
11+
{
12+
private function getCloseContext(
13+
?XmlProcessorContext $context = NULL,
14+
array $nodePath = ['foo', 'bar']
15+
): AbstractElementContext
16+
{
17+
return new AbstractElementContext(
18+
$context ?? $this->getMockBuilder(XmlProcessorContext::class)
19+
->disableOriginalConstructor()
20+
->getMock(),
21+
$nodePath
22+
);
23+
}
24+
25+
public function test__construct(): void
26+
{
27+
$nodeProcessorContext = $this->getCloseContext();
28+
self::assertInstanceOf(AbstractElementContext::class, $nodeProcessorContext);
29+
}
30+
31+
/**
32+
* @dataProvider setSelfClosingDataProvider
33+
*/
34+
function testSetSelfClosing($set, $expect): void
35+
{
36+
$nodeProcessorContext = $this->getCloseContext();
37+
$nodeProcessorContext->setSelfClosing($set);
38+
self::assertEquals($expect, $nodeProcessorContext->getSelfClosing());
39+
}
40+
41+
function setSelfClosingDataProvider(): \Generator
42+
{
43+
yield [true, true];
44+
yield [false, false];
45+
}
46+
47+
/**
48+
* @dataProvider getSelfClosingDataProvider
49+
*/
50+
function testGetSelfClosing($set, $expect): void
51+
{
52+
$nodeProcessorContext = $this->getCloseContext();
53+
if ($set !== NULL) {
54+
$nodeProcessorContext->setSelfClosing($set);
55+
}
56+
self::assertEquals($expect, $nodeProcessorContext->getSelfClosing());
57+
}
58+
59+
function getSelfClosingDataProvider(): \Generator
60+
{
61+
yield [NULL, false];
62+
yield [true, true];
63+
yield [false, false];
64+
}
65+
66+
}

tests/Unit/XmlProcessorTest.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,36 @@ public function testProcessFile()
7373
[\XMLReader::SUBST_ENTITIES => true]
7474
);
7575
$xmlProcessor->processFile(__DIR__ . '/../Fixtures/XmlProcessorTest/test.xml');
76+
}
77+
78+
public function testProcessFile_skipCurrentNode()
79+
{
80+
$nodeProcessor = $this->getMockForAbstractClass(NodeProcessorInterface::class);
81+
82+
$nodeProcessor->method('getSubscribedEvents')
83+
->will(
84+
$this->returnCallback(fn() => yield from [
85+
'NodeType_' . \XMLReader::ELEMENT => function (OpenContext $context) {
86+
$context->getXmlProcessorContext()->skipCurrentNode();
87+
self::assertNotEquals('bar', $context->getCurrentNodeName());
88+
},
89+
])
90+
);
91+
92+
$xmlProcessor = new XmlProcessor([
93+
$nodeProcessor
94+
]);
95+
96+
$xmlProcessor->processFile(__DIR__ . '/../Fixtures/XmlProcessorTest/test.xml');
97+
98+
$xmlProcessor->setSkipNodes(['foo']);
99+
$xmlProcessor->processFile(__DIR__ . '/../Fixtures/XmlProcessorTest/test.xml');
100+
101+
$xmlProcessor = new XmlProcessor(
102+
[$nodeProcessor],
103+
[\XMLReader::SUBST_ENTITIES => true]
104+
);
105+
$xmlProcessor->processFile(__DIR__ . '/../Fixtures/XmlProcessorTest/test.xml');
76106

77107
if(!function_exists('str_end_with')){
78108
function str_end_with(string $nodePath, string $expected){
@@ -90,8 +120,9 @@ function testCheckNodePath(string $nodePath, string $expected, bool $result): vo
90120
self::assertSame(XmlProcessor::checkNodePath($nodePath, $expected), $result);
91121
}
92122

93-
public static function checkNodePathDataProvider(): iterable
123+
public static function checkNodePathDataProvider(): \Generator
94124
{
125+
yield ['', 'foo/bar', false];
95126
yield ['/foo/bar', 'foo/bar', true];
96127
yield ['/foo', 'foo/bar', false];
97128
yield ['foo', 'foo/bar', false];

0 commit comments

Comments
 (0)