zendframework/zendxml
ZendXml provides secure XML scanning/loading for PHP to help prevent XXE and XML entity expansion (XEE) attacks. It disables external entity loading and rejects documents using ENTITY declarations, returning SimpleXMLElement or DOMDocument. Repository abandoned; moved to laminas/laminas-xml.
## Getting Started
### Minimal Steps to Begin
1. **Installation**
Add the package via Composer in your Laravel project:
```bash
composer require zendframework/zendxml
Note: Since the package is archived, ensure compatibility with your PHP version (tested up to PHP 8.1 in this release).
First Use Case: Parsing XML Import the core class and parse a simple XML string:
use Zend\Xml\Xml;
$xmlString = '<root><item>Test</item></root>';
$xml = Xml::fromString($xmlString);
$items = $xml->getElementsByTagName('item');
foreach ($items as $item) {
echo $item->current(); // Output: "Test"
}
First Use Case: Scanning HTML (New in 1.2.0)
Use the new Security::scanHtml() method to safely parse HTML:
use Zend\Xml\Security;
$html = '<div><p>Safe HTML</p></div>';
$safeXml = Security::scanHtml($html);
if ($safeXml instanceof \SimpleXMLElement) {
echo $safeXml->asXML(); // Output: Sanitized XML representation
}
Where to Look First
Zend\Xml namespace for core classes like Xml, Reader, Writer, and the new Security class.Zend\Xml\Xml and Zend\Xml\Security classes if needed:
$this->app->bind('zend.xml', function () {
return new \Zend\Xml\Xml();
});
$this->app->bind('zend.xml.security', function () {
return new \Zend\Xml\Security();
});
XML Parsing and Traversal
Use Zend\Xml\Xml for DOM-like traversal:
$xml = Xml::fromString($xmlData);
$nodes = $xml->getElementsByTagName('node');
foreach ($nodes as $node) {
$value = $node->getAttribute('id'); // Access attributes
$text = $node->current(); // Access text content
}
Generating XML
Use Zend\Xml\Writer for structured XML creation:
use Zend\Xml\Writer;
$writer = new Writer();
$writer->openMemory();
$writer->startElement('root');
$writer->writeElement('item', 'Value');
$writer->endElement();
$xmlString = $writer->flush();
HTML Sanitization (New in 1.2.0)
Use Security::scanHtml() to safely parse HTML and convert it to XML:
use Zend\Xml\Security;
$html = '<div><p>Safe HTML</p><script>alert("XSS")</script></div>';
$sanitized = Security::scanHtml($html);
if ($sanitized instanceof \SimpleXMLElement) {
// Safe to process as XML
echo $sanitized->asXML();
}
Integration with Laravel
AppServiceProvider:
public function register()
{
$this->app->singleton('zend.xml', function () {
return new \Zend\Xml\Xml();
});
$this->app->singleton('zend.xml.security', function () {
return new \Zend\Xml\Security();
});
}
// app/Facades/ZendXml.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class ZendXml extends Facade {
protected static function getFacadeAccessor() { return 'zend.xml'; }
}
// app/Facades/ZendXmlSecurity.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class ZendXmlSecurity extends Facade {
protected static function getFacadeAccessor() { return 'zend.xml.security'; }
}
Usage:
$xml = ZendXml::fromString($data);
$sanitized = ZendXmlSecurity::scanHtml($html);
Handling Large XML Files
Use Zend\Xml\Reader for streaming large files:
use Zend\Xml\Reader;
$reader = new Reader();
$reader->open('large_file.xml');
while ($reader->read()) {
if ($reader->nodeType === Reader::ELEMENT && $reader->name === 'record') {
// Process each record incrementally
}
}
Deprecation Warnings
The package is archived and may not support all PHP 8.x features. Test thoroughly or use a polyfill like php-compat:
composer require php-compat/php-compat
Namespace Conflicts
Avoid naming collisions with Laravel’s Xml helpers (e.g., collect()->xml()). Prefix usage:
$zendXml = new \Zend\Xml\Xml(); // Explicit namespace
$security = new \Zend\Xml\Security(); // Explicit namespace
Memory Issues
Parsing large XML files with Xml::fromString() can exhaust memory. Prefer Reader for streaming.
Attribute Handling
Attributes are accessed via getAttribute(), but child nodes require getElementsByTagName():
// Correct:
$value = $node->getAttribute('attr');
$child = $node->getElementsByTagName('child')->item(0);
HTML Sanitization Limitations (New in 1.2.0)
Security::scanHtml() may return false if the HTML is malformed or unsafe. Always check the return type:
$result = Security::scanHtml($html);
if ($result === false) {
// Handle unsafe HTML
}
Validate XML Structure
Use libxml_use_internal_errors() to catch parsing errors:
libxml_use_internal_errors(true);
$xml = Xml::fromString($data);
if ($xml === false) {
$errors = libxml_get_errors();
// Handle errors
}
Pretty-Print XML
Use DOMDocument for debugging:
$dom = new \DOMDocument();
$dom->loadXML($xmlString);
echo $dom->saveXML();
Debugging HTML Sanitization (New in 1.2.0)
Check the return type of Security::scanHtml() to ensure successful sanitization:
$sanitized = Security::scanHtml($html);
if ($sanitized instanceof \SimpleXMLElement) {
echo "HTML sanitized successfully!";
} else {
echo "HTML sanitization failed or returned a DOMDocument.";
}
Custom Writers/Readers
Extend Zend\Xml\Writer or Reader for domain-specific logic:
class CustomWriter extends Writer {
public function writeCustomElement($name, $data) {
$this->startElement($name);
$this->writeAttribute('custom', $data);
$this->endElement();
}
}
Laravel Events Trigger events on XML parsing/completion or HTML sanitization:
event(new XmlParsed($xml, $source));
event(new HtmlSanitized($html, $sanitizedXml));
Caching Parsed XML
Cache parsed Xml objects in Laravel’s cache:
$cacheKey = 'xml_' . md5($xmlString);
$xml = Cache::remember($cacheKey, 3600, function () use ($xmlString) {
return Xml::fromString($xmlString);
});
Custom Security Rules (New in 1.2.0)
Extend Security::scanHtml() behavior by subclassing and overriding methods:
class CustomSecurity extends \Zend\Xml\Security {
public function scanHtml(string $html, DOMDocument $dom = null, int $libXmlConstants = 0) {
// Custom logic before/after sanitization
$result = parent::scanHtml($html, $dom, $libXmlConstants);
// Additional processing
return $result;
}
}
How can I help you explore Laravel packages today?