PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.7.7
Import WP – CSV & XML Import Export for WordPress v2.7.7
2.15.1 2.15.0 2.14.24 2.14.23 2.7.0 2.7.1 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 2.9.1 All 144 releases
jc-importer / class / Common / Importer / Preview / XMLPreview.php

XMLPreview.php in Import WP – CSV & XML Import Export for WordPress 2.7.7, at class/Common/Importer/Preview/XMLPreview.php

581 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImportWP\Common\Importer\Preview;
4
5 use ImportWP\Common\Importer\File\XMLFile;
6 use ImportWP\Common\Importer\PreviewInterface;
7 use XMLReader;
8
9 class XMLPreview implements PreviewInterface
10 {
11
12 /**
13 * @var XMLFile $file
14 */
15 private $file;
16
17 /**
18 * @var \XMLReader $xml
19 */
20 private $xml;
21
22 /**
23 * @var string
24 */
25 private $record_path;
26
27 /**
28 * @var array
29 */
30 private $record_path_parts = array();
31
32 public $processed_nodes = array();
33 public $processed_index_nodes = array();
34 public $previous_node = '';
35 public $node_index = 0;
36 public $last_element = '';
37 public $last_depth = 0;
38 private $prefix = '';
39 private $xpath = false;
40 private $class;
41
42 /**
43 * XMLPreview constructor.
44 *
45 * @param \ImportWP\Common\Importer\File\XMLFile $file
46 * @param string $record_path
47 * @param array $args
48 */
49 public function __construct(XMLFile $file, $record_path, $args = array())
50 {
51 $this->file = $file;
52 $this->file->processing(true);
53 $this->record_path = $record_path;
54
55 if (!empty($record_path) && strpos($record_path, '/') !== false) {
56 $this->record_path_parts = explode('/', $record_path);
57
58 // remove empty first tag
59 if (empty($this->record_path_parts[0])) {
60 array_shift($this->record_path_parts);
61 }
62 }
63 }
64
65 public function data()
66 {
67 $this->file->setRecordPath($this->record_path);
68 $record = $this->file->getRecord();
69
70 $xml = new \XMLReader();
71 $xml->xml($record);
72
73 $result = $this->parseXml2($xml);
74 return $result;
75 }
76
77 private function generateXpath($nodes, $element = false)
78 {
79 if (count($nodes) < 1) {
80 return '';
81 }
82
83 $result = array_slice($nodes, 2);
84 if ($element) {
85 $result[] = $element;
86 }
87
88 return !empty($result) ? '/' . implode('/', $result) : '';
89 }
90
91 protected function parseXml2(\XMLReader $xml, $path = [])
92 {
93
94 $data = [];
95 $index = -1;
96
97 while ($xml->read()) {
98 switch ($xml->nodeType) {
99
100 case XMLReader::END_ELEMENT:
101
102 return $data;
103
104 case XMLReader::ELEMENT:
105
106 $element_path = $path;
107 $element_path[] = $xml->name;
108
109 $row = [
110 'node' => $xml->name,
111 'type' => 'element',
112 'xpath' => $this->generateXpath($element_path),
113 ];
114
115 $attributes = [];
116 if ($xml->hasAttributes) {
117
118 while ($xml->moveToNextAttribute()) {
119 $attributes[] = ['name' => $xml->name, 'value' => $xml->value, 'xpath' => $this->generateXpath($element_path, '@' . $xml->name)];
120 }
121 $xml->moveToElement();
122 }
123
124 $row['attr'] = $attributes;
125
126 if (!$xml->isEmptyElement) {
127 $row['value'] = $this->parseXml2($xml, $element_path);
128 }
129
130 $data[] = $row;
131 break;
132 case XMLReader::TEXT:
133 case XMLReader::CDATA:
134 $row = [
135 'node' => 'text',
136 'xpath' => $this->generateXpath($path),
137 'value' => "" . $xml->value,
138 'type' => 'text'
139 ];
140 $data[] = $row;
141 break;
142 }
143 }
144
145 return $data;
146 }
147
148 /**
149 * Generate a nested list visualising the xml document nodes
150 * and attributes based on the XML data being previewed.
151 *
152 * @return string
153 */
154 public function output()
155 {
156 $this->file->setRecordPath($this->record_path);
157 $record = $this->file->getRecord();
158
159 return $this->generatePreview($record);
160 }
161
162 /**
163 * Load xml string into XML Previewer
164 *
165 * @param string $record XML Record as string
166 *
167 * @return string
168 */
169 private function generatePreview($record)
170 {
171 $this->xml = new \XMLReader();
172 $this->xml->xml($record);
173
174 // loop through document
175 $output = $this->iterate_element();
176 return '<ul>' . $output . '</ul>';
177 }
178
179 /**
180 * Loop through XML document element by element.
181 *
182 * @return string
183 */
184 public function iterate_element()
185 {
186 $output = '';
187
188 while ($this->xml->read()) {
189
190 switch ($this->xml->nodeType) {
191 case XMLReader::ELEMENT:
192
193 $element_name = $this->xml->name;
194
195 // remove elements from if depth has decreased
196 if ($this->last_depth > $this->xml->depth) {
197
198 for ($i = ($this->xml->depth + 1); $i < count($this->processed_index_nodes); $i++) {
199 $this->processed_index_nodes[$i] = array();
200 }
201 }
202
203 // increase index of element if found
204 $found = false;
205 if (!empty($this->processed_index_nodes[$this->xml->depth])) {
206
207 foreach ($this->processed_index_nodes[$this->xml->depth] as &$node) {
208
209 if (isset($node['n']) && $node['n'] == $element_name) {
210 $node['i'] = ($node['i'] + 1);
211 $found = true;
212 }
213 }
214 }
215
216 // otherwise reset back to 1 if not found
217 if (!$found) {
218 $this->processed_index_nodes[$this->xml->depth][] = array('n' => $element_name, 'i' => 1);
219 }
220
221 $output .= $this->open_element($element_name);
222
223 $isEmpty = $this->xml->isEmptyElement;
224 if ($isEmpty) {
225 $output .= $this->close_element($element_name);
226 }
227
228
229 break;
230 case XMLReader::END_ELEMENT:
231 $output .= $this->close_element($this->xml->name);
232 break;
233 case XMLReader::TEXT:
234 case XMLReader::CDATA:
235
236 $output .= ($this->xml->value);
237 break;
238 }
239 }
240
241 return $output;
242 }
243
244 /**
245 * Create string representation of current element and its attributes.
246 *
247 * @param $element_name
248 *
249 * @return string
250 */
251 public function open_element($element_name)
252 {
253 $output = '';
254
255 $this->processed_nodes[] = $this->xml->name;
256 $this->last_element = $this->xml->name;
257
258 if ($this->xml->depth > $this->last_depth) {
259 $output .= "<ul>";
260 }
261
262 $this->last_depth = $this->xml->depth;
263
264 $attrs = '';
265
266 // generate elements xpath
267 $temp = $this->get_xpath();
268
269 // output elements class
270 $this->class = "class=\"xml-node xml-draggable\"";
271
272 if (count($this->processed_nodes) > 1) {
273 $attrs .= $this->get_attributes();
274 $output .= "<li><span {$this->class}data-xpath=\"" . $temp . "\">&lt;{$element_name}</span>{$attrs}&gt;";
275 } else {
276 $output .= "<li><span>&lt;{$element_name}</span>{$attrs}&gt;";
277 }
278
279
280 return $output;
281 }
282
283 /**
284 * Get xPath string for current attribute.
285 *
286 * @param string $attr
287 *
288 * @return string
289 */
290 public function get_xpath($attr = '')
291 {
292
293 if ($this->xpath && count($this->processed_index_nodes) <= 1) {
294 return str_replace('//', '/', $attr);
295 }
296
297 $temp = $this->gen_xpath($this->processed_nodes);
298
299
300 if ($this->xpath !== false && !empty($this->prefix) && strpos($temp, $this->prefix) === 0) {
301
302 $temp = str_replace($this->prefix, "", $temp);
303 }
304
305 $temp .= $attr;
306
307 return str_replace('//', '/', $temp);
308 }
309
310 /**
311 * Generate xPath string for current nodes.
312 *
313 * @param array $nodes
314 *
315 * @return string
316 */
317 public function gen_xpath($nodes)
318 {
319 // TODO: if base node is set don't return the base node part
320 $temp = array();
321
322 foreach ($nodes as $depth => $node) {
323
324 if ($this->xpath && $depth <= 0) {
325 continue;
326 }
327
328 foreach ($this->processed_index_nodes[$depth] as $p_node) {
329 if ($p_node['n'] == $node) {
330 $temp[] = $node . '[' . $p_node['i'] . ']';
331 }
332 }
333 }
334
335 $temp = $this->strip_record_path($temp);
336
337 return '/' . implode('/', $temp);
338 }
339
340 /**
341 * Create string representation of element attributes
342 *
343 * @return string
344 */
345 public function get_attributes()
346 {
347
348 $output = '';
349
350 // get element attributes
351 if ($this->xml->hasAttributes) {
352 while ($this->xml->moveToNextAttribute()) {
353 $output .= " <span {$this->class} data-xpath=\"" . $this->get_xpath('/@' . $this->xml->name) . "\">{$this->xml->name}=\"{$this->xml->value}\"</span>";
354 }
355
356 $this->xml->moveToElement();
357 }
358
359 return $output;
360 }
361
362 /**
363 * Create string representation of current closing element,
364 * and remove it from the currently opened list.
365 *
366 * @param $element_name
367 *
368 * @return string
369 */
370 public function close_element($element_name)
371 {
372 $output = '';
373 $prefix = '';
374
375 if ($this->last_element != $this->xml->name) {
376 if ($this->xml->depth < $this->last_depth) {
377 $output .= "</ul>";
378 }
379 }
380
381 array_pop($this->processed_nodes);
382
383 if (!$this->xml->isEmptyElement) {
384 $output .= "{$prefix}&lt;/{$element_name}&gt;</li>";
385 } else {
386 $output .= "&lt;/{$element_name}&gt;</li>";
387 }
388
389 return $output;
390 }
391
392 /**
393 * Generate array of elements xpath from file
394 *
395 * @param boolean $show_attrs
396 *
397 * @return array
398 */
399 public function generate_xpath($show_attrs = false)
400 {
401
402 // retrive parsed list of nodes/attributes
403 $nodes = $this->get_nodes($show_attrs);
404 $list = array();
405
406 // list all possible xpath
407 foreach ($nodes as $depth_nodes) {
408
409 // display nodes
410 foreach ($depth_nodes as $node) {
411 $list[] = $node['xpath'];
412
413 // skip generating attributes
414 if (!$show_attrs) {
415 continue;
416 }
417
418 // display attributes
419 foreach ($node['attrs'] as $attr) {
420 $list[] = $attr['xpath'];
421 }
422 }
423 }
424
425 return $list;
426 }
427
428 /**
429 * Loop through file and retreive array of nodes / attributes
430 *
431 * @param boolean $show_attrs
432 *
433 * @return array
434 */
435 public function get_nodes($show_attrs = false)
436 {
437
438 $output = array();
439
440 $previous_depth = -1;
441 $previous_element = false;
442 $element_index = 0;
443 $nodes = array();
444 $xpath_nodes = array();
445
446 while ($this->xml->read()) {
447
448 // if($this->xml->nodeType == XMLReader::TEXT || $this->xml->nodeType == XMLReader::CDATA){
449 // var_dump($this->xml->value);
450 // var_dump($previous_element);
451 // echo "===<br />";
452 // }
453
454 if ($this->xml->nodeType == XMLReader::ELEMENT) {
455
456 $d = $this->xml->depth;
457 $e = $this->xml->name;
458 $x = '/';
459 $p = false;
460 $a = array();
461 $v = $this->xml->value;
462
463 // var_dump($this->xml->hasValue);
464
465 // use to set element index = /posts/post[0], /posts/post[1]
466 if ($previous_element == $e) {
467 $element_index++;
468 } else {
469 $element_index = 0;
470 }
471
472 // manage depth
473 if ($d < $previous_depth) {
474
475 $c = ($previous_depth - $d);
476 for ($x = 0; $x <= $c; $x++) {
477
478 // parent node
479 array_pop($nodes);
480 }
481 } elseif ($d > $previous_depth) {
482
483 // child node
484 } else {
485
486 // partner node, same level
487 array_pop($nodes);
488 }
489
490 $nodes[] = $this->xml->name;
491
492 // generate xpath
493 $xpath = $x = '/' . implode('/', $nodes);
494 if (!in_array($xpath, $xpath_nodes)) {
495 $xpath_nodes[] = $xpath;
496 }
497
498 // get element attributes
499 if ($show_attrs && $this->xml->hasAttributes) {
500 while ($this->xml->moveToNextAttribute()) {
501 $a[] = array('attr' => $this->xml->name, 'xpath' => $x . '/@' . $this->xml->name);
502 }
503 }
504
505 // if nodes array is not empty get parent from it
506 if (count($nodes) > 1) {
507 $p = $nodes[count($nodes) - 2];
508 }
509
510 // create array if no array exists for depth
511 if (!isset($output[$d])) {
512 $output[$d] = array();
513 }
514
515 // check to see if element doesnt exist in depth array
516 if (!$this->in_element_array($e, $output[$d], $x, $element_index)) {
517
518 $output[$d][] = array(
519 'node' => $e,
520 'parent' => $p,
521 'index' => $element_index,
522 'xpath' => $x,
523 'attrs' => $a,
524 'depth' => $d,
525 'value' => $v
526
527 );
528 }
529
530 // set previous depth to current depth
531 $previous_depth = $d;
532 $previous_element = $e;
533 }
534 }
535
536 return $output;
537 }
538
539 /**
540 * Check to see if node is in array
541 *
542 * @param string $element node name
543 * @param array $array array to compare against
544 * @param string $xpath xpath string for node
545 *
546 * @return boolean
547 */
548 public function in_element_array($element, $array = array(), $xpath = '/', $index = 0)
549 {
550
551 foreach ($array as $node) {
552
553 if ($node['node'] == $element && $node['xpath'] == $xpath && $node['index'] == $index) {
554 return true;
555 }
556 }
557
558 return false;
559 }
560
561 /**
562 * Remove the xml wrapping element <record> and next node from xpath node list
563 *
564 * @param array $node_list
565 *
566 * @return array
567 */
568 private function strip_record_path($node_list)
569 {
570
571 $output = array();
572 if (count($node_list) > 2) {
573 for ($i = 2; $i < count($node_list); $i++) {
574 $output[] = $node_list[$i];
575 }
576 }
577
578 return $output;
579 }
580 }
581