Skip to content

Commit cb2f008

Browse files
committed
Allow use of custom namespace for XML serialization
1 parent de15c5f commit cb2f008

File tree

6 files changed

+264
-1
lines changed

6 files changed

+264
-1
lines changed

src/NamespaceUri.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types = 1);
2+
namespace TheSeer\Tokenizer;
3+
4+
class NamespaceUri {
5+
6+
/** @var string */
7+
private $value;
8+
9+
/**
10+
* @param string $value
11+
*/
12+
public function __construct(string $value) {
13+
$this->ensureValidUri($value);
14+
$this->value = $value;
15+
}
16+
17+
public function asString(): string {
18+
return $this->value;
19+
}
20+
21+
private function ensureValidUri($value) {
22+
if (strpos($value, ':') === false) {
23+
throw new NamespaceUriException(
24+
sprintf("Namespace URI '%s' must contain at least one colon", $value)
25+
);
26+
}
27+
}
28+
}

src/NamespaceUriException.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php declare(strict_types = 1);
2+
namespace TheSeer\Tokenizer;
3+
4+
class NamespaceUriException extends Exception {
5+
6+
}

src/XMLSerializer.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ class XMLSerializer {
1515
*/
1616
private $previousToken;
1717

18+
/**
19+
* @var NamespaceUri
20+
*/
21+
private $xmlns;
22+
23+
/**
24+
* XMLSerializer constructor.
25+
*
26+
* @param NamespaceUri $xmlns
27+
*/
28+
public function __construct(NamespaceUri $xmlns = null) {
29+
if ($xmlns === null) {
30+
$xmlns = new NamespaceUri('https://github.com/theseer/tokenizer');
31+
}
32+
$this->xmlns = $xmlns;
33+
}
34+
1835
/**
1936
* @param TokenCollection $tokens
2037
*
@@ -39,7 +56,7 @@ public function toXML(TokenCollection $tokens): string {
3956
$this->writer->setIndent(true);
4057
$this->writer->startDocument();
4158
$this->writer->startElement('source');
42-
$this->writer->writeAttribute('xmlns', 'https://github.com/theseer/tokenizer');
59+
$this->writer->writeAttribute('xmlns', $this->xmlns->asString());
4360
$this->writer->startElement('line');
4461
$this->writer->writeAttribute('no', '1');
4562

tests/NamespaceUriTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
namespace TheSeer\Tokenizer;
3+
4+
use PHPUnit\Framework\TestCase;
5+
6+
/**
7+
* @covers \TheSeer\Tokenizer\NamespaceUri
8+
*/
9+
class NamespaceUriTest extends TestCase {
10+
11+
public function testCanBeConstructedWithValidNamespace() {
12+
$this->assertInstanceOf(
13+
NamespaceUri::class,
14+
new NamespaceUri('a:b')
15+
);
16+
}
17+
18+
public function testInvalidNamespaceThrowsException() {
19+
$this->expectException(NamespaceUriException::class);
20+
new NamespaceUri('invalid-no-colon');
21+
}
22+
23+
public function testStringRepresentationCanBeRetrieved() {
24+
$this->assertEquals(
25+
'a:b',
26+
(new NamespaceUri('a:b'))->asString()
27+
);
28+
}
29+
}

tests/XMLSerializerTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,11 @@ public function testCanBeSerializedToDomDocument() {
3333
$this->assertEquals('source', $result->documentElement->localName);
3434
}
3535

36+
public function testCanBeSerializedToXmlWithCustomNamespace() {
37+
$expected = file_get_contents(__DIR__ . '/_files/customns.xml');
38+
39+
$serializer = new XMLSerializer(new NamespaceUri('custom:xml:namespace'));
40+
$this->assertEquals($expected, $serializer->toXML($this->tokens));
41+
}
3642

3743
}

