diff --git a/docs/dom.md b/docs/dom.md index 87426834..e9bf6c20 100644 --- a/docs/dom.md +++ b/docs/dom.md @@ -1,7 +1,7 @@ # DOM Component The DOM Components operate on XML documents through the DOM API. -Instead of solely wrapping a DOMDocument with our own class, +Instead of solely wrapping a XMLDocument with our own class, we embrace the fact that the DOM implementation is leaky. This package provides a set of composable tools that allow you to safely work with the DOM extension. @@ -10,7 +10,7 @@ Since not all code is in one big master class, you will find that it is not too ## Examples ```php -use DOMDocument; +use \Dom\XMLDocument; use Psl\Type; use VeeWee\XML\DOM\Configurator; use VeeWee\XML\DOM\Document; @@ -19,9 +19,8 @@ use VeeWee\XML\DOM\Xpath; use function VeeWee\XML\DOM\Loader\xml_file_loader; $doc = Document::configure( - Configurator\utf8(), + Configurator\loader(xml_file_loader('data.xml', LIBXML_NOBLANKS, 'UTF-8')) $debug ? Configurator\pretty_print() : Configurator\trim_spaces(), - Configurator\Loader(xml_file_loader('data.xml')), Configurator\validator( Validator\internal_xsd_validator() ), @@ -42,7 +41,7 @@ $count = $xpath->evaluate('count(.//item)', Type\int(), $currentNode); Of course, the example above only gives you a small idea of all the implemented features. Let's find out more by segregating the DOM component into its composable blocks: -* [Assertions](#assertions): Assert if a DOMNode is of a specific type. +* [Assertions](#assertions): Assert if a Node is of a specific type. * [Builders](#builders): Let you build XML by using a declarative API. * [Collection](#collection): A wrapper for dealing with lists of nodes. * [Configurators](#configurators): Specify how you want to configure your DOM document. @@ -50,7 +49,7 @@ Let's find out more by segregating the DOM component into its composable blocks: * [Locators](#locators): Enables you to locate specific XML elements. * [Manipulators](#manipulators): Allows you to manipulate any DOM document. * [Mappers](#mappers): Converts the DOM document to something else. -* [Predicates](#predicates): Check if a DOMNode is of a specific type. +* [Predicates](#predicates): Check if a Node is of a specific type. * [Traverser](#traverser): Traverse over a complete DOM tree and perform visitor-based manipulations. * [Validators](#validators): Validate the content of your XML document. * [XPath](#xpath): Query for specific elements based on XPath queries. @@ -58,11 +57,11 @@ Let's find out more by segregating the DOM component into its composable blocks: ## Assertions -Assert if a DOMNode is of a specific type. +Assert if a Node is of a specific type. #### assert_attribute -Assert if a node is of type `DOMAttr`. +Assert if a node is of type `Dom\Attr`. ```php use Psl\Type\Exception\AssertException; @@ -77,7 +76,7 @@ try { #### assert_cdata -Assert if a node is of type `DOMCdataSection`. +Assert if a node is of type `Dom\CDATASection`. ```php use Psl\Type\Exception\AssertException; @@ -92,7 +91,7 @@ try { #### assert_document -Assert if a node is of type `DOMDocument`. +Assert if a node is of type `Dom\XMLDocument`. ```php use Psl\Type\Exception\AssertException; @@ -107,7 +106,7 @@ try { #### assert_dome_node_list -Assert if a variable is of type `DOMNodeList`. +Assert if a variable is of type `Dom\NodeList`. ```php use Psl\Type\Exception\AssertException; @@ -122,7 +121,7 @@ try { #### assert_element -Assert if a node is of type `DOMElement`. +Assert if a node is of type `Dom\Element`. ```php use Psl\Type\Exception\AssertException; @@ -185,7 +184,7 @@ $doc->manipulate( #### attribute -Operates on a `DOMElement` and adds the attribute with specified key and value +Operates on a `Dom\Element` and adds the attribute with specified key and value ```php use function VeeWee\Xml\Dom\Builder\attribute; @@ -202,7 +201,7 @@ element('foo', #### attributes -Operates on a `DOMElement` and adds multiple attributes with specified key and value +Operates on a `Dom\Element` and adds multiple attributes with specified key and value ```php use function VeeWee\Xml\Dom\Builder\attribute; @@ -222,7 +221,7 @@ element('foo', #### cdata -Operates on a `DOMNode` and creates a `DOMCdataSection`. +Operates on a `Dom\Node` and creates a `Dom\CDATASection`. It can contain a set of configurators that can be used to dynamically change the cdata's contents. ```php @@ -242,7 +241,7 @@ element('hello', children( #### children -Operates on a `DOMNode` and attaches multiple child nodes. +Operates on a `Dom\Node` and attaches multiple child nodes. ```php use function VeeWee\Xml\Dom\Builder\element; @@ -265,7 +264,7 @@ element('hello', #### element -Operates on a `DOMNode` and creates a new element. +Operates on a `Dom\Node` and creates a new element. It can contain a set of configurators that can be used to specify the attributes, children, value, ... of the element. ```php @@ -280,7 +279,7 @@ element('hello', ...$configurators); #### escaped_value -Operates on a `DOMElement` and sets the node value. +Operates on a `Dom\Element` and sets the node value. All XML entities `<>"'` will be escaped. ```php @@ -297,7 +296,7 @@ element('hello', escaped_value('<"\'>')); #### namespaced_attribute -Operates on a `DOMElement` and adds a namespaced attribute with specified key and value +Operates on a `Dom\Element` and adds a namespaced attribute with specified key and value ```php use function VeeWee\Xml\Dom\Builder\element; @@ -315,7 +314,7 @@ element('foo', #### namespaced_attributes -Operates on a `DOMElement` and adds a namespaced attribute with specified key and value +Operates on a `Dom\Element` and adds a namespaced attribute with specified key and value ```php use function VeeWee\Xml\Dom\Builder\element; @@ -336,7 +335,7 @@ element('foo', #### namespaced_element -Operates on a `DOMNode` and creates a new namespaced element. +Operates on a `Dom\Node` and creates a new namespaced element. It can contain a set of configurators that can be used to specify the attributes, children, value, ... of the element. ```php @@ -351,8 +350,8 @@ namespaced_element('http://acme.com', 'hello', ...$configurators); #### nodes -Operates on a `DOMDocument` and is the builder that is being called by the `Document::manipulate` method. -It can return one or more `DOMNode` objects +Operates on a `Dom\XMLDocument` and is the builder that is being called by the `Document::manipulate` method. +It can return one or more `Dom\Node` objects ```php use function VeeWee\Xml\Dom\Builder\element; @@ -360,7 +359,7 @@ use function VeeWee\Xml\Dom\Builder\nodes; nodes( element('item'), - static fn (DOMDocument $document): array => [ + static fn (XMLDocument $document): array => [ element('item')($document), element('item')($document), ], @@ -371,7 +370,7 @@ nodes( #### value -Operates on a `DOMElement` and sets the node value. +Operates on a `Dom\Element` and sets the node value. ```php use function VeeWee\Xml\Dom\Builder\element; @@ -382,7 +381,7 @@ element('hello', value('world')); #### xmlns_attribute -Operates on a `DOMElement` and adds a xmlns namespace attribute. +Operates on a `Dom\Element` and adds a xmlns namespace attribute. ```php use function VeeWee\Xml\Dom\Builder\element; @@ -393,7 +392,7 @@ element('hello', xmlns_attribute('ns', 'http://ns.com')); #### xmlns_attributes -Operates on a `DOMElement` and adds multiple xmlns namespace attributes. +Operates on a `Dom\Element` and adds multiple xmlns namespace attributes. ```php use function VeeWee\Xml\Dom\Builder\element; @@ -407,24 +406,24 @@ element('hello', xmlns_attributes(['ns' => 'http://ns.com'])); ``` ## Collection -This package provides a type-safe replacement for `DOMNodeList` with few more options. +This package provides a type-safe replacement for `Dom\NodeList` with few more options. Some examples: ```php -use DOMElement; +use Dom\Element; use Psl\Type; use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Node\value; -$totalPrice = NodeList::fromDOMNodeList($list) - ->expectAllOfType(DOMElement::class) - ->filter(fn(DOMElement $element) => $element->nodeName === 'item') +$totalPrice = NodeList::fromNodeList($list) + ->expectAllOfType(Element::class) + ->filter(fn(Element $element) => $element->nodeName === 'item') ->eq(0) ->siblings() ->children() ->query('./price') ->reduce( - static fn (int $total, DOMElement $price): int + static fn (int $total, Element $price): int => $total + value($price, Type\int()), 0 ); @@ -475,7 +474,7 @@ Document::fromXmlFile( #### document_uri Allows you to keep track of the document uri, even if you are using an in-memory string. -Internally, it sets `DOMDocument::$documentURI`, which gets used as `file` in the [error-handling issues component](./error-handling.md#issues). +Internally, it sets `Dom\XMLDocument::$documentURI`, which gets used as `file` in the [error-handling issues component](./error-handling.md#issues). ```php use VeeWee\Xml\Dom\Document; @@ -504,7 +503,7 @@ Document::configure( #### normalize -This configurator normalizes an XML file to return the DOMDocument back in a "normal" form. +This configurator normalizes an XML file to return the XMLDocument back in a "normal" form. ```php use VeeWee\Xml\Dom\Document; @@ -537,8 +536,6 @@ Document::fromUnsafeDocument( Makes the output of the DOM document human-readable. -⚠️ This configurator needs to be called before loading the XML! - ```php use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Configurator\pretty_print; @@ -546,8 +543,8 @@ use function VeeWee\Xml\Dom\Configurator\loader; use function VeeWee\Xml\Dom\Loader\xml_file_loader; $doc = Document::configure( - pretty_print(), loader(xml_file_loader('data.xml')) + pretty_print(), ); ``` @@ -573,8 +570,6 @@ $doc = Document::fromXmlFile( Trims all whitespaces from the DOM document in order to make it as small as possible in bytesize. -⚠️ This configurator needs to be called before loading the XML! - ```php use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Configurator\trim_spaces; @@ -582,8 +577,8 @@ use function VeeWee\Xml\Dom\Configurator\loader; use function VeeWee\Xml\Dom\Loader\xml_file_loader; $doc = Document::configure( + loader(xml_file_loader('data.xml')), trim_spaces(), - loader(xml_file_loader('data.xml')) ); ``` @@ -591,8 +586,6 @@ $doc = Document::configure( Marks the DOM document as UTF-8. -⚠️ This configurator needs to be called before loading the XML! - ```php use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Configurator\utf8; @@ -600,8 +593,8 @@ use function VeeWee\Xml\Dom\Configurator\loader; use function VeeWee\Xml\Dom\Loader\xml_file_loader; $doc = Document::configure( + loader(xml_file_loader('data.xml', override_encoding: 'UTF-8')), utf8(), - loader(xml_file_loader('data.xml')) ); ``` @@ -625,16 +618,16 @@ $doc = Document::fromXmlFile( #### Writing your own configurator -A configurator can be any `callable` that takes a `DOMDocument` and configures it: +A configurator can be any `callable` that takes a `Dom\XMLDocument` and configures it: ```php namespace VeeWee\Xml\DOM\Configurator; -use DOMDocument; +use \Dom\XMLDocument; interface Configurator { - public function __invoke(DOMDocument $document): DOMDocument; + public function __invoke(XMLDocument $document): XMLDocument; } ``` @@ -642,8 +635,9 @@ You can apply the configurator as followed: ```php use VeeWee\Xml\Dom\Document; +use VeeWee\Xml\Dom\Configurator; -$document = Document::configure($loader, ...$configurators); +$document = Document::configure(Configurator\loader($loader), ...$configurators); ``` ## Loaders @@ -666,7 +660,7 @@ $doc = Document::configure(loader(xml_file_loader($file, LIBXML_NOCDATA))); #### xml_node_loader -Loads an XML document from an external `DOMNode`. +Loads an XML document from an external `Dom\Node`. ```php use VeeWee\Xml\Dom\Document; @@ -695,11 +689,11 @@ $doc = Document::configure(loader(xml_string_loader($xml, LIBXML_NOCDATA))); ```php namespace VeeWee\Xml\Dom\Loader; -use DOMDocument; +use \Dom\XMLDocument; interface Loader { - public function __invoke(DOMDocument $document): void; + public function __invoke(): XMLDocument; } ``` @@ -708,7 +702,7 @@ You can apply the loader as followed: ```php use VeeWee\Xml\Dom\Document; -$document = Document::configure($loader, ...$configurators); +$document = Document::fromLoader($loader, ...$configurators); ``` ## Locators @@ -718,35 +712,35 @@ The locators are split up based on what they are locating. ### Attribute -The attributes locators will return attributes and can be called on a `DOMNode`. +The attributes locators will return attributes and can be called on a `Dom\Node`. #### attributes_list -This function will look for all attributes on a `DOMNode`. +This function will look for all attributes on a `Dom\Node`. For nodes that don't support attributes, you will receive an empty `NodeList`. -The result of this function will be of type `NodeList`. +The result of this function will be of type `NodeList<\Dom\Attr>`. ```php -use DOMAttr; +use Dom\Attr; use function VeeWee\Xml\Dom\Locator\Attribute\attributes_list; $attributes = attributes_list($element)->sort( - static fn (DOMAttr $a, DOMAttr $b): int => $a->nodeName <=> $b->nodeName + static fn (Attr $a, Attr $b): int => $a->nodeName <=> $b->nodeName ); ``` #### xmlns_attributes_list -This function will look for all xmlns attributes on a `DOMNode`. +This function will look for all xmlns attributes on a `Dom\Node`. For nodes that don't support attributes, you will receive an empty `NodeList`. -The result of this function will be of type `NodeList`. +The result of this function will be of type `NodeList`. ```php -use DOMNameSpaceNode; +use Dom\Attr; use function VeeWee\Xml\Dom\Locator\Attribute\xmlns_attributes_list; $attributes = xmlns_attributes_list($element)->sort( - static fn (DOMNameSpaceNode $a, DOMNameSpaceNode $b): int => $a->prefix <=> $b->prefix + static fn (Attr $a, Attr $b): int => $a->prefix <=> $b->prefix ); ``` @@ -790,11 +784,11 @@ $products = $doc->locate(elements_with_tagname('product')); ### Element -These locators can be run on `DOMElement` instances. +These locators can be run on `Dom\Element` instances. #### Element\ancestors -Fetch all ancestor elements from a specific `DOMNode`. +Fetch all ancestor elements from a specific `Dom\Node`. ```php use function VeeWee\Xml\Dom\Locator\Element\ancestors; @@ -804,8 +798,8 @@ $ancestorNodes = ancestors($element); #### Element\children -Fetch all child `DOMElement`'s from a specific `DOMNode`. -If you only want all types of children (`DOMText`, ...), you can use the `Node\children()` locator. +Fetch all child `Dom\Element`'s from a specific `Dom\Node`. +If you only want all types of children (`Dom\Text`, ...), you can use the `Node\children()` locator. ```php use function VeeWee\Xml\Dom\Locator\Element\children; @@ -839,7 +833,7 @@ $products = parent_element($element); #### Element\siblings -Fetch all sibling elements from a specific `DOMNode`. +Fetch all sibling elements from a specific `Dom\Node`. ```php use function VeeWee\Xml\Dom\Locator\Element\siblings; @@ -849,11 +843,11 @@ $ancestorNodes = siblings($element); ### Node -These locators can be run on any `DOMNode` instance. +These locators can be run on any `Dom\Node` instance. #### Node\children -Fetch all child nodes from a specific `DOMNode`. This can be any kind of node: `DOMText`, `DOMElement`, ... +Fetch all child nodes from a specific `Dom\Node`. This can be any kind of node: `Dom\Text`, `Dom\Element`, ... If you only want the element children, you can use the `Element\children()` locator. ```php @@ -864,7 +858,7 @@ $childNodes = children($element); #### Node\detect_document -Fetch the `DOMDocument` to which a node is linked. +Fetch the `Dom\XMLDocument` to which a node is linked. If the node is not linked to a document yet, it throws a `InvalidArgumentException`. ```php @@ -875,7 +869,7 @@ $document = detect_document($element); #### Node\value -Fetch the value from the provided `DOMNode` and coerce it to a specific type. +Fetch the value from the provided `Dom\Node` and coerce it to a specific type. ```php use Psl\Type; @@ -886,7 +880,7 @@ $productPrice = value($product, Type\float()); ### Xmlns -These locators can be run on `DOMNode` instances. +These locators can be run on `Dom\Node` instances. #### Xmlns\linked_namespaces @@ -896,7 +890,7 @@ This function returns a list of all namespaces that are linked to a specific DOM use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Xmlns\linked_namespaces; -/** @var array $namespaces - A lookup of prefix -> namespace */ +/** @var list<\Dom\NamespaceInfo> $namespaces $namespaces = linked_namespaces($element); ``` @@ -908,13 +902,13 @@ This function returns a list of all namespaces that are linked to a specific DOM use VeeWee\Xml\Dom\Collection\NodeList; use function VeeWee\Xml\Dom\Locator\Xmlns\recursive_linked_namespaces; -/** @var array $namespaces - A lookup of prefix -> namespace */ +/** @var list<\Dom\NamespaceInfo> $namespaces $namespaces = recursive_linked_namespaces($element); ``` ### Xsd -Locates internally applied XSD schema's from a specific `DOMDocument`. +Locates internally applied XSD schema's from a specific `Dom\XMLDocument`. #### Xsd\locate_all_xsd_schemas @@ -975,13 +969,13 @@ $doc->manipulate( #### optimize_namespaces ```php -use DOMDocument; +use \Dom\XMLDocument; use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Manipulator\Document\optimize_namespaces; $doc = Document::empty(); $doc->manipulate( - static function (DOMDocument $document): void { + static function (XMLDocument $document): void { optimize_namespaces($document, 'prefix'); } ); @@ -989,7 +983,7 @@ $doc->manipulate( ### Element -Element specific manipulators operate on `DOMElement` instances. +Element specific manipulators operate on `Dom\Element` instances. #### copy_named_xmlns_attributes @@ -1018,11 +1012,11 @@ copy_named_xmlns_attributes($b, $a); ### Node -Node specific manipulators operate on `DOMNode` instances. +Node specific manipulators operate on `Dom\Node` instances. #### append_external_node -Makes it possible to append a `DOMNode` from an external document into a `DOMNode` from the current document. +Makes it possible to append a `Dom\Node` from an external document into a `Dom\Node` from the current document. ```php use function VeeWee\Xml\Dom\Manipulator\Node\append_external_node; @@ -1032,7 +1026,7 @@ $copiedNode = append_external_node($documentNode, $externalNode); #### import_node_deeply -Makes it possible to import a full `DOMNode` from an external document so that it can be used in the current document. +Makes it possible to import a full `Dom\Node` from an external document so that it can be used in the current document. ```php use function VeeWee\Xml\Dom\Manipulator\Node\import_node_deeply; @@ -1042,7 +1036,7 @@ $copiedNode = import_node_deeply($documentNode, $externalNode); #### remove -Makes it possible to remove any type of `DOMNode` directly. This include attributes. +Makes it possible to remove any type of `Dom\Node` directly. This include attributes. ```php use function VeeWee\Xml\Dom\Manipulator\Node\remove; @@ -1051,7 +1045,7 @@ $removedNode = remove($node); ``` #### rename -Makes it possible to rename `DOMElement` and `DOMAttr`nodes. +Makes it possible to rename `Dom\Element` and `Dom\Attr`nodes. ```php use function VeeWee\Xml\Dom\Manipulator\Node\rename; @@ -1071,7 +1065,7 @@ rename_element($element, 'foo', $newUri); ``` Besides renaming attributes and elements, you can also rename an xmlns namespace. -This however, operates on the `DOMDocument`: +This however, operates on the `Dom\XMLDocument`: ```php use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename; @@ -1081,7 +1075,7 @@ rename($doc, 'http://namespace', 'prefix'); #### remove_namespace -Makes it possible to remove a `DOMNamespaceNode` from an element. +Makes it possible to remove a xmlns `Dom\Attr` from an element. ```php use function VeeWee\Xml\Dom\Manipulator\Node\remove_namespace; @@ -1091,7 +1085,7 @@ $removedNamespace = remove_namespace($namespace, $element); #### replace_by_external_node -Makes it possible to replace a `DOMNode` from the current document with a `DOMNode` from an external document. +Makes it possible to replace a `Dom\Node` from the current document with a `Dom\Node` from an external document. ```php use function VeeWee\Xml\Dom\Manipulator\Node\replace_by_external_node; @@ -1101,7 +1095,7 @@ $copiedNode = replace_by_external_node($documentNode, $externalNode); #### replace_by_external_nodes -Makes it possible to replace a `DOMNode` from the current document with a list of `DOMNode` from an external document. +Makes it possible to replace a `Dom\Node` from the current document with a list of `Dom\Node` from an external document. ```php use function VeeWee\Xml\Dom\Manipulator\Node\replace_by_external_nodes; @@ -1158,12 +1152,12 @@ For more information on the processor configurators, [see the XSLT documentation #### Writing your own mapper -A configurator can be any `callable` that takes a `DOMDocument` and configures it: +A configurator can be any `callable` that takes a `Dom\XMLDocument` and configures it: ```php namespace VeeWee\Xml\Dom\Mapper; -use DOMDocument; +use \Dom\XMLDocument; /** * @template R @@ -1173,7 +1167,7 @@ interface Mapper /** * @return R */ - public function __invoke(DOMDocument $document): mixed; + public function __invoke(XMLDocument $document): mixed; } ``` @@ -1189,11 +1183,11 @@ $result = $document->map($mapper); ## Predicates -Check if a DOMNode is of a specific type. +Check if a Node is of a specific type. #### is_attribute -Checks if a node is of type `DOMAttr`. +Checks if a node is of type `Dom\Attr`. ```php use function VeeWee\Xml\Dom\Predicate\is_attribute; @@ -1205,7 +1199,7 @@ if (is_attribute($someNode)) { #### is_cdata -Checks if a node is of type `DOMCdataSection`. +Checks if a node is of type `Dom\CDATASection`. ```php use function VeeWee\Xml\Dom\Predicate\is_cdata; @@ -1217,7 +1211,7 @@ if (is_cdata($someNode)) { #### is_default_xmlns_attribute -Checks if a node is of type `DOMNameSpaceNode` and is the default xmlns. +Checks if a node is of type `Dom\Attr` and is the default xmlns. ```php use function VeeWee\Xml\Dom\Predicate\is_default_xmlns_attribute; @@ -1229,7 +1223,7 @@ if (is_default_xmlns_attribute($namespace)) { #### is_document -Checks if a node is of type `DOMDocument`. +Checks if a node is of type `Dom\XMLDocument`. ```php use function VeeWee\Xml\Dom\Predicate\is_document; @@ -1241,7 +1235,7 @@ if (is_document($someNode)) { #### is_document_element -Checks if a node is the root `DOMElement` of the `DOMDocument`. +Checks if a node is the root `Dom\Element` of the `Dom\XMLDocument`. ```php use function VeeWee\Xml\Dom\Predicate\is_document_element; @@ -1253,7 +1247,7 @@ if (is_document_element($rootNode)) { #### is_element -Checks if a node is of type `DOMElement`. +Checks if a node is of type `Dom\Element`. ```php use VeeWee\XML\DOM\Document; @@ -1269,7 +1263,7 @@ if (is_element($item)) { #### is_non_empty_text -Checks if a node is of type `DOMText` and that its value is not just a whitespace. +Checks if a node is of type `Dom\Text` and that its value is not just a whitespace. This behaves in the opposite way of the `is_whitespace()` function and uses the `is_text()` function internally. ```php @@ -1282,7 +1276,7 @@ if (is_non_empty_text($someNode)) { #### is_text -Checks if a node is of type `DOMText`. +Checks if a node is of type `Dom\Text`. You can also check for `is_whitespace()` or `is_non_empty_text()` if you want to do a deeper check. ```php @@ -1295,7 +1289,7 @@ if (is_text($someNode)) { #### is_xmlns_attribute -Checks if a node is of type `DOMNameSpaceNode`. +Checks if a node is of type `Dom\Attr`. ```php use function VeeWee\Xml\Dom\Predicate\is_xmlns_attribute; @@ -1307,7 +1301,7 @@ if (is_xmlns_attribute($namespace)) { #### is_whitespace -Checks if a node is of type `DOMText` and that its value consists of just whitespaces. +Checks if a node is of type `Dom\Text` and that its value consists of just whitespaces. This behaves in the opposite way of the `is_non_empty_text()` function and uses the `is_text()` function internally. ```php @@ -1341,7 +1335,7 @@ Imagine you want to replace all attribute values in all XML tags with 'wazzup'. Here is an example visitor that implements this feature: ```php -use DOMNode; +use Dom\Node; use VeeWee\Xml\Dom\Traverser\Action; use VeeWee\Xml\Dom\Traverser\Visitor\AbstractVisitor; use function VeeWee\Xml\Dom\Builder\attribute; @@ -1349,7 +1343,7 @@ use function VeeWee\Xml\Dom\Predicate\is_attribute; class WazzupVisitor extends AbstractVisitor { - public function onNodeLeave(DOMNode $node) : Action + public function onNodeLeave(Node $node) : Action { if (!is_attribute($node)) { return new Action\Noop(); @@ -1362,7 +1356,7 @@ class WazzupVisitor extends AbstractVisitor } ``` -So how does it work? Every time the traverser sees a DOMNode, it will trigger all provided visitors. +So how does it work? Every time the traverser sees a Node, it will trigger all provided visitors. The visitor above will look for attribute nodes. All other nodes will be ignored. Next it will replace the attribute with value WAZZUP and add it to the node. Finally, we tell the traverser that the visitor is finished and no additional actions need to be performed. @@ -1415,12 +1409,12 @@ The result of these functions is an action that can be performed by the traverse ```php namespace VeeWee\Xml\Dom\Traverser; -use DOMNode; +use Dom\Node; interface Visitor { - public function onNodeEnter(DOMNode $node): Action; - public function onNodeLeave(DOMNode $node): Action; + public function onNodeEnter(Node $node): Action; + public function onNodeLeave(Node $node): Action; } ``` @@ -1429,11 +1423,11 @@ An action looks like this: ```php namespace VeeWee\Xml\Dom\Traverser; -use DOMNode; +use Dom\Node; interface Action { - public function __invoke(DOMNode $currentNode): void; + public function __invoke(Node $currentNode): void; } ``` @@ -1497,17 +1491,17 @@ $issues = $doc->validate(xsd_validator('myown.xsd')); #### Writing your own validator -A validator can be any `callable` that takes a `DOMDocument` and returns an `IssueCollection`. +A validator can be any `callable` that takes a `Dom\XMLDocument` and returns an `IssueCollection`. ```php namespace VeeWee\Xml\Dom\Validator; -use DOMDocument; +use \Dom\XMLDocument; use VeeWee\Xml\ErrorHandling\Issue\IssueCollection; interface Validator { - public function __invoke(DOMDocument $document): IssueCollection; + public function __invoke(XMLDocument $document): IssueCollection; } ``` @@ -1581,16 +1575,16 @@ $xpath = $doc->xpath(php_namespace()); #### Writing your own XPath configurator -A configurator can be any `callable` that takes a `DOMXPath` and configures it: +A configurator can be any `callable` that takes a `Dom\XPath` and configures it: ```php namespace VeeWee\Xml\Dom\Xpath\Configurator; -use DOMXPath; +use Dom\XPath; interface Configurator { - public function __invoke(DOMXPath $xpath): DOMXPath; + public function __invoke(XPath $xpath): XPath; } ``` @@ -1599,7 +1593,7 @@ You can apply the XPath configurator as followed: ```php use VeeWee\Xml\Dom\Document; -$document = Document::configure('some.xml'); +$document = Document::fromXmlFile('some.xml'); $document->xpath(...$configurators); ``` @@ -1628,7 +1622,7 @@ $count = $xpath->evaluate('count(.//item)', Type\int(), $productsElement); #### query -Run a specific XPath query and expect to get back a list of matching `DOMElement`. +Run a specific XPath query and expect to get back a list of matching `Dom\Element`. ```php use VeeWee\Xml\Dom\Document; @@ -1666,13 +1660,13 @@ $productName = $xpath->querySingle('/name', $product); #### Writing your own XPath locator -An XPath locator can be any `callable` that takes a `DOMXPath` and locates something on it: +An XPath locator can be any `callable` that takes a `Dom\XPath` and locates something on it: ```php namespace VeeWee\Xml\Dom\Xpath\Locator; -use DOMXPath; +use Dom\XPath; /** * @template T @@ -1682,7 +1676,7 @@ interface Locator /** * @return T */ - public function __invoke(DOMXPath $xpath): mixed; + public function __invoke(XPath $xpath): mixed; } ``` @@ -1691,7 +1685,7 @@ You can apply the locator as followed: ```php use VeeWee\Xml\Dom\Document; -$document = Document::configure('some.xml'); +$document = Document::fromXmlFile('some.xml'); $xpath = $document->xpath(); $result = $xpath->locate($locator); diff --git a/docs/xsd.md b/docs/xsd.md index 3e302a0d..92c264d8 100644 --- a/docs/xsd.md +++ b/docs/xsd.md @@ -10,7 +10,7 @@ use VeeWee\Xml\Xsd\Schema\Manipulator; use function VeeWee\Xml\Dom\Locator\Xsd\locate_all_xsd_schemas; $doc = Document::fromXmlFile('some.xml'); -$schemas = locate_all_xsd_schemas($doc->toUnsafeDocument()) +$schemas = $doc->map(locate_all_xsd_schemas(...)) ->manipulate(Manipulator\base_path('/var/www')) ->manipulate(Manipulator\overwrite_with_local_files([ 'http://www.w3.org/2001/XMLSchema' => '/local/XMLSchema.xsd' diff --git a/psalm.xml b/psalm.xml index 8cf94053..b7811a25 100644 --- a/psalm.xml +++ b/psalm.xml @@ -3,7 +3,7 @@ errorLevel="1" resolveFromConfigFile="true" strictBinaryOperands="true" - phpVersion="8.1" + phpVersion="8.3" allowStringToStandInForClass="true" rememberPropertyAssignmentsAfterCall="false" skipChecksOnUnresolvableIncludes="false" diff --git a/src/Xml/Dom/Configurator/loader.php b/src/Xml/Dom/Configurator/loader.php new file mode 100644 index 00000000..52e851d4 --- /dev/null +++ b/src/Xml/Dom/Configurator/loader.php @@ -0,0 +1,20 @@ +toUnsafeDocument(); + }; +} \ No newline at end of file diff --git a/src/Xml/Dom/Loader/xml_file_loader.php b/src/Xml/Dom/Loader/xml_file_loader.php index 1baff7cf..2c55886c 100644 --- a/src/Xml/Dom/Loader/xml_file_loader.php +++ b/src/Xml/Dom/Loader/xml_file_loader.php @@ -13,11 +13,11 @@ * @param int $options - bitmask of LIBXML_* constants https://www.php.net/manual/en/libxml.constants.php * @return Closure(): XMLDocument */ -function xml_file_loader(string $file, int $options = 0): Closure +function xml_file_loader(string $file, int $options = 0, ?string $override_encoding = null): Closure { - return static fn () => disallow_issues(static function () use ($file, $options): XMLDocument { + return static fn () => disallow_issues(static function () use ($file, $options, $override_encoding): XMLDocument { Assert::fileExists($file); - return XMLDocument::createFromFile($file, $options); + return XMLDocument::createFromFile($file, $options, $override_encoding); }); } diff --git a/src/Xml/Dom/Loader/xml_string_loader.php b/src/Xml/Dom/Loader/xml_string_loader.php index b53e746c..e2669aaa 100644 --- a/src/Xml/Dom/Loader/xml_string_loader.php +++ b/src/Xml/Dom/Loader/xml_string_loader.php @@ -13,9 +13,9 @@ * @param int $options - bitmask of LIBXML_* constants https://www.php.net/manual/en/libxml.constants.php * @return Closure(): XMLDocument */ -function xml_string_loader(string $xml, int $options = 0): Closure +function xml_string_loader(string $xml, int $options = 0, ?string $override_encoding = null): Closure { - return static fn () => disallow_issues(static function () use ($xml, $options): XMLDocument { - return XMLDocument::createFromString($xml, $options); + return static fn () => disallow_issues(static function () use ($xml, $options, $override_encoding): XMLDocument { + return XMLDocument::createFromString($xml, $options, $override_encoding); }); } diff --git a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php index 653937a3..6faf413f 100644 --- a/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php +++ b/src/Xml/Dom/Manipulator/Xmlns/rename_element_namespace.php @@ -33,7 +33,9 @@ function rename_element_namespace(\Dom\Element $element, string $namespaceURI, s // Remove the attribute that would become a duplicate $element->removeAttributeNode($attr); } else { + // @codeCoverageIgnoreStart throw $e; + // @codeCoverageIgnoreEnd } } $attr->rename($attr->namespaceURI, 'xmlns:' . $newPrefix); diff --git a/src/bootstrap.php b/src/bootstrap.php index c11c580f..af32d488 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -24,6 +24,7 @@ 'Xml\Dom\Configurator\canonicalize' => __DIR__.'/Xml/Dom/Configurator/canonicalize.php', 'Xml\Dom\Configurator\comparable' => __DIR__.'/Xml/Dom/Configurator/comparable.php', 'Xml\Dom\Configurator\document_uri' => __DIR__.'/Xml/Dom/Configurator/document_uri.php', + 'Xml\Dom\Configurator\loader' => __DIR__.'/Xml/Dom/Configurator/loader.php', 'Xml\Dom\Configurator\normalize' => __DIR__.'/Xml/Dom/Configurator/normalize.php', 'Xml\Dom\Configurator\optimize_namespaces' => __DIR__.'/Xml/Dom/Configurator/optimize_namespaces.php', 'Xml\Dom\Configurator\pretty_print' => __DIR__.'/Xml/Dom/Configurator/pretty_print.php', diff --git a/tests/Xml/Dom/Assert/AssertAttributeTest.php b/tests/Xml/Dom/Assert/AssertAttributeTest.php new file mode 100644 index 00000000..e94a26c0 --- /dev/null +++ b/tests/Xml/Dom/Assert/AssertAttributeTest.php @@ -0,0 +1,48 @@ +expectException(AssertException::class); + } + + $actual = assert_attribute($node); + static::assertSame($node, $actual); + } + + public static function provideTestCases() + { + $doc = Document::fromXmlString( + << + Hello + + EOXML + )->toUnsafeDocument(); + + yield [$doc, false]; + yield [$doc->documentElement, false]; + yield [$doc->documentElement->firstElementChild, false]; + yield [$doc->documentElement->firstElementChild->attributes->getNamedItem('attr'), true]; + yield [$doc->documentElement->firstElementChild->attributes->getNamedItem('xmlns:foo'), true]; + yield [$doc->documentElement->firstElementChild->attributes->getNamedItem('xmlns'), true]; + yield [$doc->documentElement->firstElementChild->firstChild, false]; + yield [null, false]; + } +} diff --git a/tests/Xml/Dom/Configurator/LoaderTest.php b/tests/Xml/Dom/Configurator/LoaderTest.php new file mode 100644 index 00000000..b6b95059 --- /dev/null +++ b/tests/Xml/Dom/Configurator/LoaderTest.php @@ -0,0 +1,40 @@ +'; + + $loader = loader(static function () use ($xml): \Dom\XMLDocument { + return Document::fromXmlString($xml)->toUnsafeDocument(); + }); + + $result = $loader($doc); + static::assertNotSame($doc, $result); + static::assertXmlStringEqualsXmlString($xml, $result->saveXML()); + } + + + public function test_it_can_mark_xml_loading_as_failed(): void + { + $doc = \Dom\XMLDocument::createEmpty(); + $exception = new Exception('Could not load the XML document'); + $loader = loader(static function () use ($exception): never { + throw $exception; + }); + + $this->expectExceptionObject($exception); + $loader($doc); + } +} diff --git a/tests/Xml/Dom/Loader/XmlFileLoaderTest.php b/tests/Xml/Dom/Loader/XmlFileLoaderTest.php index 89f0a340..0d005b2a 100644 --- a/tests/Xml/Dom/Loader/XmlFileLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlFileLoaderTest.php @@ -9,6 +9,7 @@ use VeeWee\Tests\Xml\Helper\FillFileTrait; use VeeWee\Xml\Exception\RuntimeException; use function VeeWee\Xml\Dom\Loader\xml_file_loader; +use function VeeWee\Xml\Dom\Loader\xml_string_loader; final class XmlFileLoaderTest extends TestCase { @@ -60,4 +61,17 @@ public function test_it_throws_exception_on_invalid_file(): void $loader(); } + + public function test_it_can_override_charset(): void + { + $xml = 'héllo'; + [$file, $handle] = $this->fillFile($xml); + $loader = xml_file_loader($file, override_encoding: 'Windows-1252'); + + $doc = $loader(); + fclose($handle); + + static::assertSame('héllo', $doc->documentElement->textContent); + static::assertSame('Windows-1252', $doc->xmlEncoding); + } } diff --git a/tests/Xml/Dom/Loader/XmlStringLoaderTest.php b/tests/Xml/Dom/Loader/XmlStringLoaderTest.php index 36bc1bdb..73a6fca0 100644 --- a/tests/Xml/Dom/Loader/XmlStringLoaderTest.php +++ b/tests/Xml/Dom/Loader/XmlStringLoaderTest.php @@ -39,4 +39,14 @@ public function test_it_can_load_with_options(): void static::assertSame('HELLO', $doc->saveXML($doc->documentElement)); } + + public function test_it_can_override_charset(): void + { + $xml = 'héllo'; + $loader = xml_string_loader($xml, override_encoding: 'Windows-1252'); + $doc = $loader(); + + static::assertSame('héllo', $doc->documentElement->textContent); + static::assertSame('Windows-1252', $doc->xmlEncoding); + } }