11<?php
2+
23declare (strict_types=1 );
34
45namespace Netlogix \XmlProcessor ;
56
7+ use XMLReader ;
8+ use Netlogix \XmlProcessor \NodeProcessor \Context \AbstractElementContext ;
69use Netlogix \XmlProcessor \NodeProcessor \Context \CloseContext ;
710use Netlogix \XmlProcessor \NodeProcessor \Context \NodeProcessorContext ;
811use Netlogix \XmlProcessor \NodeProcessor \Context \OpenContext ;
@@ -13,14 +16,33 @@ class XmlProcessor
1316{
1417 public const
1518 EVENT_OPEN_FILE = 'openFile ' ,
16- EVENT_END_OF_FILE = 'endOfFile ' ;
19+ EVENT_END_OF_FILE = 'endOfFile ' ,
20+ NODE_TYPE_NONE = 'NodeType_ ' . XMLReader::NONE ,
21+ NODE_TYPE_ELEMENT = 'NodeType_ ' . XMLReader::ELEMENT ,
22+ NODE_TYPE_ATTRIBUTE = 'NodeType_ ' . XMLReader::ATTRIBUTE ,
23+ NODE_TYPE_TEXT = 'NodeType_ ' . XMLReader::TEXT ,
24+ NODE_TYPE_CDATA = 'NodeType_ ' . XMLReader::CDATA ,
25+ NODE_TYPE_ENTITY_REF = 'NodeType_ ' . XMLReader::ENTITY_REF ,
26+ NODE_TYPE_ENTITY = 'NodeType_ ' . XMLReader::ENTITY ,
27+ NODE_TYPE_PI = 'NodeType_ ' . XMLReader::PI ,
28+ NODE_TYPE_COMMENT = 'NodeType_ ' . XMLReader::COMMENT ,
29+ NODE_TYPE_DOC = 'NodeType_ ' . XMLReader::DOC ,
30+ NODE_TYPE_DOC_TYPE = 'NodeType_ ' . XMLReader::DOC_TYPE ,
31+ NODE_TYPE_DOC_FRAGMENT = 'NodeType_ ' . XMLReader::DOC_FRAGMENT ,
32+ NODE_TYPE_NOTATION = 'NodeType_ ' . XMLReader::NOTATION ,
33+ NODE_TYPE_WHITESPACE = 'NodeType_ ' . XMLReader::WHITESPACE ,
34+ NODE_TYPE_SIGNIFICANT_WHITESPACE = 'NodeType_ ' . XMLReader::SIGNIFICANT_WHITESPACE ,
35+ NODE_TYPE_END_ELEMENT = 'NodeType_ ' . XMLReader::END_ELEMENT ,
36+ NODE_TYPE_END_ENTITY = 'NodeType_ ' . XMLReader::END_ENTITY ,
37+ NODE_TYPE_XML_DECLARATION = 'NodeType_ ' . XMLReader::XML_DECLARATION ;
38+
1739 private array $ nodePath = [];
1840 private string $ currentValue = '' ;
1941
20- private ?array $ skipNodes = NULL ;
42+ private ?array $ skipNodes = null ;
2143 private array $ eventCache = [];
2244
23- private \ XMLReader $ xml ;
45+ private XMLReader $ xml ;
2446 private XmlProcessorContext $ context ;
2547
2648 /** @var iterable<NodeProcessorInterface> */
@@ -29,6 +51,11 @@ class XmlProcessor
2951 /** @var iterable<bool> */
3052 private iterable $ parserProperties ;
3153
54+ /**
55+ * @var string[]
56+ */
57+ private ?array $ whitelistEvents = null ;
58+
3259 private bool $ skipCurrentNode = false ;
3360 private bool $ selfClosing = false ;
3461
@@ -39,21 +66,16 @@ class XmlProcessor
3966 public function __construct (
4067 iterable $ processors ,
4168 iterable $ parserProperties = [
42- \ XMLReader::VALIDATE => false
69+ XMLReader::VALIDATE => false
4370 ]
44- )
45- {
46- $ this ->xml = new \XMLReader ();
71+ ) {
72+ $ this ->xml = new XMLReader ();
4773 $ this ->processors = $ processors ;
4874 $ this ->parserProperties = $ parserProperties ;
49- $ this ->context = new XmlProcessorContext (
50- $ this ->xml ,
51- $ this ->processors ,
52- fn () => $ this ->skipCurrentNode = true
53- );
75+ $ this ->context = new XmlProcessorContext ($ this ->xml , $ this ->processors , fn () => $ this ->skipCurrentNode = true );
5476 }
5577
56- function setSkipNodes (?array $ skipNodes = NULL ): void
78+ function setSkipNodes (?array $ skipNodes = null ): void
5779 {
5880 $ this ->skipNodes = $ skipNodes ;
5981 }
@@ -63,6 +85,16 @@ function getSkipNodes(): ?array
6385 return $ this ->skipNodes ;
6486 }
6587
88+ function setWhitelistEvents (?array $ whitelistEvents = null ): void
89+ {
90+ $ this ->whitelistEvents = $ whitelistEvents ;
91+ }
92+
93+ function getWhitelistEvents (): ?array
94+ {
95+ return $ this ->whitelistEvents ;
96+ }
97+
6698 function getProcessor (string $ processorName ): ?NodeProcessorInterface
6799 {
68100 return $ this ->context ->getProcessor ($ processorName );
@@ -74,51 +106,54 @@ public function processFile(string $filename): void
74106 foreach ($ this ->parserProperties as $ parserProperty => $ value ) {
75107 $ this ->xml ->setParserProperty ($ parserProperty , $ value );
76108 }
77- $ this ->getProcessorEvents (self ::EVENT_OPEN_FILE );
109+ $ this ->callProcessorEvents (self ::EVENT_OPEN_FILE );
78110 while ($ this ->xml ->read ()) {
79111 switch ($ this ->xml ->nodeType ) {
80- case \ XMLReader::END_ELEMENT :
112+ case XMLReader::END_ELEMENT :
81113 $ this ->eventCloseElement ();
82114 break ;
83- case \ XMLReader::ELEMENT :
115+ case XMLReader::ELEMENT :
84116 $ this ->selfClosing = $ this ->xml ->isEmptyElement ;
85117 $ this ->eventOpenElement ();
86- if ($ skip = $ this ->shouldSkipNode ()) {
118+ $ skip = $ this ->shouldSkipNode ();
119+ if ($ skip ) {
87120 $ this ->xml ->next ();
88121 }
89122 if ($ skip || $ this ->selfClosing ) {
90123 $ this ->eventCloseElement ();
91124 }
92125 break ;
93- case \ XMLReader::TEXT :
126+ case XMLReader::TEXT :
94127 $ this ->eventTextElement ();
95128 break ;
96129 default :
97- $ this ->getProcessorEvents ('NodeType_ ' . $ this ->xml ->nodeType );
130+ $ this ->callProcessorEvents ('NodeType_ ' . $ this ->xml ->nodeType );
98131 break ;
99132 }
100133 }
101- $ this ->getProcessorEvents (self ::EVENT_END_OF_FILE );
134+ $ this ->callProcessorEvents (self ::EVENT_END_OF_FILE );
102135 $ this ->xml ->close ();
103136 }
104137
105138 private function skipNode (): bool
106139 {
107140 $ result = $ this ->xml ->next ();
108141 $ this ->eventCloseElement ();
142+
109143 return $ result ;
110144 }
111145
112146 private function shouldSkipNode (): bool
113147 {
114148 if ($ this ->skipCurrentNode ) {
115149 $ this ->skipCurrentNode = false ;
150+
116151 return true ;
117152 }
118- if ($ this ->skipNodes === NULL ) {
153+ if ($ this ->skipNodes === null ) {
119154 return false ;
120155 }
121- $ nodePath = implode ('/ ' , $ this ->nodePath );
156+ $ nodePath = \ implode ('/ ' , $ this ->nodePath );
122157 foreach ($ this ->skipNodes as $ skipNode ) {
123158 if (self ::checkNodePath ($ nodePath , $ skipNode )) {
124159 return true ;
@@ -131,26 +166,29 @@ private function shouldSkipNode(): bool
131166 private function eventOpenElement (): void
132167 {
133168 $ this ->pushNodePath ();
134- $ this ->getProcessorEvents ( ' NodeType_ ' . \XMLReader:: ELEMENT , OpenContext::class);
169+ $ this ->callProcessorEvents (XmlProcessor:: NODE_TYPE_ELEMENT , OpenContext::class);
135170 }
136171
137172 private function eventTextElement (): void
138173 {
139174 $ this ->currentValue = $ this ->xml ->value ;
140- $ this ->getProcessorEvents ( ' NodeType_ ' . \XMLReader:: TEXT , TextContext::class);
175+ $ this ->callProcessorEvents (XmlProcessor:: NODE_TYPE_TEXT , TextContext::class);
141176 }
142177
143178 private function eventCloseElement (): void
144179 {
145- $ this ->getProcessorEvents ( ' NodeType_ ' . \XMLReader:: END_ELEMENT , CloseContext::class);
180+ $ this ->callProcessorEvents (XmlProcessor:: NODE_TYPE_END_ELEMENT , CloseContext::class);
146181 $ this ->popNodePath ();
147182 }
148183
149- private function getProcessorEvents (string $ event , string $ contextClass = NodeProcessorContext::class): void
184+ private function callProcessorEvents (string $ event , string $ contextClass = NodeProcessorContext::class): void
150185 {
151- $ context = NULL ;
186+ if ($ this ->whitelistEvents !== null && !\in_array ($ event , $ this ->whitelistEvents , true )) {
187+ return ;
188+ }
189+ $ context = null ;
152190 foreach ($ this ->getProcessorForEvent ($ event ) as $ action ) {
153- call_user_func ($ action , $ context = $ context ?? $ this ->createContext ($ contextClass ));
191+ \ call_user_func ($ action , $ context ??= $ this ->createContext ($ contextClass ));
154192 }
155193 unset($ context );
156194 }
@@ -160,15 +198,16 @@ private function getProcessorEvents(string $event, string $contextClass = NodePr
160198 */
161199 private function getProcessorForEvent (string $ event ): iterable
162200 {
163- $ nodePath = implode ('/ ' , $ this ->nodePath );
201+ $ nodePath = \ implode ('/ ' , $ this ->nodePath );
164202
165- if (!is_array ($ this ->eventCache [$ nodePath ][$ event ] ?? false )) {
203+ if (!\ is_array ($ this ->eventCache [$ nodePath ][$ event ] ?? false )) {
166204 $ this ->eventCache [$ nodePath ][$ event ] = [];
167205 foreach ($ this ->processors as $ processor ) {
168206 foreach ($ processor ->getSubscribedEvents ($ nodePath , $ this ->context ) as $ e => $ action ) {
169- if ($ e = == $ event ) {
170- $ this -> eventCache [ $ nodePath ][ $ event ][] = $ action ;
207+ if ($ e ! == $ event ) {
208+ continue ;
171209 }
210+ $ this ->eventCache [$ nodePath ][$ event ][] = $ action ;
172211 }
173212 }
174213 }
@@ -185,6 +224,7 @@ private function getAttributes(): array
185224 while ($ this ->xml ->moveToNextAttribute ()) {
186225 $ attributes [$ this ->xml ->name ] = $ this ->xml ->value ;
187226 }
227+
188228 return $ attributes ;
189229 }
190230
@@ -195,32 +235,28 @@ private function pushNodePath(): void
195235
196236 private function popNodePath (): void
197237 {
198- array_pop ($ this ->nodePath );
238+ \ array_pop ($ this ->nodePath );
199239 }
200240
201241 private function createContext (string $ contextClass ): NodeProcessorContext
202242 {
203243 $ context = new $ contextClass ($ this ->context , $ this ->nodePath );
204- if (method_exists ( $ context, ' setSelfClosing ' ) ) {
244+ if ($ context instanceof AbstractElementContext ) {
205245 $ context ->setSelfClosing ($ this ->selfClosing );
206246 }
207- if (method_exists ( $ context, ' setAttributes ' ) ) {
247+ if ($ context instanceof OpenContext ) {
208248 $ context ->setAttributes ($ this ->getAttributes ());
209- }
210- if (method_exists ($ context , 'setText ' )) {
249+ } elseif ($ context instanceof TextContext) {
211250 $ context ->setText ($ this ->currentValue );
212251 }
252+
213253 return $ context ;
214254 }
215255
216256 static function checkNodePath (string $ nodePath , string $ expected ): bool
217257 {
218- return
219- $ expected === '/ ' . $ nodePath ||
220- $ nodePath === $ expected || (
221- function_exists ('str_end_with ' )
222- ? str_end_with ($ nodePath , $ expected ) :
223- substr_compare ($ nodePath , $ expected , -strlen ($ expected )) === 0
224- );
258+ return $ nodePath === $ expected
259+ || '/ ' . $ nodePath === $ expected
260+ || \str_ends_with ($ nodePath , $ expected );
225261 }
226262}
0 commit comments