true, 'yes' => true, 'false' => false, 'no' => false, ]; /** * * @var XMLReader */ protected $xmlReader; /** * * @var string */ protected $file; /** * * @var string */ protected $filterNode; /** * * @var null|int */ protected $filterNodeDepth; /** * * @var null|int */ protected $count = null; /** * * @var int */ protected $offset = 0; public function __clone() { $this->xmlReader = new XMLReader(); $this->open($this->file); } /** * * @param string $filterNode * @param null|int $filterNodeDepth * @return void */ public function __construct(string $filterNode, ?int $filterNodeDepth = null) { $this->xmlReader = new \XMLReader(); $this->setNodeFilterCriteria($filterNode, $filterNodeDepth); } public function getFiletypeExtension(): string { return 'xml'; } public function setNodeFilterCriteria(string $node, ?int $depth = null) { $this->filterNode = $node; $this->filterNodeDepth = $depth; } public function open(string $file): bool { $this->file = $file; $this->offset = 0; return $this->xmlReader->open($file); } public function isValid(): bool { $result = $this->getNextNode(); $this->rewind(); return $result; } public function rewind(): void { if ($this->offset > 0) { $this->close(); $this->open($this->file); } } public function count(): int { if (null === $this->count) { $this->rewind(); $this->count = 0; while ($this->getNextNode()) { $this->count++; } $this->rewind(); } return $this->count; } public function setOffset(int $offset): void { if ($this->offset != $offset) { if ($offset < $this->offset) { $this->rewind(); } while ($this->offset < $offset && $this->getNextNode()) { } } } public function getOffset(): int { return $this->offset; } public function read(\Closure $callback): bool { return $this->getNextNode($callback); } public function close(): void { if ($this->xmlReader) { $this->xmlReader->close(); } } public function getNextNode(?\Closure $callback = null): bool { while($this->xmlReader->read()) { if ($this->xmlReader->nodeType == XMLReader::ELEMENT && (null === $this->filterNodeDepth || $this->xmlReader->depth == $this->filterNodeDepth) && (null === $this->filterNode || $this->xmlReader->name == $this->filterNode)) { $this->offset++; if (null === $this->filterNodeDepth) { $this->filterNodeDepth = $this->xmlReader->depth; } if (null !== $callback) { $xml = $this->xmlReader->readOuterXml(); $dom = new DOMDocument('1.0', 'UTF-8'); $dom->preserveWhiteSpace = false; $dom->loadXml($xml); $data = $this->parseNodeToArray($dom->documentElement); $callback($data); } return true; } } return false; } private function parseNodeToArray(\DOMNode $node, ?array &$output = null): array { $value = [ '@attributes' => [], '@value' => null, ]; if (null === $output) { $output = array(); } if ($node->hasAttributes()) { $attributes = array(); /** * @var \DOMNode $attribute */ foreach ($node->attributes as $attribute) { $attributes[$attribute->nodeName] = $this->convertValue($attribute->nodeValue); } $value['@attributes'] = $attributes; } if ($node->hasChildNodes()) { if ($node->childNodes->length == 1 && in_array($node->childNodes->item(0)->nodeType, array(XML_CDATA_SECTION_NODE, XML_TEXT_NODE))) { $value['@value'] = trim($node->textContent); } else { /** * @var \DOMNode */ foreach ($node->childNodes as $child) { $this->parseNodeToArray($child, $value); } } } if (!isset($output[$node->nodeName])) { $output[$node->nodeName] = $value; } else { if (is_string($output[$node->nodeName])) { $output[$node->nodeName] = array(0 => $output[$node->nodeName]); } elseif (!isset($output[$node->nodeName][0])) { $output[$node->nodeName] = array(0 => $output[$node->nodeName]); } $output[$node->nodeName][] = $value; } return $output; } /** * Funkcja pomocnicza konwertująca wartość string na odpowiadające im wartości bool * * @param string $value * @return string|bool */ private function convertValue(string $value) { $value = trim($value); return isset(self::BOOL_VALUES[$value]) ? self::BOOL_VALUES[$value] : $value; } }