tests/_files/customns.xml

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?xml version="1.0"?>
2+
<source xmlns="custom:xml:namespace">
3+
<line no="1">
4+
<token name="T_OPEN_TAG">&lt;?php </token>
5+
<token name="T_DECLARE">declare</token>
6+
<token name="T_OPEN_BRACKET">(</token>
7+
<token name="T_STRING">strict_types</token>
8+
<token name="T_WHITESPACE"> </token>
9+
<token name="T_EQUAL">=</token>
10+
<token name="T_WHITESPACE"> </token>
11+
<token name="T_LNUMBER">1</token>
12+
<token name="T_CLOSE_BRACKET">)</token>
13+
<token name="T_SEMICOLON">;</token>
14+
</line>
15+
<line no="2">
16+
<token name="T_NAMESPACE">namespace</token>
17+
<token name="T_WHITESPACE"> </token>
18+
<token name="T_STRING">foo</token>
19+
<token name="T_SEMICOLON">;</token>
20+
</line>
21+
<line no="3"/>
22+
<line no="4">
23+
<token name="T_CLASS">class</token>
24+
<token name="T_WHITESPACE"> </token>
25+
<token name="T_STRING">bar</token>
26+
<token name="T_WHITESPACE"> </token>
27+
<token name="T_OPEN_CURLY">{</token>
28+
</line>
29+
<line no="5">
30+
<token name="T_WHITESPACE"> </token>
31+
<token name="T_CONST">const</token>
32+
<token name="T_WHITESPACE"> </token>
33+
<token name="T_STRING">x</token>
34+
<token name="T_WHITESPACE"> </token>
35+
<token name="T_EQUAL">=</token>
36+
<token name="T_WHITESPACE"> </token>
37+
<token name="T_CONSTANT_ENCAPSED_STRING">'abc'</token>
38+
<token name="T_SEMICOLON">;</token>
39+
</line>
40+
<line no="6"/>
41+
<line no="7">
42+
<token name="T_WHITESPACE"> </token>
43+
<token name="T_DOC_COMMENT">/** @var int */</token>
44+
</line>
45+
<line no="8">
46+
<token name="T_WHITESPACE"> </token>
47+
<token name="T_PRIVATE">private</token>
48+
<token name="T_WHITESPACE"> </token>
49+
<token name="T_VARIABLE">$y</token>
50+
<token name="T_WHITESPACE"> </token>
51+
<token name="T_EQUAL">=</token>
52+
<token name="T_WHITESPACE"> </token>
53+
<token name="T_LNUMBER">1</token>
54+
<token name="T_SEMICOLON">;</token>
55+
</line>
56+
<line no="9"/>
57+
<line no="10">
58+
<token name="T_WHITESPACE"> </token>
59+
<token name="T_PUBLIC">public</token>
60+
<token name="T_WHITESPACE"> </token>
61+
<token name="T_FUNCTION">function</token>
62+
<token name="T_WHITESPACE"> </token>
63+
<token name="T_STRING">__construct</token>
64+
<token name="T_OPEN_BRACKET">(</token>
65+
<token name="T_CLOSE_BRACKET">)</token>
66+
<token name="T_WHITESPACE"> </token>
67+
<token name="T_OPEN_CURLY">{</token>
68+
</line>
69+
<line no="11">
70+
<token name="T_WHITESPACE"> </token>
71+
<token name="T_COMMENT">// do something</token>
72+
</line>
73+
<line no="12">
74+
<token name="T_WHITESPACE"> </token>
75+
<token name="T_CLOSE_CURLY">}</token>
76+
</line>
77+
<line no="13"/>
78+
<line no="14">
79+
<token name="T_WHITESPACE"> </token>
80+
<token name="T_PUBLIC">public</token>
81+
<token name="T_WHITESPACE"> </token>
82+
<token name="T_FUNCTION">function</token>
83+
<token name="T_WHITESPACE"> </token>
84+
<token name="T_STRING">getY</token>
85+
<token name="T_OPEN_BRACKET">(</token>
86+
<token name="T_CLOSE_BRACKET">)</token>
87+
<token name="T_COLON">:</token>
88+
<token name="T_WHITESPACE"> </token>
89+
<token name="T_STRING">int</token>
90+
<token name="T_WHITESPACE"> </token>
91+
<token name="T_OPEN_CURLY">{</token>
92+
</line>
93+
<line no="15">
94+
<token name="T_WHITESPACE"> </token>
95+
<token name="T_RETURN">return</token>
96+
<token name="T_WHITESPACE"> </token>
97+
<token name="T_VARIABLE">$this</token>
98+
<token name="T_OBJECT_OPERATOR">-&gt;</token>
99+
<token name="T_STRING">y</token>
100+
<token name="T_SEMICOLON">;</token>
101+
</line>
102+
<line no="16">
103+
<token name="T_WHITESPACE"> </token>
104+
<token name="T_CLOSE_CURLY">}</token>
105+
</line>
106+
<line no="17"/>
107+
<line no="18">
108+
<token name="T_WHITESPACE"> </token>
109+
<token name="T_PUBLIC">public</token>
110+
<token name="T_WHITESPACE"> </token>
111+
<token name="T_FUNCTION">function</token>
112+
<token name="T_WHITESPACE"> </token>
113+
<token name="T_STRING">getSomeX</token>
114+
<token name="T_OPEN_BRACKET">(</token>
115+
<token name="T_CLOSE_BRACKET">)</token>
116+
<token name="T_COLON">:</token>
117+
<token name="T_WHITESPACE"> </token>
118+
<token name="T_STRING">string</token>
119+
<token name="T_WHITESPACE"> </token>
120+
<token name="T_OPEN_CURLY">{</token>
121+
</line>
122+
<line no="19">
123+
<token name="T_WHITESPACE"> </token>
124+
<token name="T_RETURN">return</token>
125+
<token name="T_WHITESPACE"> </token>
126+
<token name="T_STRING">self</token>
127+
<token name="T_DOUBLE_COLON">::</token>
128+
<token name="T_STRING">x</token>
129+
<token name="T_SEMICOLON">;</token>
130+
</line>
131+
<line no="20">
132+
<token name="T_WHITESPACE"> </token>
133+
<token name="T_CLOSE_CURLY">}</token>
134+
</line>
135+
<line no="21"/>
136+
<line no="22">
137+
<token name="T_WHITESPACE"> </token>
138+
<token name="T_PUBLIC">public</token>
139+
<token name="T_WHITESPACE"> </token>
140+
<token name="T_FUNCTION">function</token>
141+
<token name="T_WHITESPACE"> </token>
142+
<token name="T_STRING">some</token>
143+
<token name="T_OPEN_BRACKET">(</token>
144+
<token name="T_STRING">bar</token>
145+
<token name="T_WHITESPACE"> </token>
146+
<token name="T_VARIABLE">$b</token>
147+
<token name="T_CLOSE_BRACKET">)</token>
148+
<token name="T_COLON">:</token>
149+
<token name="T_WHITESPACE"> </token>
150+
<token name="T_STRING">string</token>
151+
<token name="T_WHITESPACE"> </token>
152+
<token name="T_OPEN_CURLY">{</token>
153+
</line>
154+
<line no="23">
155+
<token name="T_WHITESPACE"> </token>
156+
<token name="T_RETURN">return</token>
157+
<token name="T_WHITESPACE"> </token>
158+
<token name="T_VARIABLE">$b</token>
159+
<token name="T_OBJECT_OPERATOR">-&gt;</token>
160+
<token name="T_STRING">getSomeX</token>
161+
<token name="T_OPEN_BRACKET">(</token>
162+
<token name="T_CLOSE_BRACKET">)</token>
163+
<token name="T_WHITESPACE"> </token>
164+
<token name="T_DOT">.</token>
165+
<token name="T_WHITESPACE"> </token>
166+
<token name="T_CONSTANT_ENCAPSED_STRING">'-def'</token>
167+
<token name="T_SEMICOLON">;</token>
168+
</line>
169+
<line no="24">
170+
<token name="T_WHITESPACE"> </token>
171+
<token name="T_CLOSE_CURLY">}</token>
172+
</line>
173+
<line no="25">
174+
<token name="T_CLOSE_CURLY">}</token>
175+
</line>
176+
<line no="26"/>
177+
</source>

0 commit comments

Comments
 (0)