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 Runtime Laravel Package

goetas-webservices/xsd2php-runtime

Runtime support for code generated by goetas-webservices/xsd2php. Provides the base classes and helpers needed to use PHP classes generated from XML Schema definitions, simplifying integration and serialization in PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a runtime library — it contains no generator, no console command, and no Laravel service provider. It exists so that PHP classes produced by goetas-webservices/xsd2php can be autoloaded and (de)serialized in production. Your first steps are therefore about splitting generator from runtime.

  1. Install the runtime in production, the generator in dev:

    composer require goetas-webservices/xsd2php-runtime
    composer require --dev goetas-webservices/xsd2php
    

    Keep them roughly in step — generated code from a newer generator may reference handler/base types that an older runtime does not ship (you'll see Class not found or "no handler for type" errors).

  2. Look first at what the generator actually emitted, not at this package. Open a couple of generated classes and note:

    • the root PHP namespace and directory (from your xsd2php config),
    • which @JMS\Type(...) / @JMS\XmlNamespace annotations appear on properties — these tell you which runtime handlers you must register,
    • any \DateTime, \DateInterval, SimpleXMLElement, or interface references in use statements. Those are the runtime's surface area.
  3. Generate one small schema to prove the pipeline end to end. Use the xsd2php console binary (or the Xsd2Php class programmatically) with a YAML/array config mapping XSD namespaces to PHP namespaces:

    # xsd2php.yaml (build-time only)
    my_schema:
      xsd: /path/to/schema.xsd
      php: App\Generated\MySchema
    

    Then composer dump-autoload after pointing PSR-4 at the output directory. Nothing in this package wires autoloading for you.

  4. First real use case: parse an inbound XML document into typed objects.

    use JMS\Serializer\SerializerBuilder;
    use JMS\Serializer\Handler\HandlerRegistryInterface;
    use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\BaseTypesHandler;
    use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\XmlSchemaDateHandler;
    
    $serializer = SerializerBuilder::create()
        ->configureHandlers(function (HandlerRegistryInterface $handlers) {
            $handlers->registerSubscribingHandler(new BaseTypesHandler());
            $handlers->registerSubscribingHandler(new XmlSchemaDateHandler());
        })
        ->setCacheDir(storage_path('framework/cache/jms'))
        ->build();
    
    $order = $serializer->deserialize($xmlString, App\Generated\MySchema\OrderType::class, 'xml');
    

    The XSD-specific types (xs:date, xs:dateTime, xs:time, xs:duration, binary types) are handled by those handlers, not by JMS's defaults. addDefaultHandlers(true) alone is not sufficient.

Implementation Patterns

Build-step generation, committed or cached output. Treat generated classes like compiled assets. A typical Laravel setup:

  • app/Generated/ (or generated/) as the target, registered in composer.json under autoload.psr-4.
  • An Artisan command xsd:generate that shells out to vendor/bin/xsd2php (or calls the generator class directly) so developers and CI use one code path.
  • Run generation before composer dump-autoload, and fail the CI build if regeneration produces a diff — that catches schemas that changed without committed code being updated.

Bind a single configured serializer in a service provider. JMS's SerializerBuilder and metadata parsing are expensive; build once and share it.

$this->app->singleton(SerializerInterface::class, function () {
    return SerializerBuilder::create()
        ->configureHandlers(function (HandlerRegistryInterface $h) {
            $h->registerSubscribingHandler(new BaseTypesHandler());
            $h->registerSubscribingHandler(new XmlSchemaDateHandler());
        })
        ->setCacheDir(storage_path('framework/cache/jms'))
        ->build();
});

Point setCacheDir at a writable path and clear it on deploy (or key it by release) — stale JMS metadata after a regeneration is the most common "why is this field null" bug.

Wrap the serializer behind a domain gateway. Do not scatter App\Generated\... class names through controllers. A small service per integration (InvoiceXmlGateway, ShippingStatusMapper) that accepts a string, validates, deserializes, and returns your own DTOs keeps the generated namespace an implementation detail you can regenerate freely.

Validate against the XSD before deserializing. JMS will happily produce a half-populated object from garbage XML. A DOMDocument::schemaValidate() pass turns "silently wrong data" into a clear exception with line numbers.

Convert typed objects to array/JSON for your API layer. JMS can serialize to json from the same objects, which makes generated models a convenient internal representation:

$json = $serializer->serialize($order, 'json', SerializationContext::create()->setSerializeNull(false));

Round-trip tests. Commit a few fixture XML files under tests/fixtures/xsd/. Test deserialize -> serialize -> C14N and compare canonicalized XML. That single test catches handler misregistration, namespace drift, and date-format regressions better than anything else.

If you're doing SOAP, goetas-webservices/soap-client builds on this runtime and its handler classes; register your handlers on that client's serializer too, or date fields will arrive as raw strings.

Gotchas and Tips

  • The runtime is not the generator. Never add xsd2php to require "just in case" — it pulls code-generation dependencies into production and can conflict on transitive packages.
  • No auto-discovery. There is no facade, config file, or service provider in this package. If anything looks "unwired," it's your wiring.
  • Handler names move between majors. XmlSchemaDateHandler / BaseTypesHandler live under GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\. Before upgrading, list vendor/goetas-webservices/xsd2php-runtime/src/Jms/Handler/ — a renamed or removed handler shows up as a runtime Type "... " is not registered error only on the specific fields that use it.
  • Wrong namespace URI = silent nulls. Deserialization matches on the XML namespace URI, not the prefix. A sender using ns2: is fine; a sender using a slightly different URI (or the unqualified default namespace) produces empty objects without an error. Log the raw payload on any "all fields null" result.
  • minOccurs="0" and maxOccurs="unbounded" semantics. Optional repeated elements deserialize to arrays that may be null or empty; always guard before foreach. Optional scalars may be null even when you expected a default.
  • Date/time fidelity. xs:date and xs:dateTime are different generated types; timezone offsets and fractional seconds are preserved inconsistently. Normalize to UTC in your gateway layer, not in the generated classes.
  • xs:any / xs:anyType / wildcards. These land as generic DOM/SimpleXMLElement content. Treat as untrusted — never evaluate or pass through to templates, and strip them if you only need a few known fields.
  • XXE and entity expansion. When you parse the raw XML yourself for validation or XPath, disable external entities (libxml_disable_entity_loader(true) on older PHP, or the LIBXML_NONET flag) — Laravel gives you nothing here by default.
  • Unknown-element strictness. JMS ignores unexpected XML by default. If you need to detect schema drift, enable strict deserialization per-integration rather than globally; global strict mode tends to break on harmless vendor extensions.
  • Queue workers and Octane. A singleton serializer is fine, but the JMS metadata cache on disk can go stale across a deploy in a long-running worker. Restart workers on deploy and clear the serializer cache directory as part of the release step.
  • Large generated trees. Generate into a dedicated directory with its own PSR-4 root and exclude it from static analysis / code style tools (or set them to a lower level); the generator's output is not idiomatic for PHPStan's strict rules and will dominate your reports.
  • Extension points are JMS's, not this package's. Custom scalar formats, naming strategies, and post-deserialization hooks all go through a custom SubscribingHandlerInterface implementation or a JMS event listener registered on your builder — subclassing generated classes is a dead end because they get overwritten on the next generation run.
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