added TypeRepository managing all mappings from php to xml types; removed classmap generation from SoapServerFactory, this will be done by TypeRepository::createComplexTypeMap(...);

Conflicts:

	Resources/config/webservice.xml
	Soap/SoapServerFactory.php
	Util/QName.php
	WebServiceContext.php
This commit is contained in:
Christian Kerl 2011-07-17 15:08:46 +02:00 committed by Francis Besset
parent 5da442b716
commit 76e7f42ccb
7 changed files with 170 additions and 57 deletions

View File

@ -0,0 +1,91 @@
<?php
/*
* This file is part of the WebServiceBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Bundle\WebServiceBundle\Converter;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\Util\Assert;
use Bundle\WebServiceBundle\Util\QName;
use Bundle\WebServiceBundle\Util\String;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class TypeRepository
{
const ARRAY_SUFFIX = '[]';
private $xmlNamespaces = array();
private $defaultTypeMap = array();
public function addXmlNamespace($prefix, $url)
{
$this->xmlNamespaces[$prefix] = $url;
}
public function getXmlNamespace($prefix)
{
return $this->xmlNamespaces[$prefix];
}
public function addDefaultTypeMapping($phpType, $xmlType)
{
Assert::thatArgumentNotNull('phpType', $phpType);
Assert::thatArgumentNotNull('xmlType', $xmlType);
$this->defaultTypeMap[$phpType] = $this->getQName($xmlType);
}
public function fixTypeInformation(ServiceDefinition $definition)
{
$typeMap = $this->defaultTypeMap;
foreach($definition->getAllTypes() as $type) {
$phpType = $type->getPhpType();
$xmlType = $type->getXmlType();
if (null === $phpType) {
throw new \InvalidArgumentException();
}
if (null === $xmlType) {
if (!isset($typeMap[$phpType])) {
$parts = explode('\\', $phpType);
$xmlTypeName = ucfirst(end($parts));
if (String::endsWith($phpType, self::ARRAY_SUFFIX)) {
$xmlTypeName = str_replace(self::ARRAY_SUFFIX, 'Array', $xmlTypeName);
}
$typeMap[$phpType] = new QName($definition->getNamespace(), $xmlTypeName);
}
$xmlType = $typeMap[$phpType];
} else {
$xmlType = $this->getQName($xmlType);
}
$type->setXmlType((string) $xmlType);
}
}
private function getQName($xmlType)
{
if (QName::isPrefixedQName($xmlType)) {
return QName::fromPrefixedQName($xmlType, array($this, 'getXmlNamespace'));
} else {
return QName::fromPackedQName($xmlType);
}
}
public function createComplexTypeMap(ServiceDefinition $definition)
{
}
}

View File

@ -65,9 +65,9 @@ class WebServiceExtension extends Extension
$arguments = array(); $arguments = array();
foreach($this->contextArguments as $i => $argument) { foreach($this->contextArguments as $i => $argument) {
if (in_array($i, array(1, 3, 4))) { if (in_array($i, array(1, 2, 3))) {
$argument = new Reference($argument->__toString().$bindingSuffix); $argument = new Reference($argument->__toString().$bindingSuffix);
} elseif (5 === $i) { } elseif (6 === $i) {
$argument = array_merge($argument, $config); $argument = array_merge($argument, $config);
} else { } else {
$argument = new Reference($argument->__toString()); $argument = new Reference($argument->__toString());

View File

@ -13,15 +13,17 @@
<parameter key="webservice.binder.response.documentwrapped.class">Bundle\WebServiceBundle\ServiceBinding\DocumentLiteralWrappedResponseMessageBinder</parameter> <parameter key="webservice.binder.response.documentwrapped.class">Bundle\WebServiceBundle\ServiceBinding\DocumentLiteralWrappedResponseMessageBinder</parameter>
<parameter key="webservice.definition.dumper.wsdl.rpcliteral.class">Bundle\WebServiceBundle\ServiceDefinition\Dumper\WsdlDumper</parameter> <parameter key="webservice.definition.dumper.wsdl.rpcliteral.class">Bundle\WebServiceBundle\ServiceDefinition\Dumper\WsdlDumper</parameter>
<parameter key="webservice.converter.repository.class">Bundle\WebServiceBundle\Converter\ConverterRepository</parameter> <parameter key="webservice.converter.repository.class">Bundle\WebServiceBundle\Converter\ConverterRepository</parameter>
<parameter key="webservice.type.repository.class">Bundle\WebServiceBundle\Converter\TypeRepository</parameter>
</parameters> </parameters>
<services> <services>
<service id="webservice.context" class="%webservice.context.class%" abstract="true"> <service id="webservice.context" class="%webservice.context.class%" abstract="true">
<argument type="service" id="webservice.definition.loader"/> <argument type="service" id="webservice.definition.loader"/>
<argument type="service" id="webservice.definition.dumper.wsdl"/> <argument type="service" id="webservice.definition.dumper.wsdl"/>
<argument type="service" id="webservice.converter.repository"/>
<argument type="service" id="webservice.binder.request"/> <argument type="service" id="webservice.binder.request"/>
<argument type="service" id="webservice.binder.response"/> <argument type="service" id="webservice.binder.response"/>
<argument type="service" id="webservice.type.repository"/>
<argument type="service" id="webservice.converter.repository"/>
<argument type="collection"> <argument type="collection">
<argument key="cache_dir">%webservice.cache_dir%</argument> <argument key="cache_dir">%webservice.cache_dir%</argument>
<argument key="debug">%kernel.debug%</argument> <argument key="debug">%kernel.debug%</argument>
@ -38,9 +40,28 @@
<service id="webservice.definition.dumper.wsdl.rpcliteral" class="%webservice.definition.dumper.wsdl.rpcliteral.class%" /> <service id="webservice.definition.dumper.wsdl.rpcliteral" class="%webservice.definition.dumper.wsdl.rpcliteral.class%" />
<service id="webservice.converter.repository" class="%webservice.converter.repository.class%"> <service id="webservice.converter.repository" class="%webservice.converter.repository.class%" />
<call method="registerTypeConverterServices">
<argument type="service" id="service_container" /> <service id="webservice.type.repository" class="%webservice.type.repository.class%">
<call method="addXmlNamespace">
<argument>xsd</argument>
<argument>http://www.w3.org/2001/XMLSchema</argument>
</call>
<call method="addDefaultTypeMapping">
<argument>string</argument>
<argument>xsd:string</argument>
</call>
<call method="addDefaultTypeMapping">
<argument>int</argument>
<argument>xsd:int</argument>
</call>
<call method="addDefaultTypeMapping">
<argument>bool</argument>
<argument>xsd:boolean</argument>
</call>
<call method="addDefaultTypeMapping">
<argument>float</argument>
<argument>xsd:float</argument>
</call> </call>
</service> </service>
</services> </services>

View File

@ -109,4 +109,22 @@ class ServiceDefinition
{ {
$this->headers->addAll($headers); $this->headers->addAll($headers);
} }
/**
* @return array
*/
public function getAllTypes()
{
$types = array();
foreach($this->methods as $method) {
foreach($method->getArguments() as $argument) {
$types[] = $argument->getType();
}
$types[] = $method->getReturn();
}
return $types;
}
} }

