file = $file; $this->file->processing(true); $this->record_path = $record_path; if (!empty($record_path) && strpos($record_path, '/') !== false) { $this->record_path_parts = explode('/', $record_path); // remove empty first tag if (empty($this->record_path_parts[0])) { array_shift($this->record_path_parts); } } } public function data() { $this->file->setRecordPath($this->record_path); $record = $this->file->getRecord(); $xml = new \XMLReader(); $xml->xml($record); $result = $this->parseXml2($xml); return $result; } private function generateXpath($nodes, $element = false) { if (count($nodes) < 1) { return ''; } $result = array_slice($nodes, 2); if ($element) { $result[] = $element; } return !empty($result) ? '/' . implode('/', $result) : ''; } protected function parseXml2(\XMLReader $xml, $path = []) { $data = []; $index = -1; while ($xml->read()) { switch ($xml->nodeType) { case XMLReader::END_ELEMENT: return $data; case XMLReader::ELEMENT: $element_path = $path; $element_path[] = $xml->name; $row = [ 'node' => $xml->name, 'type' => 'element', 'xpath' => $this->generateXpath($element_path), ]; $attributes = []; if ($xml->hasAttributes) { while ($xml->moveToNextAttribute()) { $attributes[] = ['name' => $xml->name, 'value' => $xml->value, 'xpath' => $this->generateXpath($element_path, '@' . $xml->name)]; } $xml->moveToElement(); } $row['attr'] = $attributes; if (!$xml->isEmptyElement) { $row['value'] = $this->parseXml2($xml, $element_path); } $data[] = $row; break; case XMLReader::TEXT: case XMLReader::CDATA: $row = [ 'node' => 'text', 'xpath' => $this->generateXpath($path), 'value' => "" . $xml->value, 'type' => 'text' ]; $data[] = $row; break; } } return $data; } /** * Generate a nested list visualising the xml document nodes * and attributes based on the XML data being previewed. * * @return string */ public function output() { $this->file->setRecordPath($this->record_path); $record = $this->file->getRecord(); return $this->generatePreview($record); } /** * Load xml string into XML Previewer * * @param string $record XML Record as string * * @return string */ private function generatePreview($record) { $this->xml = new \XMLReader(); $this->xml->xml($record); // loop through document $output = $this->iterate_element(); return ''; } /** * Loop through XML document element by element. * * @return string */ public function iterate_element() { $output = ''; while ($this->xml->read()) { switch ($this->xml->nodeType) { case XMLReader::ELEMENT: $element_name = $this->xml->name; // remove elements from if depth has decreased if ($this->last_depth > $this->xml->depth) { for ($i = ($this->xml->depth + 1); $i < count($this->processed_index_nodes); $i++) { $this->processed_index_nodes[$i] = array(); } } // increase index of element if found $found = false; if (!empty($this->processed_index_nodes[$this->xml->depth])) { foreach ($this->processed_index_nodes[$this->xml->depth] as &$node) { if (isset($node['n']) && $node['n'] == $element_name) { $node['i'] = ($node['i'] + 1); $found = true; } } } // otherwise reset back to 1 if not found if (!$found) { $this->processed_index_nodes[$this->xml->depth][] = array('n' => $element_name, 'i' => 1); } $output .= $this->open_element($element_name); $isEmpty = $this->xml->isEmptyElement; if ($isEmpty) { $output .= $this->close_element($element_name); } break; case XMLReader::END_ELEMENT: $output .= $this->close_element($this->xml->name); break; case XMLReader::TEXT: case XMLReader::CDATA: $output .= ($this->xml->value); break; } } return $output; } /** * Create string representation of current element and its attributes. * * @param $element_name * * @return string */ public function open_element($element_name) { $output = ''; $this->processed_nodes[] = $this->xml->name; $this->last_element = $this->xml->name; if ($this->xml->depth > $this->last_depth) { $output .= ""; } } array_pop($this->processed_nodes); if (!$this->xml->isEmptyElement) { $output .= "{$prefix}</{$element_name}>"; } else { $output .= "</{$element_name}>"; } return $output; } /** * Generate array of elements xpath from file * * @param boolean $show_attrs * * @return array */ public function generate_xpath($show_attrs = false) { // retrive parsed list of nodes/attributes $nodes = $this->get_nodes($show_attrs); $list = array(); // list all possible xpath foreach ($nodes as $depth_nodes) { // display nodes foreach ($depth_nodes as $node) { $list[] = $node['xpath']; // skip generating attributes if (!$show_attrs) { continue; } // display attributes foreach ($node['attrs'] as $attr) { $list[] = $attr['xpath']; } } } return $list; } /** * Loop through file and retreive array of nodes / attributes * * @param boolean $show_attrs * * @return array */ public function get_nodes($show_attrs = false) { $output = array(); $previous_depth = -1; $previous_element = false; $element_index = 0; $nodes = array(); $xpath_nodes = array(); while ($this->xml->read()) { // if($this->xml->nodeType == XMLReader::TEXT || $this->xml->nodeType == XMLReader::CDATA){ // var_dump($this->xml->value); // var_dump($previous_element); // echo "===
"; // } if ($this->xml->nodeType == XMLReader::ELEMENT) { $d = $this->xml->depth; $e = $this->xml->name; $x = '/'; $p = false; $a = array(); $v = $this->xml->value; // var_dump($this->xml->hasValue); // use to set element index = /posts/post[0], /posts/post[1] if ($previous_element == $e) { $element_index++; } else { $element_index = 0; } // manage depth if ($d < $previous_depth) { $c = ($previous_depth - $d); for ($x = 0; $x <= $c; $x++) { // parent node array_pop($nodes); } } elseif ($d > $previous_depth) { // child node } else { // partner node, same level array_pop($nodes); } $nodes[] = $this->xml->name; // generate xpath $xpath = $x = '/' . implode('/', $nodes); if (!in_array($xpath, $xpath_nodes)) { $xpath_nodes[] = $xpath; } // get element attributes if ($show_attrs && $this->xml->hasAttributes) { while ($this->xml->moveToNextAttribute()) { $a[] = array('attr' => $this->xml->name, 'xpath' => $x . '/@' . $this->xml->name); } } // if nodes array is not empty get parent from it if (count($nodes) > 1) { $p = $nodes[count($nodes) - 2]; } // create array if no array exists for depth if (!isset($output[$d])) { $output[$d] = array(); } // check to see if element doesnt exist in depth array if (!$this->in_element_array($e, $output[$d], $x, $element_index)) { $output[$d][] = array( 'node' => $e, 'parent' => $p, 'index' => $element_index, 'xpath' => $x, 'attrs' => $a, 'depth' => $d, 'value' => $v ); } // set previous depth to current depth $previous_depth = $d; $previous_element = $e; } } return $output; } /** * Check to see if node is in array * * @param string $element node name * @param array $array array to compare against * @param string $xpath xpath string for node * * @return boolean */ public function in_element_array($element, $array = array(), $xpath = '/', $index = 0) { foreach ($array as $node) { if ($node['node'] == $element && $node['xpath'] == $xpath && $node['index'] == $index) { return true; } } return false; } /** * Remove the xml wrapping element and next node from xpath node list * * @param array $node_list * * @return array */ private function strip_record_path($node_list) { $output = array(); if (count($node_list) > 2) { for ($i = 2; $i < count($node_list); $i++) { $output[] = $node_list[$i]; } } return $output; } }