goetas-webservices / xsd2php

Convert XSD into PHP classes and JMS serializer definitions
MIT License
238 stars 92 forks source link
code-generation custom-serialization jms-metadata php xml xml-namespaces xsd xsd-files

UKRAINE NEEDS YOUR HELP NOW!

On 24 February 2022, Russian President Vladimir Putin ordered an invasion of Ukraine by Russian Armed Forces.

Your support is urgently needed.

THANK YOU!

xsd2php

Build Status Code Coverage Scrutinizer Code Quality

Convert XSD into PHP classes.

With goetas-webservices/xsd2php you can convert any XSD/WSDL definition into PHP classes.

XSD2PHP can also generate JMS Serializer compatible metadata that can be used to serialize/unserialize the object instances.

Installation

There is one recommended way to install xsd2php via Composer:

  "require": {
      ..
      "goetas-webservices/xsd2php-runtime":"^0.2.2",
      ..
  },
  "require-dev": {
      ..
      "goetas-webservices/xsd2php":"^0.3",
      ..
  },

Usage

With this example we will convert OTA XSD definitions into PHP classes.

Suppose that you have all XSD files in /home/my/ota, first of all we need a configuration file (as example config.yml) that will keep all the namespace and directory mappings information.

# config.yml
# Linux Users: PHP Namespaces use back slash \ rather than a forward slash /
# So for destinations_php, the namespace would be TestNs\MyApp

xsd2php:
  namespaces:
    'http://www.example.org/test/': 'TestNs\MyApp'
  destinations_php: 
    'TestNs\MyApp': soap/src
#    'TestNs\MyApp': soap\src  #  on Windows

  destinations_jms:
    'TestNs\MyApp': soap/metadata
#    'TestNs\MyApp': soap\metadata  #  on Windows

#  Uncomment this section if you want to have also symfony/validator metadata to be generated from XSD    
#  destinations_validation:
#    'TestNs\MyApp': soap/validation
#    'TestNs\MyApp': soap\validation  #  on Windows

  aliases: # optional
    'http://www.example.org/test/':
      MyCustomXSDType:  'MyCustomMappedPHPType'
  naming_strategy: short # optional and default
  path_generator: psr4 # optional and default
#  known_locations: # optional
#    "http://www.example.org/test/somefile.xsd": somefile.xsd
#  known_namespace_locations: # optional
#    "urn:veloconnect:catalog-1.1": xsd/catalog-1.1.xsd
# configs_jms:  #optional
#   xml_cdata: false # Disables CDATA

Here is an explanation on the meaning of each parameter:

Generate PHP classes and JMS metadata info

vendor/bin/xsd2php convert config.yml /home/my/ota/OTA_Air*.xsd

This command will generate PHP classes and JMS metadata files for all the XSD files matching /home/my/ota/OTA_Air*.xsd and using the configuration available in config.yml

Serialize / Unserialize

XSD2PHP can also generate for you JMS Serializer metadata that you can use to serialize/unserialize the generated PHP class instances.

The parameter aliases in the configuration file, will instruct XSD2PHP to not generate any metadata information or PHP class for the {http://www.example.org/test/}MyCustomXSDType type. All reference to this type are replaced with the MyCustomMappedPHPType name.

You have to provide a custom serializer for this type/alis.

Here is an example on how to configure JMS serializer to handle custom types

<?php

use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\Handler\HandlerRegistryInterface;

use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\BaseTypesHandler;
use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\XmlSchemaDateHandler;

$serializerBuilder = SerializerBuilder::create();
$serializerBuilder->addMetadataDir('metadata dir', 'TestNs');
$serializerBuilder->configureHandlers(function (HandlerRegistryInterface $handler) use ($serializerBuilder) {
    $serializerBuilder->addDefaultHandlers();
    $handler->registerSubscribingHandler(new BaseTypesHandler()); // XMLSchema List handling
    $handler->registerSubscribingHandler(new XmlSchemaDateHandler()); // XMLSchema date handling

    // $handler->registerSubscribingHandler(new YourhandlerHere());
});

$serializer = $serializerBuilder->build();

// deserialize the XML into Demo\MyObject object
$object = $serializer->deserialize('<some xml/>', 'TestNs\MyObject', 'xml');

// some code ....

// serialize the Demo\MyObject back into XML
$newXml = $serializer->serialize($object, 'xml');

To disable the CDATA, configure JMS as so:

xsd2php:
  configs_jms:
    xml_cdata: false

Validation

<?php

use Symfony\Component\Validator\Validation;

// get the validator
$builder = Validation::createValidatorBuilder();
foreach (glob('soap/validation/*.yml') as $file) {
    $builder->addYamlMapping($file);
}
$validator =  $builder->getValidator();

// validate $object
$violations = $validator->validate($object, null, ['xsd_rules']);

Dealing with xsd:anyType or xsd:anySimpleType

If your XSD contains xsd:anyType or xsd:anySimpleType types you have to specify a handler for this.

When you generate the JMS metadata you have to specify a custom handler:

# config.yml

xsd2php:
  ...
  aliases: 
    'http://www.w3.org/2001/XMLSchema':
      anyType: 'MyCustomAnyTypeHandler'
      anySimpleType: 'MyCustomAnySimpleTypeHandler'

Now you have to create a custom serialization handler:

use JMS\Serializer\XmlSerializationVisitor;
use JMS\Serializer\XmlDeserializationVisitor;

use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\GraphNavigator;
use JMS\Serializer\VisitorInterface;
use JMS\Serializer\Context;

class MyHandler implements SubscribingHandlerInterface
{
    public static function getSubscribingMethods()
    {
        return array(
            array(
                'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
                'format' => 'xml',
                'type' => 'MyCustomAnyTypeHandler',
                'method' => 'deserializeAnyType'
            ),
            array(
                'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                'format' => 'xml',
                'type' => 'MyCustomAnyTypeHandler',
                'method' => 'serializeAnyType'
            )
        );
    }

    public function serializeAnyType(XmlSerializationVisitor $visitor, $data, array $type, Context $context)
    {
        // serialize your object here
    }

    public function deserializeAnyType(XmlDeserializationVisitor $visitor, $data, array $type)
    {
        // deserialize your object here
    }
}

Naming Strategy

There are two types of naming strategies: short and long. The default is short, this naming strategy can however generate naming conflicts.

The long naming strategy will suffix elements with Element and types with Type.

An XSD for instance with a type named User, a type named UserType, a root element named User and UserElement, will only work when using the long naming strategy.

Note

The code in this project is provided under the MIT license. For professional support contact goetas@gmail.com or visit https://www.goetas.com