Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Xsd2Php Laravel Package

goetas/xsd2php

goetas/xsd2php converts XML Schema and WSDL definitions into PHP classes, generating typed models and serialization mappings so you can work with SOAP/XML services in PHP without hand-writing boilerplate. It integrates with JMS Serializer and supports common XSD constructs.

Deep Wiki
Context7

Getting Started

goetas/xsd2php is a code generator: you point it at an XSD (or a set of them) and it emits plain PHP classes annotated for JMS Serializer, so you can hydrate them from XML and serialize them back. It is not a runtime XML mapper you call directly — the day-to-day value is in the generation step and the JMS annotations baked into the output.

Minimum setup:

composer require --dev goetas/xsd2php
composer require jms/serializer

The generator binary lands at vendor/bin/xsd2php. It is configured with a small YAML file (keys vary slightly by pinned version — always confirm against the README of the exact tag you install):

xsd2php:
  namespaces:
    'http://example.com/order/v1':
      alias: 'Order'
      phpNamespace: 'App\Xml\Order'
  destinations:
    classes: 'src/Xml/Order'
    metadata: 'src/Xml/Order/Metadata'

Then:

vendor/bin/xsd2php convert xsd2php.yml

Where to look first:

  1. The generated classes — open one and read the @XmlRoot, @XmlNamespace, @Type, @SerializedName, @XmlList annotations. That is the contract you will be working against.
  2. The bundle's Command/ directory to see exactly which CLI options and config keys your version supports.
  3. JMS Serializer's XmlSerializationVisitor docs, because every serialization quirk you hit will actually be a JMS quirk.

First use case: pick one small, real schema you already need to consume (an order ack, a status response, a webhook payload), generate classes, and round-trip a fixture:

use JMS\Serializer\SerializerBuilder;

$serializer = SerializerBuilder::create()->build();

$order = $serializer->deserialize($xmlString, \App\Xml\Order\Order::class, 'xml');
$xml   = $serializer->serialize($order, 'xml');

If deserialize returns an object and serialize produces XML that passes xmllint --schema schema.xsd, you understand the tool.

Implementation Patterns

Generator as a build step, output as checked-in code. Treat generation like a protobuf or migration step. Commit the generated classes, keep a composer generate-xml script or Makefile target, and regenerate only when the schema changes. This keeps diffs reviewable and removes a fragile dev dependency from CI.

"scripts": {
  "generate-xml": "xsd2php convert xsd2php.yml",
  "test:xml": "xmllint --noout --schema schema.xsd tests/fixtures/*.xml"
}

One PHP namespace per XML namespace. The namespaces map is the single most important piece of config. Keep the alias identical to the schema's canonical prefix and pin it, because it becomes the @XmlNamespace prefix in output. Renaming an alias later changes serialized XML and is a breaking change.

Keep generated DTOs on the infrastructure boundary. These classes are mechanical and often ugly (auto-generated names for anonymous complex types). Do not let them become your domain model. Wrap them in a small mapper:

final class OrderXmlMapper
{
    public function toDomain(\App\Xml\Order\Order $dto): Order { /* ... */ }
    public function fromDomain(Order $order): \App\Xml\Order\Order { /* ... */ }
}

Configure the serializer once, in a service provider. Use a filesystem metadata cache so JMS does not re-parse annotations per request:

$serializer = SerializerBuilder::create()
    ->setCacheDir(storage_path('framework/serializer'))
    ->setDebug(app()->environment('local'))
    ->build();

Schema-per-config for large integrations. Do not pile twenty XSDs into one YAML file. Generate each service's schema into its own namespace and run separate convert invocations. Collisions in auto-generated type names are far easier to resolve in isolation.

Test with golden fixtures, both directions. Keep a corpus of real payloads. Assert deserialize → serialize is semantically stable (canonicalize with xmllint --c14n before diffing) and that every fixture validates against the original XSD. This catches the annotation problems that unit tests on object graphs miss.

Soap/envelope wrapping is your job. The generator produces message bodies, not envelopes. If you are talking to a SOAP endpoint, build the <soap:Envelope> yourself or serialize into a pre-built DOM, rather than expecting the generated root element to be a valid request.

Escape hatch: read the XSD directly. When you only need validation or a couple of fields, skip generation and use the underlying goetas-webservices/xsd-reader (a transitive dependency) to introspect the schema. It is often less work than generating a 200-class tree for a two-field response.

Gotchas and Tips

  • Archived since 2016. This is the headline risk. It targets PHP 5.6/7.0-era Symfony Console, Doctrine annotations, and JMS Serializer. On PHP 8 you will hit deprecations, removed each()-era helpers, and annotation-vs-attribute mismatches in modern JMS. Prototype on your actual PHP version before committing to it, and strongly consider the maintained successor package goetas-webservices/xsd2php or a hand-written DTO + Symfony Serializer approach for new work.
  • Never hand-edit generated classes. The next convert silently overwrites you. If the generator gets something wrong, either fix the schema, post-process with a deterministic script (php-cs-fixer, a small AST rewriter for declare(strict_types=1)), or override by subclassing in your own namespace.
  • Namespace prefixes are the number one source of bugs. Serialized output is namespace-sensitive; a schema-valid payload with the wrong prefix or a missing xmlns:xsi will be rejected by strict consumers. Inspect the generated @XmlNamespace annotations and, when needed, control prefixes via a JMS SerializationContext.
  • nillable is not modelled faithfully. An xsi:nil="true" element typically deserializes to null and re-serializes as an omitted or empty element, not as xsi:nil. If the consumer requires nil semantics, handle it with a custom JMS handler or post-serialization DOM surgery.
  • <choice>, substitution groups, and xs:any are lossy. Choice becomes a bag of optional properties with no mutual-exclusion enforcement; substitution groups and wildcards are often dropped. Do not rely on generated classes for validation — validate with xmllint or DOMDocument::schemaValidate at the boundary.
  • Dates and decimals. xs:date/xs:dateTime may map to \DateTime with a fixed format annotation, or stay a string depending on the version. Timezone handling is a frequent silent bug — assert on exact string output, not just object equality.
  • Metadata cache must be cleared after regeneration. Stale JMS metadata cache is the classic "my new field is ignored" symptom. Wire php artisan cache:clear or delete the serializer cache dir into your generate script.
  • Debugging workflow: serialize to XML and eyeball it first ($serializer->serialize($obj, 'xml')), then diff against a known-good payload, then validate against the XSD. If deserialization returns an object with all-null fields, the problem is almost always a namespace or @SerializedName mismatch, not the XML itself.
  • Performance is generate-time, not runtime. Never invoke xsd2php in a request path. Regeneration on a large schema can take seconds and produce thousands of files; run it in CI or locally and commit the result.
  • Extension points are limited. There is no plugin API for custom type converters worth relying on. Realistically your options are: post-process generated files, wrap with mappers, or fork. Budget for a fork if you need this long-term.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource