From 8d3743d92872e3892062a3e628e665296558af78 Mon Sep 17 00:00:00 2001 From: Christian Kerl Date: Fri, 8 Oct 2010 14:24:42 +0200 Subject: [PATCH] added initial support for service binding --- DependencyInjection/WebServiceExtension.php | 35 +++++- Resources/config/services.xml | 23 +++- ...mentLiteralWrappedRequestMessageBinder.php | 26 +++++ ...entLiteralWrappedResponseMessageBinder.php | 16 +++ ServiceBinding/MessageBinderInterface.php | 18 ++++ .../RpcLiteralRequestMessageBinder.php | 22 ++++ .../RpcLiteralResponseMessageBinder.php | 13 +++ ServiceBinding/ServiceBinder.php | 101 ++++++++++++++++++ .../{ServiceHeader.php => Argument.php} | 4 +- ServiceDefinition/Header.php | 35 ++++++ ServiceDefinition/Loader/XmlFileLoader.php | 39 ++++--- .../{ServiceMethod.php => Method.php} | 19 +++- .../{ServiceType.php => Type.php} | 2 +- Soap/SoapRequest.php | 1 - Soap/SoapResponse.php | 3 + SoapKernel.php | 53 ++++----- Tests/SoapKernelTest.php | 23 ++-- Tests/fixtures/api-servicedefinition.xml | 14 +++ 18 files changed, 379 insertions(+), 68 deletions(-) create mode 100644 ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php create mode 100644 ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php create mode 100644 ServiceBinding/MessageBinderInterface.php create mode 100644 ServiceBinding/RpcLiteralRequestMessageBinder.php create mode 100644 ServiceBinding/RpcLiteralResponseMessageBinder.php create mode 100644 ServiceBinding/ServiceBinder.php rename ServiceDefinition/{ServiceHeader.php => Argument.php} (83%) create mode 100644 ServiceDefinition/Header.php rename ServiceDefinition/{ServiceMethod.php => Method.php} (59%) rename ServiceDefinition/{ServiceType.php => Type.php} (97%) create mode 100644 Tests/fixtures/api-servicedefinition.xml diff --git a/DependencyInjection/WebServiceExtension.php b/DependencyInjection/WebServiceExtension.php index 015591c..e4b4d9e 100644 --- a/DependencyInjection/WebServiceExtension.php +++ b/DependencyInjection/WebServiceExtension.php @@ -31,12 +31,19 @@ class WebServiceExtension extends Extension $configuration->setAlias('http_kernel', 'webservice.kernel'); } - if(!isset($config['definition'])) + if(isset($config['definition'])) + { + $this->registerServiceDefinitionConfig($config['definition'], $configuration); + } + else { throw new \InvalidArgumentException(); } - $this->registerServiceDefinitionConfig($config['definition'], $configuration); + if(isset($config['binding'])) + { + $this->registerServiceBindingConfig($config['binding'], $configuration); + } } protected function registerServiceDefinitionConfig(array $config, ContainerBuilder $configuration) @@ -50,6 +57,30 @@ class WebServiceExtension extends Extension $configuration->setParameter('webservice.definition.resource', isset($config['resource']) ? $config['resource'] : null); } + protected function registerServiceBindingConfig(array $config, ContainerBuilder $configuration) + { + $style = isset($config['style']) ? $config['style'] : 'document-literal-wrapped'; + + if(!in_array($style, array('document-literal-wrapped', 'rpc-literal'))) + { + throw new \InvalidArgumentException(); + } + + $binderNamespace = 'Bundle\\WebServiceBundle\\ServiceBinding\\'; + + switch ($style) + { + case 'document-literal-wrapped': + $configuration->setParameter('webservice.binder.request.class', $binderNamespace . 'DocumentLiteralWrappedRequestMessageBinder'); + $configuration->setParameter('webservice.binder.response.class', $binderNamespace . 'DocumentLiteralWrappedResponseMessageBinder'); + break; + case 'rpc-literal': + $configuration->setParameter('webservice.binder.request.class', $binderNamespace . 'RpcLiteralRequestMessageBinder'); + $configuration->setParameter('webservice.binder.response.class', $binderNamespace . 'RpcLiteralResponseMessageBinder'); + break; + } + } + public function getXsdValidationBasePath() { return null; diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 896ec68..accd3c7 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -6,9 +6,12 @@ Bundle\WebServiceBundle\Soap\SoapRequest + Bundle\WebServiceBundle\ServiceBinding\DocumentLiteralWrappedRequestMessageBinder + Bundle\WebServiceBundle\ServiceBinding\DocumentLiteralWrappedResponseMessageBinder + Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition Bundle\WebServiceBundle\ServiceDefinition\Loader\XmlFileLoader - Bundle\WebServiceBundle\ServiceDefinition\Dumper\Wsdl11DocumentLiteralFileDumper + Bundle\WebServiceBundle\ServiceDefinition\Dumper\Wsdl11DocumentLiteralWrappedFileDumper @@ -19,12 +22,22 @@ - - - - + + + + + + + + + + + + + + %webservice.definition.name% diff --git a/ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php b/ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php new file mode 100644 index 0000000..6401d19 --- /dev/null +++ b/ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php @@ -0,0 +1,26 @@ + 1) + { + throw new \InvalidArgumentException(); + } + + $result = array(); + $message = $message[0]; + + foreach($messageDefinition->getArguments() as $argument) + { + $result[$argument->getName()] = $message->{$argument->getName()}; + } + + return $result; + } +} \ No newline at end of file diff --git a/ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php b/ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php new file mode 100644 index 0000000..7ff4910 --- /dev/null +++ b/ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php @@ -0,0 +1,16 @@ +{$messageDefinition->getName() . 'Result'} = $message; + + return $result; + } +} \ No newline at end of file diff --git a/ServiceBinding/MessageBinderInterface.php b/ServiceBinding/MessageBinderInterface.php new file mode 100644 index 0000000..867c781 --- /dev/null +++ b/ServiceBinding/MessageBinderInterface.php @@ -0,0 +1,18 @@ +getArguments() as $argument) + { + $result[$argument->getName()] = $message[$i]; + $i++; + } + + return $result; + } +} \ No newline at end of file diff --git a/ServiceBinding/RpcLiteralResponseMessageBinder.php b/ServiceBinding/RpcLiteralResponseMessageBinder.php new file mode 100644 index 0000000..b04c14f --- /dev/null +++ b/ServiceBinding/RpcLiteralResponseMessageBinder.php @@ -0,0 +1,13 @@ +loadServiceDefinition($definition); + + $this->definition = $definition; + $this->definitionDumper = $dumper; + + $this->requestMessageBinder = $requestMessageBinder; + $this->responseMessageBinder = $responseMessageBinder; + } + + public function getSerializedServiceDefinition() + { + // TODO: add caching + return $this->definitionDumper->dumpServiceDefinition($this->definition); + } + + public function isServiceHeader($name) + { + return $this->definition->getHeaders()->has($name); + } + + public function isServiceMethod($name) + { + return $this->definition->getMethods()->has($name); + } + + public function processServiceHeader($name, $data) + { + $headerDefinition = $this->definition->getHeaders()->get($name); + + return $this->createSoapHeader($headerDefinition, $data); + } + + public function processServiceMethodArguments($name, $arguments) + { + $methodDefinition = $this->definition->getMethods()->get($name); + + $result = array(); + $result['_controller'] = $methodDefinition->getController(); + $result = array_merge($result, $this->requestMessageBinder->processMessage($methodDefinition, $arguments)); + + return $result; + } + + public function processServiceMethodReturnValue($name, $return) + { + $methodDefinition = $this->definition->getMethods()->get($name); + + return $this->responseMessageBinder->processMessage($methodDefinition, $return); + } + + protected function createSoapHeader(Header $headerDefinition, $data) + { + if(!preg_match('/^\{(.+)\}(.+)$/', $headerDefinition->getType()->getXmlType(), $matches)) + { + throw new \InvalidArgumentException(); + } + + return new SoapHeader($matches[1], $matches[2], $data); + } +} \ No newline at end of file diff --git a/ServiceDefinition/ServiceHeader.php b/ServiceDefinition/Argument.php similarity index 83% rename from ServiceDefinition/ServiceHeader.php rename to ServiceDefinition/Argument.php index 8df73fb..4f812ec 100644 --- a/ServiceDefinition/ServiceHeader.php +++ b/ServiceDefinition/Argument.php @@ -2,12 +2,12 @@ namespace Bundle\WebServiceBundle\ServiceDefinition; -class ServiceHeader +class Argument { private $name; private $type; - public function __construct($name = null, ServiceType $type = null) + public function __construct($name = null, Type $type = null) { $this->setName($name); $this->setType($type); diff --git a/ServiceDefinition/Header.php b/ServiceDefinition/Header.php new file mode 100644 index 0000000..42ef3e2 --- /dev/null +++ b/ServiceDefinition/Header.php @@ -0,0 +1,35 @@ +setName($name); + $this->setType($type); + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getType() + { + return $this->type; + } + + public function setType($type) + { + $this->type = $type; + } +} \ No newline at end of file diff --git a/ServiceDefinition/Loader/XmlFileLoader.php b/ServiceDefinition/Loader/XmlFileLoader.php index af3cdfd..b35a265 100644 --- a/ServiceDefinition/Loader/XmlFileLoader.php +++ b/ServiceDefinition/Loader/XmlFileLoader.php @@ -2,13 +2,11 @@ namespace Bundle\WebServiceBundle\ServiceDefinition\Loader; -use Bundle\WebServiceBundle\ServiceDefinition\ServiceType; - use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition; - -use Bundle\WebServiceBundle\ServiceDefinition\ServiceMethod; - -use Bundle\WebServiceBundle\ServiceDefinition\ServiceHeader; +use Bundle\WebServiceBundle\ServiceDefinition\Header; +use Bundle\WebServiceBundle\ServiceDefinition\Method; +use Bundle\WebServiceBundle\ServiceDefinition\Argument; +use Bundle\WebServiceBundle\ServiceDefinition\Type; class XmlFileLoader extends FileLoader { @@ -35,11 +33,11 @@ class XmlFileLoader extends FileLoader /** * @param \SimpleXMLElement $node * - * @return \Bundle\WebServiceBundle\ServiceDefinition\ServiceHeader + * @return \Bundle\WebServiceBundle\ServiceDefinition\Header */ protected function parseHeader(\SimpleXMLElement $node) { - $header = new ServiceHeader((string)$node['name'], $this->parseType($node->type)); + $header = new Header((string)$node['name'], $this->parseType($node->type)); return $header; } @@ -47,11 +45,16 @@ class XmlFileLoader extends FileLoader /** * @param \SimpleXMLElement $node * - * @return \Bundle\WebServiceBundle\ServiceDefinition\ServiceMethod + * @return \Bundle\WebServiceBundle\ServiceDefinition\Method */ protected function parseMethod(\SimpleXMLElement $node) { - $method = new ServiceMethod((string)$node['name'], (string)$node['controller']); + $method = new Method((string)$node['name'], (string)$node['controller']); + + foreach($node->argument as $argument) + { + $method->getArguments()->add($this->parseArgument($argument)); + } return $method; } @@ -59,7 +62,19 @@ class XmlFileLoader extends FileLoader /** * @param \SimpleXMLElement $node * - * @return \Bundle\WebServiceBundle\ServiceDefinition\ServiceType + * @return \Bundle\WebServiceBundle\ServiceDefinition\Argument + */ + protected function parseArgument(\SimpleXMLElement $node) + { + $argument = new Argument((string)$node['name'], $this->parseType($node->type)); + + return $argument; + } + + /** + * @param \SimpleXMLElement $node + * + * @return \Bundle\WebServiceBundle\ServiceDefinition\Type */ protected function parseType(\SimpleXMLElement $node) { @@ -67,7 +82,7 @@ class XmlFileLoader extends FileLoader $qname = explode(':', $node['xml-type'], 2); $xmlType = sprintf('{%s}%s', $namespaces[$qname[0]], $qname[1]); - $type = new ServiceType((string)$node['php-type'], $xmlType, (string)$node['converter']); + $type = new Type((string)$node['php-type'], $xmlType, (string)$node['converter']); return $type; } diff --git a/ServiceDefinition/ServiceMethod.php b/ServiceDefinition/Method.php similarity index 59% rename from ServiceDefinition/ServiceMethod.php rename to ServiceDefinition/Method.php index d78ead6..1a157fe 100644 --- a/ServiceDefinition/ServiceMethod.php +++ b/ServiceDefinition/Method.php @@ -2,15 +2,19 @@ namespace Bundle\WebServiceBundle\ServiceDefinition; -class ServiceMethod +use Bundle\WebServiceBundle\Util\Collection; + +class Method { private $name; private $controller; + private $arguments; - public function __construct($name = null, $controller = null) + public function __construct($name = null, $controller = null, array $arguments = array()) { $this->setName($name); $this->setController($controller); + $this->setArguments($arguments); } public function getName() @@ -32,4 +36,15 @@ class ServiceMethod { $this->controller = $controller; } + + public function getArguments() + { + return $this->arguments; + } + + public function setArguments($arguments) + { + $this->arguments = new Collection('getName'); + $this->arguments->addAll($arguments); + } } \ No newline at end of file diff --git a/ServiceDefinition/ServiceType.php b/ServiceDefinition/Type.php similarity index 97% rename from ServiceDefinition/ServiceType.php rename to ServiceDefinition/Type.php index bcf0699..f4e33de 100644 --- a/ServiceDefinition/ServiceType.php +++ b/ServiceDefinition/Type.php @@ -2,7 +2,7 @@ namespace Bundle\WebServiceBundle\ServiceDefinition; -class ServiceType +class Type { private $phpType; private $xmlType; diff --git a/Soap/SoapRequest.php b/Soap/SoapRequest.php index da0d99b..cbc8b89 100644 --- a/Soap/SoapRequest.php +++ b/Soap/SoapRequest.php @@ -46,7 +46,6 @@ class SoapRequest extends Request parent::__construct($query, null, $attributes, $cookies, null, $server); $this->rawContent = $rawContent != null ? $rawContent : $this->loadRawContent(); - $this->soapHeaders = new Collection('getName'); } diff --git a/Soap/SoapResponse.php b/Soap/SoapResponse.php index 4d7cc34..46042cc 100644 --- a/Soap/SoapResponse.php +++ b/Soap/SoapResponse.php @@ -26,6 +26,9 @@ class SoapResponse extends Response */ protected $soapHeaders; + /** + * @var mixed + */ protected $soapReturnValue; public function __construct($returnValue = null) diff --git a/SoapKernel.php b/SoapKernel.php index 36b1514..ba80ca4 100644 --- a/SoapKernel.php +++ b/SoapKernel.php @@ -10,12 +10,6 @@ namespace Bundle\WebServiceBundle; -use Bundle\WebServiceBundle\Soap\SoapHeader; - -use Bundle\WebServiceBundle\ServiceDefinition\ServiceHeader; - -use Bundle\WebServiceBundle\ServiceDefinition\Dumper\DumperInterface; - use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -23,9 +17,9 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Bundle\WebServiceBundle\Soap\SoapRequest; use Bundle\WebServiceBundle\Soap\SoapResponse; +use Bundle\WebServiceBundle\Soap\SoapHeader; -use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition; -use Bundle\WebServiceBundle\ServiceDefinition\Loader\LoaderInterface; +use Bundle\WebServiceBundle\ServiceBinding\ServiceBinder; use Bundle\WebServiceBundle\Util\String; @@ -55,24 +49,20 @@ class SoapKernel implements HttpKernelInterface protected $soapResponse; /** - * @var \Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition + * @var \Bundle\WebServiceBundle\ServiceBinding\ServiceBinder */ - protected $serviceDefinition; + protected $serviceBinder; /** * @var \Symfony\Component\HttpKernel\HttpKernelInterface */ protected $kernel; - public function __construct(ServiceDefinition $definition, LoaderInterface $loader, DumperInterface $dumper, HttpKernelInterface $kernel) + public function __construct(ServiceBinder $serviceBinder, HttpKernelInterface $kernel) { - $this->serviceDefinition = $definition; - $loader->loadServiceDefinition($this->serviceDefinition); + $this->serviceBinder = $serviceBinder; - // assume $dumper creates WSDL 1.1 file - $wsdl = $dumper->dumpServiceDefinition($this->serviceDefinition); - - $this->soapServer = new \SoapServer($wsdl); + $this->soapServer = new \SoapServer($this->serviceBinder->getSerializedServiceDefinition()); $this->soapServer->setObject($this); $this->kernel = $kernel; @@ -107,19 +97,21 @@ class SoapKernel implements HttpKernelInterface */ public function __call($method, $arguments) { - if($this->serviceDefinition->getHeaders()->has($method)) + if($this->serviceBinder->isServiceHeader($method)) { // collect request soap headers - $headerDefinition = $this->serviceDefinition->getHeaders()->get($method); - $this->soapRequest->getSoapHeaders()->add($this->createSoapHeader($headerDefinition, $arguments[0])); + $this->soapRequest->getSoapHeaders()->add( + $this->serviceBinder->processServiceHeader($method, $arguments[0]) + ); return; } - if($this->serviceDefinition->getMethods()->has($method)) + if($this->serviceBinder->isServiceMethod($method)) { - $methodDefinition = $this->serviceDefinition->getMethods()->get($method); - $this->soapRequest->attributes->set('_controller', $methodDefinition->getController()); + $this->soapRequest->attributes->add( + $this->serviceBinder->processServiceMethodArguments($method, $arguments) + ); // delegate to standard http kernel $response = $this->kernel->handle($this->soapRequest, self::MASTER_REQUEST, true); @@ -133,7 +125,10 @@ class SoapKernel implements HttpKernelInterface } // return operation return value to soap server - return $this->soapResponse->getReturnValue(); + return $this->serviceBinder->processServiceMethodReturnValue( + $method, + $this->soapResponse->getReturnValue() + ); } } @@ -180,14 +175,4 @@ class SoapKernel implements HttpKernelInterface return $response; } - - protected function createSoapHeader(ServiceHeader $headerDefinition, $data) - { - if(!preg_match('/^\{(.+)\}(.+)$/', $headerDefinition->getType()->getXmlType(), $matches)) - { - throw new \InvalidArgumentException(); - } - - return new SoapHeader($matches[1], $matches[2], $data); - } } \ No newline at end of file diff --git a/Tests/SoapKernelTest.php b/Tests/SoapKernelTest.php index 890259f..49e8727 100644 --- a/Tests/SoapKernelTest.php +++ b/Tests/SoapKernelTest.php @@ -10,17 +10,20 @@ namespace Bundle\WebServiceBundle\Tests; - -use Bundle\WebServiceBundle\ServiceDefinition\ServiceMethod; - -use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition; - use Symfony\Component\HttpFoundation\Request; use Bundle\WebServiceBundle\SoapKernel; use Bundle\WebServiceBundle\Soap\SoapRequest; use Bundle\WebServiceBundle\Soap\SoapResponse; +use Bundle\WebServiceBundle\ServiceBinding\ServiceBinder; +use Bundle\WebServiceBundle\ServiceBinding\RpcLiteralResponseMessageBinder; +use Bundle\WebServiceBundle\ServiceBinding\RpcLiteralRequestMessageBinder; + +use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition; +use Bundle\WebServiceBundle\ServiceDefinition\Method; +use Bundle\WebServiceBundle\ServiceDefinition\Loader\XmlFileLoader; + /** * UnitTest for \Bundle\WebServiceBundle\SoapKernel. * @@ -36,17 +39,19 @@ class SoapKernelTest extends \PHPUnit_Framework_TestCase public function setUp() { $serviceDefinition = new ServiceDefinition('api'); - $serviceDefinition->getMethods()->add(new ServiceMethod('math_multiply', 'MathController::multiply')); - - $serviceDefinitionLoader = null; + $serviceDefinitionLoader = new XmlFileLoader(__DIR__ . '/fixtures/api-servicedefinition.xml'); $serviceDefinitionDumper = new StaticFileDumper(__DIR__ . '/fixtures/api.wsdl'); + $requestMessageBinder = new RpcLiteralRequestMessageBinder(); + $responseMessageBinder = new RpcLiteralResponseMessageBinder(); + + $serviceBinder = new ServiceBinder($serviceDefinition, $serviceDefinitionLoader, $serviceDefinitionDumper, $requestMessageBinder, $responseMessageBinder); $httpKernel = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); $httpKernel->expects($this->any()) ->method('handle') ->will($this->returnValue(new SoapResponse(200))); - $this->soapKernel = new SoapKernel($serviceDefinition, $serviceDefinitionLoader, $serviceDefinitionDumper, $httpKernel); + $this->soapKernel = new SoapKernel($serviceBinder, $httpKernel); } public function testHandle() diff --git a/Tests/fixtures/api-servicedefinition.xml b/Tests/fixtures/api-servicedefinition.xml new file mode 100644 index 0000000..30fd286 --- /dev/null +++ b/Tests/fixtures/api-servicedefinition.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file