PHP libxml Library Reference Guide
Guide to the PHP libxml extension: capture parse errors, parse and validate XML with SimpleXML or DOM, and avoid XXE in untrusted input.
This guide covers the PHP libxml extension: what it is, how PHP's XML parsers report errors through it, and how to parse, validate, and manipulate XML safely. The core idea to take away is that libxml is the shared error-reporting and configuration layer underneath SimpleXML, DOM, and XMLReader — once you understand it, every XML extension in PHP becomes easier to debug.
What is PHP libxml?
libxml is the C library that PHP uses for almost all of its XML processing. The PHP libxml extension exposes that library's error handling and parser options to your code. You rarely call it on its own — instead you use a higher-level extension that is built on top of it:
- SimpleXML — the quick, array-like way to read XML.
- DOM (
DOMDocument) — full read/write access to the document tree. XMLReader/XMLWriter— streaming, memory-efficient parsing of large files.
When any of these hit malformed XML, they report the problem through libxml. The libxml_* functions let you capture, inspect, and clear those errors.
Installing PHP libxml
The libxml extension is bundled and enabled by default — there is nothing to install. You can confirm it is active at runtime:
<?php
var_dump(extension_loaded('libxml')); // bool(true)
echo LIBXML_DOTTED_VERSION; // e.g. "2.9.14" — the linked libxml2 version
?>If a custom build reports false, recompile PHP with --with-libxml (older PHP used --enable-libxml).
Handling libxml errors
This is the most important part of the extension. By default, a malformed document emits PHP warnings, which is awkward to handle in production. Instead, switch to internal error mode: libxml then collects errors in a buffer that you read yourself.
<?php
// Stop warnings; buffer errors instead.
libxml_use_internal_errors(true);
$broken = '<root><item>unclosed</root>';
$xml = simplexml_load_string($broken);
if ($xml === false) {
foreach (libxml_get_errors() as $error) {
// Each $error is a LibXMLError object.
printf(
"[%s] line %d: %s",
$error->level === LIBXML_ERR_FATAL ? 'fatal' : 'warning',
$error->line,
trim($error->message)
);
echo PHP_EOL;
}
libxml_clear_errors(); // Empty the buffer so it doesn't leak into later parses.
}
?>A LibXMLError exposes level (LIBXML_ERR_WARNING, LIBXML_ERR_ERROR, LIBXML_ERR_FATAL), code, message, line, column, and file. Related functions:
libxml_use_internal_errors()— turn buffering on/off.libxml_get_errors()— return all buffered errors as an array.libxml_get_last_error()— return only the most recent error.libxml_clear_errors()— empty the buffer.
Parsing XML Documents
The most common use of XML in PHP is reading a document. simplexml_load_string() (and its file twin simplexml_load_file()) return false on failure, so always pair them with internal error mode:
<?php
libxml_use_internal_errors(true);
$source = '<catalog><book id="1">PHP Basics</book></catalog>';
$xml = simplexml_load_string($source);
if ($xml === false) {
echo "Failed to parse XML." . PHP_EOL;
foreach (libxml_get_errors() as $error) {
echo trim($error->message) . PHP_EOL;
}
libxml_clear_errors();
} else {
echo "Loaded: " . $xml->book . PHP_EOL; // Loaded: PHP Basics
echo "id = " . $xml->book['id'] . PHP_EOL; // id = 1
}
?>Parser options (libxml constants)
Most XML functions accept an $options bitmask of LIBXML_* constants. Combine them with the bitwise OR (|) operator:
<?php
$xml = simplexml_load_string(
'<a> <b>text</b> </a>',
'SimpleXMLElement',
LIBXML_NOCDATA | LIBXML_NOBLANKS // drop CDATA wrappers + ignore whitespace-only nodes
);
echo $xml->b; // text
?>Frequently used options:
| Constant | Effect |
|---|---|
LIBXML_NOBLANKS | Remove blank (whitespace-only) nodes. |
LIBXML_NOCDATA | Merge CDATA sections as plain text. |
LIBXML_NOERROR / LIBXML_NOWARNING | Suppress errors / warnings. |
LIBXML_COMPACT | Small-node optimisation for large documents. |
LIBXML_NOENT | Substitute entities — dangerous with untrusted input (see below). |
Security: untrusted XML and XXE
XML eXternal Entity (XXE) attacks let a malicious document read local files or trigger network requests. Never enable entity loading on input you do not control. The safe defaults in modern PHP (7.0+) already disable external entity loading, so the rule is simple:
- Do not pass
LIBXML_NOENTorLIBXML_DTDLOADwhen parsing untrusted XML. - On PHP < 8.0 you may additionally call
libxml_disable_entity_loader(true)as a hard guard. Seelibxml_disable_entity_loader()for details (the function is deprecated in 8.0+ because loading is off by default).
Validating XML Documents
libxml can validate a document against a DTD or an XSD schema. DOMDocument::schemaValidate() is the most direct way, and validation errors flow through the same buffer:
<?php
libxml_use_internal_errors(true);
$doc = new DOMDocument();
if (!$doc->load('example.xml')) {
echo "Could not load document." . PHP_EOL;
exit;
}
if ($doc->schemaValidate('example.xsd')) {
echo "The XML document is valid." . PHP_EOL;
} else {
echo "Validation failed:" . PHP_EOL;
foreach (libxml_get_errors() as $error) {
echo " line {$error->line}: " . trim($error->message) . PHP_EOL;
}
libxml_clear_errors();
}
?>For very large files, prefer the streaming XMLReader, which validates as it reads without loading the whole document into memory:
<?php
$reader = new XMLReader();
$reader->open('example.xml');
$reader->setSchema('example.xsd'); // attach the XSD before reading
$valid = true;
while ($reader->read()) {
if (!$reader->isValid()) {
$valid = false;
break;
}
}
$reader->close();
echo $valid ? "Document is valid." : "Document is not valid.";
?>Manipulating XML Documents
To change a document you generally use DOM. The example below builds a document in memory (so it runs without any external file), appends a node, and prints the result:
<?php
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->formatOutput = true; // pretty-print the output
// Build a root, then add a child element with text content.
$root = $doc->createElement('catalog');
$doc->appendChild($root);
$book = $doc->createElement('book', 'Learning PHP');
$book->setAttribute('id', '42');
$root->appendChild($book);
echo $doc->saveXML();
// <?xml version="1.0" encoding="UTF-8"?>
// <catalog>
// <book id="42">Learning PHP</book>
// </catalog>
?>When editing a file from disk, load it, locate the target node with getElementsByTagName(), append to that node (not to the document, which may only hold one root element), then save() it back.
When would I use this?
- Reading config or feed data (RSS/Atom, SOAP responses, sitemaps) — parse with SimpleXML, guard with internal errors.
- Validating uploads — reject documents that fail
schemaValidate()before you trust them. - Generating XML for an API or export — build it with DOM so attributes and encoding are handled correctly.
- Debugging "invalid XML" failures — read
libxml_get_errors()to see the exact line and column.
Conclusion
The libxml extension is the foundation under PHP's XML stack. The pattern that pays off everywhere is: call libxml_use_internal_errors(true), parse or validate, then inspect libxml_get_errors() and libxml_clear_errors(). From there, pick the right tool — SimpleXML for quick reads, DOM for editing, XMLReader for large files — and pass LIBXML_* options to control parsing behaviour. Keep entity loading off for untrusted input and your XML handling will be both robust and safe.