mtownsend/request-xml
Adds simple XML response parsing to Laravel's HTTP client. Call xml() on responses to convert XML into arrays, making XML APIs as easy to work with as JSON. Lightweight and Laravel-friendly for legacy or SOAP-style endpoints.
Architecture fit. This is a micro-package that fills a genuine gap in Laravel's request handling. Laravel's Request gives first-class support for JSON payloads ($request->json(), $request->input() with content negotiation) but has never shipped a first-class XML path. mtownsend/request-xml registers a macro on the Request facade so a controller can call $request->xml() and receive the parsed body as a PHP array, mirroring the ergonomics developers already expect from ->json(). It is additive by design — no service container bindings, no middleware, no config publishing — which makes it architecturally inert in the best sense: it slots into the existing request lifecycle without changing it. Release 2.7.0 does not alter this: it is a framework-compatibility bump only (Laravel 13.x support, PR #16), with no new API surface, no new dependencies, and no architectural change.
Integration feasibility. In a standard Laravel application the cost is a single composer require plus auto-discovery of the service provider; there is no published config, no migrations, and no bootstrapping. The only hard runtime dependency of note is ext-simplexml, which is bundled in virtually every PHP build but is occasionally absent in hardened or minimal container images and must be confirmed in the deployment manifest. For applications that already accept XML from third parties (SOAP-adjacent partners, payment gateways, EDI front-ends, RSS/Atom ingestion), this removes a recurring homegrown-parser chore. The 2.7.0 release widens the viable framework window rather than narrowing it, so it does not change the integration cost — but it does mean the package now tracks a Laravel major we may not yet be running, which is a planning signal for our own upgrade roadmap rather than a blocker.
Technical risk. The dominant risk is not code quality but the attack surface that XML inherently carries. Any implementation built on SimpleXML must be explicitly hardened against XML External Entity (XXE) injection, entity-expansion ("billion laughs") denial of service, and external DTD retrieval, typically by parsing with LIBXML_NONET and by never enabling LIBXML_NOENT. Beyond security, expect behavioral gaps the macro will not smooth over: XML namespaces collapse or collide when mapped to a flat array, attributes have no natural representation, mixed content and repeated sibling elements need explicit conventions (item singular vs. items plural is a classic SimpleXML-to-array artifact), and CDATA and character-encoding handling depend on LIBXML_NOCDATA. Large documents will be materialized fully in memory by SimpleXML, so the package is unsuitable for multi-megabyte payloads without a streaming parser (XMLReader) instead. On maintainership: 2.7.0 was contributed by an outside party (Laravel Shift) and merged by the maintainer, which is a modest positive signal for responsiveness and bus-factor, but the project remains effectively single-maintainer with a small user base. The MIT license and the package's ~100 lines of logic keep a vendored fork trivial.
Key questions.
LIBXML_NONET and without LIBXML_NOENT, and is that verifiable in the source before adoption? (Re-verify on the 2.7.0 tag — the release is compatibility-scoped, so flags should be unchanged, but confirm.)null, or an empty array — and is it documented?Content-Type/Accept headers before parsing, or does it parse the body unconditionally?Request::macro) compatible with the versions we actually run?Stack fit. Targets any Laravel application from the 5.5+ era onward that uses package auto-discovery; the macro pattern is stable across those versions. It is a natural fit where XML enters through HTTP request bodies specifically — not for XML responses (use a resource/serializer), not for XML consumed from queues or files (use a dedicated parser), and not for SOAP (use a WSDL client). Scoping it to inbound HTTP XML keeps the blast radius small.
Migration path. Adoption is additive rather than substitutive. Existing controllers that call simplexml_load_string($request->getContent()) or a private XmlRequest trait can be migrated one endpoint at a time to $request->xml(), with a small adapter layer if the legacy helper returns a different shape. Because the package does not alter global request behavior, a partial migration is safe; there is no cutover moment. Reversing course is equally cheap: vendor the macro into AppServiceProvider and drop the dependency. 2.7.0 requires no migration of its own — it is a drop-in from 2.6.0.
Compatibility. Verify four things before adopting: the composer constraint against our Laravel major, the presence of ext-simplexml in every runtime image (web, queue worker, CLI), the absence of an existing xml() macro in AppServiceProvider or a dependent package, and — new for this release — that upgrading to 2.7.0 does not raise the PHP floor above what our images provide. If any conflict exists, the macro pattern means the last registration wins silently — a subtle source of environment-specific bugs, so a smoke test in staging is warranted. Treat the Laravel 13 support as forward-looking rather than as a reason to upgrade now: pin 2.7.0 on the current framework major and let it carry us through the next Laravel upgrade without a package swap.
Sequencing. (1) Security review of the parser invocation — this gates everything else. (2) A single low-risk endpoint pilot behind a feature flag, with schema fixtures for happy-path and malformed payloads. (3) Codify the array-shape contract in tests so a future package upgrade or a switch to a first-party parser is caught by CI. (4) Broaden adoption endpoint by endpoint, and standardize on a single XmlRequest form-request base class that wraps the macro and applies validation — that class, not the package, becomes the durable abstraction. Where this release changes the sequence: fold the 2.6.0 → 2.7.0 bump into the next routine dependency cycle (it is a no-behavior-change minor) rather than treating it as a project, and note in the upgrade runbook that the package is now cleared for Laravel 13 so it drops off the framework-upgrade blocker list.
Maintenance. Near-zero ongoing maintenance: no configuration to drift, no database surface, no scheduled work. The real maintenance obligation is upstream — tracking that the maintainer keeps pace with new Laravel majors and PHP releases. Release 2.7.0 is direct evidence that this is happening: Laravel 13 support landed, contributed by Laravel Shift and merged by the maintainer. That is the single most reassuring maintenance datapoint we have received, and it materially lowers the "package goes quiet across a framework upgrade" scenario that previously argued for pinning and vendoring. The residual caveat is that a one-off compatibility PR is not a support commitment — there is still no release cadence, and a future Laravel major could sit unaddressed for months. Mitigate by pinning the version, vendoring the macro into application code if the dependency becomes a release blocker, and treating the MIT license as permission to own it. Total code surface remains small enough to audit in an afternoon.
Support. Community-only: GitHub issues and the README. There is no commercial backing, no CVE-watch process, and no coordinated disclosure channel. For a TPM this shifts responsibility for security monitoring onto our own vulnerability management — we must watch for XML-parser CVEs in PHP itself and re-audit the macro's libxml flags on any upgrade. Note that a compatibility bump is a common vector for subtle regressions in unglamorous code: widen the smoke test slightly on 2.7.0 to cover the malformed-payload path, not just the happy path, even though the changelog claims no behavioral change.
Scaling. Parsing cost is proportional to payload size and is negligible at typical API scales (kilobytes). The SimpleXML tree-per-request model becomes a memory and latency concern only at the multi-megabyte end; if the roadmap includes bulk XML ingestion, plan for a streaming parser and treat this package as scoped to small request bodies. Horizontal scaling is unaffected — the package is stateless and adds no shared resources. Watch for CPU amplification from entity-expansion attacks, which is why request body size limits at the ingress layer are a required companion control, not an optional one.
Failure modes. Malformed XML producing an unhandled exception is the most likely production incident — wrap the call in a form request or middleware that returns a 400 with a structured error. XXE and entity-expansion are the highest-severity modes and must be closed at the parser, not just at the WAF. Schema drift from partners (new namespaces, changed cardinality) will surface as silently missing keys in the parsed array rather than as errors, so contract tests against sample payloads are essential. Macro collision or a missing ext-simplexml in a newly provisioned container will fail loudly at boot or first request — both are caught by the pilot and by CI. The 2.7.0-specific failure mode to watch is composer resolution: if the widened Laravel 13 constraint came with a tightened PHP constraint, a container rebuild can fail at install time with no code change on our side, so pin the lockfile and rebuild-test the image once after bumping.
Ramp-up. Essentially zero for any Laravel developer: $request->xml() reads like $request->json(), and the mental model transfers directly. The onboarding cost is not the API but the output-shape semantics — developers must learn how attributes, namespaces, and repeated elements map to arrays, and should be pointed at fixtures rather than documentation alone. Budget half a day of engineering time for the security review and pilot, and treat the wrapper form-request class as the team's shared vocabulary so the package itself stays an implementation detail. This release adds no new concepts for the team to learn.
How can I help you explore Laravel packages today?