[], 'children' => null, ], ($options ?? []) ); return ( new struct_complex( $name, $options['attributes'], $options['children'] ) ); } /** */ function make_document( array $children ) : struct_document { return ( new struct_document( $children ) ); } /** */ function _get_logic( $node ) { if ($node instanceof struct_text) { return (new implementation_text($node)); } else if ($node instanceof struct_comment) { return (new implementation_comment($node)); } else if ($node instanceof struct_complex) { return (new implementation_complex($node)); } else if ($node instanceof struct_document) { return (new implementation_document($node)); } else { var_dump($node); throw (new \Exception('unhandled struct')); } } /** */ function find( $node, \Closure $predicate, $options = null ) : array { $options = \array_merge( [ 'max_depth' => null, ], ($options ?? []) ); return _get_logic($node)->find(fn($x, $y, $z) => find($x, $y, $z), $predicate, $options['max_depth']); } /** * shortcut for xpath/css like navigation * * @param array $path {list} */ function walk( $node, array $path ) { if (\count($path) <= 0) { return $node; } else { $hits = find( $node, fn($x) => ( ($x instanceof struct_complex) && ($x->name === $path[0]) ), [ 'max_depth' => 1, ] ); if (\count($hits) <= 0) { throw (new \Exception('not found: ' . $path[0])); } else if (\count($hits) >= 2) { throw (new \Exception('ambiguous: ' . $path[0])); } else { return walk( $hits[0], \array_slice($path, 1) ); } } } /** */ function transform( $node, \Closure $function ) { return _get_logic($node)->transform(fn($x, $y) => transform($x, $y), $function); } /** */ function to_raw( $node ) { return _get_logic($node)->to_raw(fn($x) => to_raw($x)); } /** */ function to_string( $node, $options = null ) { $options = \array_merge( [ 'depth' => 0, ], ($options ?? []) ); return _get_logic($node)->to_string(fn($x, $y) => to_string($x, $y), $options['depth']); } /** */ function _convert( \DOMNode $doc ) { if ($doc instanceof \DOMComment) { return make_comment($doc->data); } else if ($doc instanceof \DOMText) { $content = \trim($doc->nodeValue); return ( empty($content) ? null : make_text($content) ); } else if ($doc instanceof \DOMElement) { $attributes = []; foreach ($doc->attributes as $attribute_raw) { $attributes[$attribute_raw->name] = $attribute_raw->value; } $children = []; foreach ($doc->childNodes as $child_raw) { $child = _convert($child_raw); if (\is_null($child)) { // do nothing } else { \array_push($children, $child); } } return make_complex($doc->nodeName, ['attributes' => $attributes, 'children' => $children]); } else if ($doc instanceof \DOMDocument) { $children = []; foreach ($doc->childNodes as $child_raw) { $child = _convert($child_raw); if (\is_null($child)) { // do nothing } else { \array_push($children, $child); } } return make_document($children); } else { var_dump($doc); throw (new \Exception('unhandled node type: ' . \strval($doc))); } } /** * @see https://www.php.net/manual/en/class.domdocument.php * @see https://www.php.net/manual/en/domdocument.loadxml.php * @see https://www.php.net/manual/en/libxml.constants.php */ function parse( string $xml ) { $doc = new \DOMDocument(); $doc->loadXML(\sprintf('<_dummy>%s', $xml), 0); $out = _convert($doc); return ( (\count($out->children[0]->children) === 1) ? $out->children[0]->children[0] : make_document($out->children[0]->children) ); } /** * just a shortcut */ function parse_raw( string $xml ) { $node = parse($xml); return to_raw($node); } ?>