ottosmops / xmltoolkit
Add and remove XML tags and attributes.
Requires
- php: ^8.3
Requires (Dev)
- phpunit/phpunit: ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Xmltoolkit is a PHP class that provides various utilities for manipulating XML documents. It allows loading XML from files or strings, performing XPath queries (with namespace support), and modifying XML elements and attributes.
Installation
Use Composer to install the package:
composer require ottosmops/xmltoolkit
Usage
require 'vendor/autoload.php'; use Ottosmops\Xmltoolkit\Xmltoolkit; $xml = new Xmltoolkit(); $xml->loadFromFile('example.xml'); $xml->renameTagByXPath('//a', 'ref'); $xml->saveToFile('example.xml', true); // pretty print // Find elements by attribute value $elements = $xml->findElementsByAttributeValue('type', 'special'); // Replace attribute value $xml->replaceAttributeValue('type', 'special', 'normal'); // Find elements by text content $elements = $xml->findElementsByTextContent('oldtext'); // Replace text content $xml->replaceElementTextContent('oldtext', 'newtext'); // Save as pretty-printed string $prettyXml = $xml->saveToString(true);
Namespace Support
You can register XML namespaces for XPath queries:
$xml = new Xmltoolkit(); $xml->registerNamespaces(['ns' => 'http://example.com/ns']); $xml->loadFromFile('example.xml'); $nodes = $xml->queryXPath('//ns:tag');
Preserve Formatting
When editing source files under version control, keep the diff small:
$xml = new Xmltoolkit(); $xml->preserveFormatting(); $xml->loadFromFile('letter.xml'); // ... edit ... $xml->saveToFile('letter.xml');
The output keeps the source's XML declaration (or its absence), its final newline (or its absence) and its non-ASCII character references such as  . A restored reference replaces every occurrence of that character, including ones the source had written literally. What DOM cannot keep: line breaks and spaces inside start tags, <a></a> (becomes <a/>) and whitespace after the root element.
Wrap Text Matches
Tag names in running text, e.g. persons in TEI, deciding match by match:
$xml = new Xmltoolkit(); $xml->registerNamespaces(['tei' => 'http://www.tei-c.org/ns/1.0']); $xml->preserveFormatting(); $xml->loadFromFile('letter.xml'); $count = $xml->wrapTextMatches( "/(?<!\w)Thurneysen(?:'?e?n?s)?(?!\w)/u", function (string $match, \DOMText $node, int $line, int $offset) { // ask the user, look up an ID ... or return null to leave the match $persName = $node->ownerDocument->createElementNS($node->lookupNamespaceURI(null), 'persName'); $persName->setAttribute('ref', 'kbga-actors-403'); return $persName; }, within: '//tei:text', exclude: ['tei:bibl', 'tei:note', 'tei:persName'], ); $xml->saveToFile('letter.xml');
Only text nodes are searched, so matches inside tags, attributes or excluded elements cannot occur, and names spanning element boundaries (Eduard <lb/>Thurneysen) are not found. Create the element in the document's namespace (createElementNS), otherwise it is serialized with xmlns="". $offset is the byte offset of the match in $node->data. $line is the match's line in the source, computed from the parent element's start tag; line breaks inside start tags of preceding siblings shift it, and it is 0 below elements created after loading.
Regex
$elements = $xml->findElementsByAttributeRegex('type', '/^sp.*/'); $xml->replaceAttributeValueRegex('type', '/^sp.*/', 'normal'); $elements = $xml->findElementsByTextRegex('/old.*/'); $xml->replaceElementTextRegex('/old.*/', 'newtext');
Methods
Loading and saving
All other methods throw a LogicException if no document has been loaded successfully.
Load an XML file into the DOM object and ensure it is UTF-8 encoded.
public function loadFromFile(string $filePath): bool
Load an XML string into the DOM object and ensure it is UTF-8 encoded.
public function loadFromString(string $xmlString): bool
Load an XML/HTML fragment. The fragment is wrapped in a <root> element.
public function loadFromFragment(string $xmlFragment): bool
Save the XML document to a file and ensure it is UTF-8 encoded.
public function saveToFile(string $filePath, bool $prettyPrint = false): bool
Return the XML as a string and ensure it is UTF-8 encoded.
public function saveToString(bool $prettyPrint = false): string
Register namespaces for XPath queries (['prefix' => 'namespaceURI']). Can be called before or after loading.
public function registerNamespaces(array $namespaces): void
Keep the source's XML declaration, final newline and character references when saving (see Preserve Formatting). Can be called before or after loading.
public function preserveFormatting(bool $preserve = true): void
Find and replace
Find elements by attribute value.
public function findElementsByAttributeValue(string $attributeName, string $attributeValue): array
Replace attribute value for all elements with a given attribute value.
public function replaceAttributeValue(string $attributeName, string $oldValue, string $newValue): void
Find elements by text content.
public function findElementsByTextContent(string $textContent): array
Replace text content for all elements with a given text content.
public function replaceElementTextContent(string $oldText, string $newText): void
Find and replace with regex
Find elements by attribute value using regex.
public function findElementsByAttributeRegex(string $attributeName, string $pattern): array
Replace attribute value using regex.
public function replaceAttributeValueRegex(string $attributeName, string $pattern, string $replacement): void
Find elements by text content using regex. Only elements without child elements are matched.
public function findElementsByTextRegex(string $pattern): array
Replace text content using regex.
public function replaceElementTextRegex(string $pattern, string $replacement): void
XPath
Run an arbitrary XPath query and return the found nodes as an array of XML strings.
public function queryXPath(string $xpathExpression): array
Rename a tag found by an XPath expression.
public function renameTagByXPath(string $xpathExpression, string $newTagName): void
Rename an attribute found by an XPath expression.
public function renameAttributeByXPath(string $xpathExpression, string $oldAttributeName, string $newAttributeName): void
Add a new attribute to a tag found by an XPath expression.
public function addAttributeByXPath(string $xpathExpression, string $attributeName, string $attributeValue): void
Remove an attribute from a tag found by an XPath expression.
public function removeAttributeByXPath(string $xpathExpression, string $attributeName): void
Append an HTML string to the nodes found by an XPath expression (as last child).
public function appendHtmlToXPath(string $xpathExpression, string $htmlString): void
Wrap regex matches in text nodes with the elements a callback returns (see Wrap Text Matches). Returns the number of wrapped matches.
public function wrapTextMatches(string $pattern, callable $wrapper, string $within = '/', array $exclude = []): int
Remove an element found by an XPath expression.
public function removeElementByXPath(string $xpathExpression): void
Wrap an element found by an XPath expression with a new element.
public function wrapElementByXPath(string $xpathExpression, string $wrapperTagName): void
Remove an element but keep its content (unwrap the element) based on an XPath expression.
public function unwrapElementByXPath(string $xpathExpression): void
Tests
Run the tests with PHPUnit:
vendor/bin/phpunit