shanethehat/pretty-xml
Pretty XML is a lightweight PHP package for formatting raw XML into clean, readable, indented output. It wraps DOMDocument to add proper line breaks and indentation, making XML easier to debug, log, or display. Simple to install and use in any PHP or Laravel project.
Install via Composer:
composer require shanethehat/pretty-xml
Look first at README.md and src/PrettyXml.php. The package is intentionally tiny — essentially a thin, well-behaved wrapper around PHP's DOMDocument. There is almost no configuration surface, so a five-minute read of the source tells you everything.
The first realistic use case is turning a compact XML payload into something a human can read:
use ShaneTheHat\PrettyXml\PrettyXml;
$raw = '<order><id>42</id><items><item qty="2">Widget</item></items></order>';
// Pretty print for logs, emails, or a debug panel
echo PrettyXml::pretty($raw);
And the inverse — collapsing whitespace before storing or transmitting:
$minified = PrettyXml::minify($raw);
Both methods operate on strings, not files, which keeps the API simple. Read the file yourself (file_get_contents, Laravel's Storage::get) and pass the result in.
Response formatting in Laravel. If you expose XML from an API (RSS, sitemaps, SOAP-style responses), wrap the response so the output stays readable in development but compact in production:
return response(PrettyXml::pretty($xml), 200, ['Content-Type' => 'application/xml']);
A dedicated formatter service. Rather than sprinkling static calls through controllers, bind a small wrapper you can mock and swap:
namespace App\Support;
use ShaneTheHat\PrettyXml\PrettyXml;
class XmlFormatter
{
public function pretty(string $xml): string
{
return PrettyXml::pretty($xml);
}
public function minify(string $xml): string
{
return PrettyXml::minify($xml);
}
}
Downloadable, human-readable exports. When generating an XML export for a customer, pretty print before streaming it so the file opens nicely in editors and browsers:
return response()->streamDownload(function () use ($xml) {
echo PrettyXml::pretty($xml);
}, 'export.xml', ['Content-Type' => 'application/xml']);
Logging and debugging. In a local-only middleware or a Log::debug call, pretty print outbound XML so it diffs cleanly against expected fixtures. Gate it behind app()->isLocal() so production logs stay compact.
Storage normalization. Minify on write to save bytes, pretty print on read for operators who inspect the database or object storage directly:
Storage::put("orders/{$id}.xml", PrettyXml::minify($xml));
Testing. XML assertions are far easier to read when you pretty print actual vs. expected before dumping on failure. Combine with assertXmlStringEqualsXmlString (which ignores formatting) and use pretty() only for failure messages.
Chaining with builders. Because it accepts and returns strings, it composes naturally with any XML generator — SimpleXMLElement::asXML(), DOMDocument::saveXML(), spatie/array-to-xml, etc. Pretty print as the final step before output.
Whitespace in mixed content is semantic. Pretty printing injects indentation into element-only content, but XML with mixed content (text interleaved with child tags) can be silently altered. For documents like XHTML or DocBook, minify only, or skip formatting inside elements whose text matters (<pre>, <code>).
It loads the whole document into memory. DOMDocument is not a streaming parser. For large reports (tens of MB), prefer XMLReader or stream a pre-formatted file — do not funnel multi-hundred-megabyte payloads through this.
Invalid XML throws. Malformed input produces a DOMDocument warning/exception. Wrap in try/catch in request paths and convert to a 422 rather than letting it bubble into a 500.
CDATA sections and entity references can shift. Reformatting through DOMDocument may normalize CDATA and entity handling. If byte-for-byte fidelity matters (signatures, checksums over the raw string), do not round-trip — hash the original.
Encoding declarations get regenerated. The <?xml ... ?> prolog may be rewritten (e.g., encoding normalized to UTF-8). If callers depend on a specific declaration, prepend it yourself after minifying.
Namespaces usually survive, but verify. Default namespaces and prefixed namespaces are preserved by DOMDocument; however, if you post-process the string with regex you'll break them. Format the XML first, then never touch namespace prefixes again.
Minify is not a compressor. It removes formatting whitespace, not comments or redundant attributes. If you need gzip-level savings, gzencode the minified output.
Config quirks are essentially nonexistent. There is no publishable config file. Any behavior you want to change (indent string, LIBXML_NOBLANKS, comment stripping) means dropping to DOMDocument directly:
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$doc->loadXML($xml, LIBXML_NOBLANKS);
echo $doc->saveXML();
Extension points are limited by design. Subclass PrettyXml or wrap it (see the XmlFormatter pattern) rather than editing vendor code. If your needs outgrow the package — comment removal, attribute sorting, XSLT-driven formatting — replace it with a small DOMDocument helper of your own; you won't lose much.
Debugging tip. When the output looks wrong, first run the same input through raw DOMDocument::loadXML with libxml_use_internal_errors(true) and inspect libxml_get_errors(). The formatter's behavior is a direct consequence of the underlying parser state, not a bug in the package.
Pin the version. With a low-traffic package, lock the version in composer.json and keep your wrapper interface stable so a future refactor is a one-file change.
How can I help you explore Laravel packages today?