saloonphp/xml-wrangler
XML Wrangler is a lightweight SaloonPHP plugin for working with XML in HTTP requests and responses. Easily build XML bodies, set the right headers, and parse XML responses into usable data for your Laravel or PHP API integrations.
Installation:
composer require saloonphp/xml-wrangler
Ensure your project uses PHP 8.1+ (recommended: 8.3+ for full feature support).
First Use Case: Parsing XML from a Saloon Response
use Saloon\Saloon;
use SaloonHttp\SaloonHttpFacade as Http;
use Saloonphp\XmlWrangler\XmlReader;
// Example: Fetch XML from an API
$response = Http::get('https://example.com/api/xml-endpoint');
// Parse the response
$data = XmlReader::fromResponse($response)
->query('//items/item') // XPath query
->map(fn ($node) => [
'id' => $node->getAttribute('id'),
'name' => $node->getContent(),
])
->all();
First Use Case: Generating XML for a Request
use Saloonphp\XmlWrangler\XmlWriter;
$xml = XmlWriter::make()
->root('request')
->element('user', [
'id' => '123',
])
->element('name', 'John Doe')
->element('email', 'john@example.com')
->toString();
// Use $xml in a Saloon request
$response = Http::post('https://example.com/api', [
'body' => $xml,
'headers' => ['Content-Type' => 'application/xml'],
]);
Where to Look First:
README.md for quick examples.src/XmlReader.php (parsing logic).src/XmlWriter.php (generation logic).src/Query.php (XPath and node traversal).tests/ for real-world usage patterns (e.g., XmlReaderTest.php, XmlWriterTest.php).Pattern: Use XmlReader::fromResponse() to parse Saloon responses with XPath queries.
$response = $this->client->send($request);
$items = XmlReader::fromResponse($response)
->query('//catalog/item')
->each(fn ($node) => [
'sku' => $node->getAttribute('sku'),
'price' => (float) $node->query('./price')->first()->getContent(),
])
->toArray();
Laravel Integration Tip:
Http or Saloon to auto-parse XML responses:
use SaloonHttp\SaloonHttpFacade as Http;
Http::macro('xml', function ($url, $method = 'GET') {
$response = Http::{$method}($url);
return XmlReader::fromResponse($response)->toArray();
});
Usage: Http::xml('https://example.com/feed').Pattern: Build XML dynamically using XmlWriter for API requests.
$xml = XmlWriter::make()
->root('soapenv:Envelope', ['xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/'])
->element('soapenv:Header')
->element('soapenv:Body')
->element('ns1:GetUserDetails', ['xmlns:ns1' => 'http://example.com'])
->element('userId', $userId)
->up()
->up()
->toString();
Tip: Use DTOs or arrays to generate XML:
$user = User::find(1);
$xml = XmlWriter::make()
->root('user')
->element('id', $user->id)
->element('name', $user->name)
->element('email', $user->email)
->toString();
Pattern: Map namespaces for XPath queries or XML generation.
// Parsing with namespaces
$data = XmlReader::fromString($xml)
->mapNamespaces([
'soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
'ns1' => 'http://example.com',
])
->query('//soap:Envelope/soap:Body/ns1:GetUserResponse')
->first();
// Generating with namespaces
$xml = XmlWriter::make()
->root('soap:Envelope', ['xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/'])
->element('soap:Header')
->element('soap:Body')
->element('ns1:GetUserDetails', ['xmlns:ns1' => 'http://example.com'])
->element('userId', '123')
->up()
->up()
->toString();
Pattern: Use XmlReader::fromStream() to avoid memory issues.
$stream = fopen('large_file.xml', 'r');
$reader = XmlReader::fromStream($stream);
foreach ($reader->query('//item') as $node) {
// Process each node individually
$data[] = $node->getContent();
}
fclose($stream);
Laravel Tip: Combine with Laravel’s Storage facade:
$stream = Storage::disk('s3')->readStream('large-export.xml');
$reader = XmlReader::fromStream($stream);
Pattern: Use map() and each() to transform XML nodes into arrays or collections.
$users = XmlReader::fromString($xml)
->query('//users/user')
->map(fn ($node) => [
'id' => $node->getAttribute('id'),
'name' => $node->query('./name')->first()->getContent(),
])
->toCollection();
Tip: Chain with Laravel Collections for further processing:
$filteredUsers = $users->filter(fn ($user) => $user['id'] > 100);
Pattern: Generate XML and write it to a file or return it in a Laravel response.
// Write to a file
$xml = XmlWriter::make()->root('data')->element('item', 'value')->toString();
Storage::put('export.xml', $xml);
// Return in a Laravel response
return response($xml, 200, ['Content-Type' => 'application/xml']);
Concerns\HasResponse to add XML parsing:
use Saloonphp\XmlWrangler\XmlReader;
class XmlResponse extends SaloonResponse
{
public function xml(): array
{
return XmlReader::fromResponse($this)->toArray();
}
}
Usage:
$response = $this->client->send($request);
$data = $response->xml();
$user = XmlReader::fromString($xml)
->query('//user')
->first()
->map(fn ($key, $value) => [
'attribute' => 'user_' . snake_case($key),
'value' => $value,
])
->toArray();
return User::create($user);
public function handle($request, Closure $next)
{
if ($request->wantsXml()) {
$request->merge(XmlReader::fromString($request->getContent())->toArray());
}
return $next($request);
}
$response = $this->get('/api/export');
$xml = XmlReader::fromResponse($response);
expect($xml->query('//items/item')->count())->toBe(3);
// ❌ Fails if namespace isn't mapped
$reader->query('//soap:Envelope');
// ✅ Works with mapped namespace
$reader->mapNamespaces(['soap' => 'http://schemas.xmlsoap.org/soap/envelope/'])
->
How can I help you explore Laravel packages today?