View File

@ -11,24 +11,21 @@
namespace Bundle\WebServiceBundle\Soap; namespace Bundle\WebServiceBundle\Soap;
use Bundle\WebServiceBundle\Converter\ConverterRepository; use Bundle\WebServiceBundle\Converter\ConverterRepository;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\ServiceDefinition\Type;
use Bundle\WebServiceBundle\Util\QName;
/** /**
* @author Christian Kerl <christian-kerl@web.de> * @author Christian Kerl <christian-kerl@web.de>
*/ */
class SoapServerFactory class SoapServerFactory
{ {
private $definition;
private $converters;
private $wsdlFile; private $wsdlFile;
private $classmap;
private $converters;
private $debug; private $debug;
public function __construct(ServiceDefinition $definition, $wsdlFile, ConverterRepository $converters, $debug = false) public function __construct($wsdlFile, array $classmap, ConverterRepository $converters, $debug = false)
{ {
$this->definition = $definition;
$this->wsdlFile = $wsdlFile; $this->wsdlFile = $wsdlFile;
$this->classmap = $classmap;
$this->converters = $converters; $this->converters = $converters;
$this->debug = $debug; $this->debug = $debug;
} }
@ -38,7 +35,7 @@ class SoapServerFactory
return new \SoapServer( return new \SoapServer(
$this->wsdlFile, $this->wsdlFile,
array( array(
'classmap' => $this->createSoapServerClassmap(), 'classmap' => $this->classmap,
'typemap' => $this->createSoapServerTypemap($request, $response), 'typemap' => $this->createSoapServerTypemap($request, $response),
'features' => SOAP_SINGLE_ELEMENT_ARRAYS, 'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
'cache_wsdl' => $this->debug ? WSDL_CACHE_NONE : WSDL_CACHE_DISK, 'cache_wsdl' => $this->debug ? WSDL_CACHE_NONE : WSDL_CACHE_DISK,
@ -65,36 +62,4 @@ class SoapServerFactory
return $typemap; return $typemap;
} }
private function createSoapServerClassmap()
{
$classmap = array();
foreach($this->definition->getHeaders() as $header) {
$this->addSoapServerClassmapEntry($classmap, $header->getType());
}
foreach($this->definition->getMethods() as $method) {
foreach($method->getArguments() as $arg) {
$this->addSoapServerClassmapEntry($classmap, $arg->getType());
}
}
return $classmap;
}
private function addSoapServerClassmapEntry(&$classmap, Type $type)
{
// TODO: fix this hack
if(null === $type->getXmlType()) return;
$xmlType = QName::fromPackedQName($type->getXmlType())->getName();
$phpType = $type->getPhpType();
if(isset($classmap[$xmlType]) && $classmap[$xmlType] != $phpType) {
// log warning
}
$classmap[$xmlType] = $phpType;
}
} }

View File

@ -18,6 +18,20 @@ class QName
private $namespace; private $namespace;
private $name; private $name;
public static function isPrefixedQName($qname)
{
return false !== strpos($qname, ':') ? true : false;
}
public static function fromPrefixedQName($qname, $resolveNamespacePrefixCallable)
{
Assert::thatArgument('qname', self::isPrefixedQName($qname));
list($prefix, $name) = explode(':', $qname);
return new self(call_user_func($resolveNamespacePrefixCallable, $prefix), $name);
}
public static function fromPackedQName($qname) public static function fromPackedQName($qname)
{ {
Assert::thatArgument('qname', preg_match('/^\{(.+)\}(.+)$/', $qname, $matches)); Assert::thatArgument('qname', preg_match('/^\{(.+)\}(.+)$/', $qname, $matches));
@ -28,7 +42,7 @@ class QName
public function __construct($namespace, $name) public function __construct($namespace, $name)
{ {
$this->namespace = $namespace; $this->namespace = $namespace;
$this->name = $name; $this->name = $name;
} }
public function getNamespace() public function getNamespace()

View File

@ -11,6 +11,7 @@
namespace Bundle\WebServiceBundle; namespace Bundle\WebServiceBundle;
use Bundle\WebServiceBundle\Converter\ConverterRepository; use Bundle\WebServiceBundle\Converter\ConverterRepository;
use Bundle\WebServiceBundle\Converter\TypeRepository;
use Bundle\WebServiceBundle\ServiceBinding\MessageBinderInterface; use Bundle\WebServiceBundle\ServiceBinding\MessageBinderInterface;
use Bundle\WebServiceBundle\ServiceBinding\ServiceBinder; use Bundle\WebServiceBundle\ServiceBinding\ServiceBinder;
use Bundle\WebServiceBundle\ServiceDefinition\Dumper\DumperInterface; use Bundle\WebServiceBundle\ServiceDefinition\Dumper\DumperInterface;
@ -26,9 +27,10 @@ use Symfony\Component\Config\Loader\LoaderInterface;
*/ */
class WebServiceContext class WebServiceContext
{ {
private $converterRepository;
private $requestMessageBinder; private $requestMessageBinder;
private $responseMessageBinder; private $responseMessageBinder;
private $typeRepository;
private $converterRepository;
private $wsdlFileDumper; private $wsdlFileDumper;
@ -38,15 +40,16 @@ class WebServiceContext
private $serviceBinder; private $serviceBinder;
private $serverFactory; private $serverFactory;
public function __construct(LoaderInterface $loader, DumperInterface $dumper, ConverterRepository $converterRepository, MessageBinderInterface $requestMessageBinder, MessageBinderInterface $responseMessageBinder, array $options = array()) public function __construct(LoaderInterface $loader, DumperInterface $dumper, MessageBinderInterface $requestMessageBinder, MessageBinderInterface $responseMessageBinder, TypeRepository $typeRepository, ConverterRepository $converterRepository, array $options) {
{
$this->loader = $loader; $this->loader = $loader;
$this->wsdlFileDumper = $dumper; $this->wsdlFileDumper = $dumper;
$this->converterRepository = $converterRepository;
$this->requestMessageBinder = $requestMessageBinder; $this->requestMessageBinder = $requestMessageBinder;
$this->responseMessageBinder = $responseMessageBinder; $this->responseMessageBinder = $responseMessageBinder;
$this->typeRepository = $typeRepository;
$this->converterRepository = $converterRepository;
$this->options = $options; $this->options = $options;
} }
@ -60,6 +63,8 @@ class WebServiceContext
$this->serviceDefinition = $this->loader->load($this->options['resource'], $this->options['resource_type']); $this->serviceDefinition = $this->loader->load($this->options['resource'], $this->options['resource_type']);
$this->serviceDefinition->setName($this->options['name']); $this->serviceDefinition->setName($this->options['name']);
$this->serviceDefinition->setNamespace($this->options['namespace']); $this->serviceDefinition->setNamespace($this->options['namespace']);
$this->typeRepository->fixTypeInformation($this->serviceDefinition);
} }
return $this->serviceDefinition; return $this->serviceDefinition;
@ -67,15 +72,14 @@ class WebServiceContext
public function getWsdlFile($endpoint = null) public function getWsdlFile($endpoint = null)
{ {
$id = null !== $endpoint ? '.'. md5($endpoint) : ''; $file = sprintf('%s/%s.%s.wsdl', $this->options['cache_dir'], $this->options['name'], md5($endpoint));
$file = sprintf('%s/%s.wsdl', $this->options['cache_dir'], $this->options['name'].$id); $cache = new ConfigCache($file, $this->options['debug']);
$cache = new ConfigCache($file, true);
if(!$cache->isFresh()) { if(!$cache->isFresh()) {
$cache->write($this->wsdlFileDumper->dumpServiceDefinition($this->getServiceDefinition(), array('endpoint' => $endpoint))); $cache->write($this->wsdlFileDumper->dumpServiceDefinition($this->getServiceDefinition(), array('endpoint' => $endpoint)));
} }
return $file; return (string) $cache;
} }
public function getWsdlFileContent($endpoint = null) public function getWsdlFileContent($endpoint = null)
@ -95,7 +99,7 @@ class WebServiceContext
public function getServerFactory() public function getServerFactory()
{ {
if (null === $this->serverFactory) { if (null === $this->serverFactory) {
$this->serverFactory = new SoapServerFactory($this->getServiceDefinition(), $this->getWsdlFile(), $this->converterRepository, $this->options['debug']); $this->serverFactory = new SoapServerFactory($this->getWsdlFile(), array(), $this->converterRepository, $this->options['debug']);
} }
return $this->serverFactory; return $this->serverFactory;