The bundle is back!

The definition of service has changed, read the README.
This commit is contained in:
Francis Besset 2011-07-17 10:46:54 +02:00
parent 81118f8d47
commit 1c608ccf20
48 changed files with 641 additions and 535 deletions

View File

@ -10,28 +10,14 @@
namespace Bundle\WebServiceBundle\Controller; namespace Bundle\WebServiceBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerAware;
use Bundle\WebServiceBundle\Soap\SoapRequest; use Bundle\WebServiceBundle\Soap\SoapRequest;
use Bundle\WebServiceBundle\Soap\SoapResponse; use Bundle\WebServiceBundle\Soap\SoapResponse;
use Bundle\WebServiceBundle\Soap\SoapHeader;
use Bundle\WebServiceBundle\Soap\SoapServerFactory;
use Bundle\WebServiceBundle\ServiceBinding\ServiceBinder; use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Response;
use Bundle\WebServiceBundle\Converter\ConverterRepository; use Symfony\Component\HttpKernel\HttpKernelInterface;
use Bundle\WebServiceBundle\Util\String;
/** /**
*
*
* @author Christian Kerl <christian-kerl@web.de> * @author Christian Kerl <christian-kerl@web.de>
*/ */
class SoapWebServiceController extends ContainerAware class SoapWebServiceController extends ContainerAware
@ -57,54 +43,33 @@ class SoapWebServiceController extends ContainerAware
protected $serviceBinder; protected $serviceBinder;
/** /**
* @var \Symfony\Component\HttpKernel\HttpKernelInterface * @return \Bundle\WebServiceBundle\Soap\SoapResponse
*/ */
protected $kernel; public function callAction($webservice)
public function __construct(ContainerInterface $container, HttpKernelInterface $kernel)
{ {
$this->setContainer($container); $webServiceContext = $this->container->get('webservice.context.'.$webservice);
$this->kernel = $kernel;
}
public function getRequest()
{
return $this->soapRequest;
}
public function getResponse()
{
return $this->soapResponse;
}
public function call($webservice)
{
$webServiceContext = $this->container->get('webservice.context.' . $webservice);
$this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request'));
$this->serviceBinder = $webServiceContext->getServiceBinder(); $this->serviceBinder = $webServiceContext->getServiceBinder();
$this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request'));
$this->soapServer = $webServiceContext->getServerFactory()->create($this->soapRequest, $this->soapResponse); $this->soapServer = $webServiceContext->getServerFactory()->create($this->soapRequest, $this->soapResponse);
$this->soapServer->setObject($this); $this->soapServer->setObject($this);
ob_start(); ob_start();
{
$this->soapServer->handle($this->soapRequest->getSoapMessage()); $this->soapServer->handle($this->soapRequest->getSoapMessage());
} $this->soapResponse->setContent(ob_get_clean());
$soapResponseContent = ob_get_clean();
$this->soapResponse->setContent($soapResponseContent);
return $this->soapResponse; return $this->soapResponse;
} }
public function definition($webservice) /**
* @return Symfony\Component\HttpFoundation\Response
*/
public function definitionAction($webservice)
{ {
$webServiceContext = $this->container->get('webservice.context.' . $webservice); $webServiceContext = $this->container->get('webservice.context.'.$webservice);
$request = $this->container->get('request'); $request = $this->container->get('request');
if($request->query->has('WSDL')) { if ($request->query->has('wsdl') || $request->query->has('WSDL')) {
$endpoint = $this->container->get('router')->generate('_webservice_call', array('webservice' => $webservice), true); $endpoint = $this->container->get('router')->generate('_webservice_call', array('webservice' => $webservice), true);
$response = new Response($webServiceContext->getWsdlFileContent($endpoint)); $response = new Response($webServiceContext->getWsdlFileContent($endpoint));
@ -129,7 +94,7 @@ class SoapWebServiceController extends ContainerAware
*/ */
public function __call($method, $arguments) public function __call($method, $arguments)
{ {
if($this->serviceBinder->isServiceHeader($method)) { if ($this->serviceBinder->isServiceHeader($method)) {
// collect request soap headers // collect request soap headers
$this->soapRequest->getSoapHeaders()->add( $this->soapRequest->getSoapHeaders()->add(
$this->serviceBinder->processServiceHeader($method, $arguments[0]) $this->serviceBinder->processServiceHeader($method, $arguments[0])
@ -138,18 +103,18 @@ class SoapWebServiceController extends ContainerAware
return; return;
} }
if($this->serviceBinder->isServiceMethod($method)) { if ($this->serviceBinder->isServiceMethod($method)) {
$this->soapRequest->attributes->add( $this->soapRequest->attributes->add(
$this->serviceBinder->processServiceMethodArguments($method, $arguments) $this->serviceBinder->processServiceMethodArguments($method, $arguments)
); );
// forward to controller // forward to controller
$response = $this->kernel->handle($this->soapRequest, HttpKernelInterface::SUB_REQUEST, false); $response = $this->container->get('http_kernel')->handle($this->soapRequest, HttpKernelInterface::SUB_REQUEST, false);
$this->soapResponse = $this->checkResponse($response); $this->soapResponse = $this->checkResponse($response);
// add response soap headers to soap server // add response soap headers to soap server
foreach($this->soapResponse->getSoapHeaders() as $header) { foreach ($this->soapResponse->getSoapHeaders() as $header) {
$this->soapServer->addSoapHeader($header->toNativeSoapHeader()); $this->soapServer->addSoapHeader($header->toNativeSoapHeader());
} }
@ -172,10 +137,26 @@ class SoapWebServiceController extends ContainerAware
*/ */
protected function checkResponse(Response $response) protected function checkResponse(Response $response)
{ {
if($response == null || !$response instanceof SoapResponse) { if (null === $response || !$response instanceof SoapResponse) {
throw new \InvalidArgumentException(); throw new \InvalidArgumentException();
} }
return $response; return $response;
} }
/**
* @return \Bundle\WebServiceBundle\Soap\SoapRequest
*/
public function getRequest()
{
return $this->soapRequest;
}
/**
* @return \Bundle\WebServiceBundle\Soap\SoapResponse
*/
public function getResponse()
{
return $this->soapResponse;
}
} }

View File

@ -10,22 +10,15 @@
namespace Bundle\WebServiceBundle\Converter; namespace Bundle\WebServiceBundle\Converter;
/**
*
* @author Christian Kerl <christian-kerl@web.de>
*/
use Bundle\WebServiceBundle\SoapKernel;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class ConverterRepository class ConverterRepository
{ {
private $typeConverters = array(); private $typeConverters = array();
public function __construct()
{
}
public function addTypeConverter(TypeConverterInterface $converter) public function addTypeConverter(TypeConverterInterface $converter)
{ {
$this->typeConverters[] = $converter; $this->typeConverters[] = $converter;

View File

@ -11,11 +11,9 @@
namespace Bundle\WebServiceBundle\Converter; namespace Bundle\WebServiceBundle\Converter;
use Bundle\WebServiceBundle\Soap\SoapRequest; use Bundle\WebServiceBundle\Soap\SoapRequest;
use Bundle\WebServiceBundle\Soap\SoapResponse; use Bundle\WebServiceBundle\Soap\SoapResponse;
/** /**
*
* @author Christian Kerl <christian-kerl@web.de> * @author Christian Kerl <christian-kerl@web.de>
*/ */
interface TypeConverterInterface interface TypeConverterInterface

View File

@ -11,13 +11,10 @@
namespace Bundle\WebServiceBundle\Converter; namespace Bundle\WebServiceBundle\Converter;
use Bundle\WebServiceBundle\Soap\SoapRequest; use Bundle\WebServiceBundle\Soap\SoapRequest;
use Bundle\WebServiceBundle\Soap\SoapResponse; use Bundle\WebServiceBundle\Soap\SoapResponse;
use Bundle\WebServiceBundle\Util\String; use Bundle\WebServiceBundle\Util\String;
/** /**
*
* @author Christian Kerl <christian-kerl@web.de> * @author Christian Kerl <christian-kerl@web.de>
*/ */
class XopIncludeTypeConverter implements TypeConverterInterface class XopIncludeTypeConverter implements TypeConverterInterface
@ -42,7 +39,7 @@ class XopIncludeTypeConverter implements TypeConverterInterface
$ref = $include->getAttribute('href'); $ref = $include->getAttribute('href');
if(String::startsWith($ref, 'cid:')) { if (String::startsWith($ref, 'cid:')) {
$cid = urldecode(substr($ref, 4)); $cid = urldecode(substr($ref, 4));
return $request->getSoapAttachments()->get($cid)->getContent(); return $request->getSoapAttachments()->get($cid)->getContent();

View File

@ -0,0 +1,36 @@
<?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\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds tagged webservice.definition.loader services to ebservice.definition.resolver service
*
* @author Francis Besset <francis.besset@gmail.com>
*/
class WebServiceResolverPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('webservice.definition.loader.resolver')) {
return;
}
$definition = $container->getDefinition('webservice.definition.loader.resolver');
foreach ($container->findTaggedServiceIds('webservice.definition.loader') as $id => $attributes) {
$definition->addMethodCall('addLoader', array(new Reference($id)));
}
}
}

View File

@ -10,7 +10,6 @@
namespace Bundle\WebServiceBundle\DependencyInjection; namespace Bundle\WebServiceBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder;
/** /**

View File

@ -10,16 +10,12 @@
namespace Bundle\WebServiceBundle\DependencyInjection; namespace Bundle\WebServiceBundle\DependencyInjection;
use Bundle\WebServiceBundle\Util\Assert;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/** /**
@ -29,6 +25,8 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
*/ */
class WebServiceExtension extends Extension class WebServiceExtension extends Extension
{ {
private $contextArguments;
// maps config options to service suffix' // maps config options to service suffix'
private $bindingConfigToServiceSuffixMap = array('rpc-literal' => '.rpcliteral', 'document-wrapped' => '.documentwrapped'); private $bindingConfigToServiceSuffixMap = array('rpc-literal' => '.rpcliteral', 'document-wrapped' => '.documentwrapped');
@ -36,7 +34,7 @@ class WebServiceExtension extends Extension
{ {
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('annotations.xml'); $loader->load('loaders.xml');
$loader->load('webservice.xml'); $loader->load('webservice.xml');
$processor = new Processor(); $processor = new Processor();
@ -45,38 +43,37 @@ class WebServiceExtension extends Extension
$config = $processor->process($configuration->getConfigTree(), $configs); $config = $processor->process($configuration->getConfigTree(), $configs);
foreach($config['services'] as $name => $serviceConfig) { foreach($config['services'] as $name => $serviceConfig) {
$this->createWebServiceContext($name, $serviceConfig, $container); $serviceConfig['name'] = $name;
$this->createWebServiceContext($serviceConfig, $container);
} }
} }
private function createWebServiceContext($name, array $config, ContainerBuilder $container) private function createWebServiceContext(array $config, ContainerBuilder $container)
{ {
$bindingDependentArguments = array(1, 3, 4);
$bindingSuffix = $this->bindingConfigToServiceSuffixMap[$config['binding']]; $bindingSuffix = $this->bindingConfigToServiceSuffixMap[$config['binding']];
unset($config['binding']); unset($config['binding']);
$contextPrototype = $container->getDefinition('webservice.context'); if (null === $this->contextArguments) {
$contextPrototypeArguments = $contextPrototype->getArguments(); $this->contextArguments = $container
->getDefinition('webservice.context')
->getArguments()
;
}
$contextId = 'webservice.context.'.$name; $contextId = 'webservice.context.'.$config['name'];
$context = $container->setDefinition($contextId, new DefinitionDecorator('webservice.context')); $context = $container->setDefinition($contextId, $definition = new DefinitionDecorator('webservice.context'));
$arguments = array(); $arguments = array();
foreach($bindingDependentArguments as $idx) { foreach($this->contextArguments as $i => $argument) {
$arguments[] = new Reference($contextPrototypeArguments[$idx].$bindingSuffix); if (in_array($i, array(1, 3, 4))) {
} $argument = new Reference($argument->__toString().$bindingSuffix);
$arguments[5] = array_merge($contextPrototypeArguments[5], $config); } elseif (5 === $i) {
$argument = array_merge($argument, $config);
$context->setArguments($arguments); } else {
$argument = new Reference($argument->__toString());
} }
public function getNamespace() $definition->replaceArgument($i, $argument);
{
return null;
} }
public function getXsdValidationBasePath()
{
return null;
} }
} }

View File

@ -0,0 +1,64 @@
<?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\EventListener;
use Bundle\WebServiceBundle\ServiceDefinition\Annotation\ConfigurationInterface;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
/**
* Based on \Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener
*
* @author Francis Besset <francis.besset@gmail.com>
*/
class ControllerListener
{
/**
* @var \Doctrine\Common\Annotations\Reader
*/
protected $reader;
/**
* Constructor.
*
* @param Reader $reader An Reader instance
*/
public function __construct(Reader $reader)
{
$this->reader = $reader;
}
/**
* Modifies the Request object to apply configuration information found in
* controllers annotations like the template to render or HTTP caching
* configuration.
*
* @param FilterControllerEvent $event A FilterControllerEvent instance
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!is_array($controller = $event->getController())) {
return;
}
$object = new \ReflectionObject($controller[0]);
$method = $object->getMethod($controller[1]);
$request = $event->getRequest();
foreach ($this->reader->getMethodAnnotations($method) as $configuration) {
if ($configuration instanceof ConfigurationInterface) {
$request->attributes->set('_'.$configuration->getAliasName(), $configuration);
}
}
}
}

View File

@ -8,14 +8,46 @@ Requirements
------------ ------------
* Install and enable PHP's `SOAP` extension * Install and enable PHP's `SOAP` extension
* Download and add `Zend\Soap` library to `app/autoload.php` * Download `Zend\Soap`
git submodule add http://github.com/zendframework/zf2.git vendor/zend-framework
* Add `Zend\Soap` library to `app/autoload.php`
// app/autoload.php
$loader->registerNamespaces(array(
'Zend\\Soap' => __DIR__.'/../vendor/zend-frameword/library',
// your other namespaces
));
QuickStart QuickStart
---------- ----------
* Put WebServiceBundle in your `src/Bundle` dir * Put WebServiceBundle in your `vendor/bundles/Bundle` dir
git submodule add https://github.com/BeSimple/BeSimpleSoapBundle.git vendor/bundles/WebServiceBundle
* Enable WebServiceBundle in your `app/AppKernel.php` * Enable WebServiceBundle in your `app/AppKernel.php`
// app/AppKernel.php
public function registerBundles()
{
return array(
// ...
new new Bundle\WebServiceBundle\WebServiceBundle(),
// ...
);
}
* Register the Bundle namespace
// app/autoload.php
$loader->registerNamespaces(array(
'Bundle' => __DIR__.'/../vendor/bundles',
'Zend\\Soap' => __DIR__.'/../vendor/zend-frameword/library',
// your other namespaces
));
* Include the WebServiceBundle's routing configuration in `app/config/routing.yml` (you can choose the prefix arbitrarily) * Include the WebServiceBundle's routing configuration in `app/config/routing.yml` (you can choose the prefix arbitrarily)
_ws: _ws:
@ -35,24 +67,32 @@ QuickStart
* Annotate your controller methods * Annotate your controller methods
// src/Acme/DemoBundle/Controller/DemoController.php // src/Acme/DemoBundle/Controller/DemoController.php
use Bundle\WebServiceBundle\ServiceDefinition\Annotation\Method;
use Bundle\WebServiceBundle\ServiceDefinition\Annotation\Param;
use Bundle\WebServiceBundle\ServiceDefinition\Annotation\Result;
use Bundle\WebServiceBundle\Soap\SoapResponse;
class DemoController extends Controller
{
/** /**
* @ws:Method("Hello") * @Method("Hello")
* @ws:Param("name", type = "string") * @Param("name", phpType = "string")
* @ws:Result(type = "string") * @Result(phpType = "string")
*/ */
public function helloAction($name) public function helloAction($name)
{ {
return new SoapResponse(sprintf('Hello %s!', $name)); return new SoapResponse(sprintf('Hello %s!', $name));
} }
}
* Open your web service endpoint * Open your web service endpoint
* `http://localhost/app_dev.php/ws/DemoApi` - HTML documentation * `http://localhost/app_dev.php/ws/DemoApi` - HTML documentation
* `http://localhost/app_dev.php/ws/DemoApi?WSDL` - WSDL file * `http://localhost/app_dev.php/ws/DemoApi?wsdl` - WSDL file
Test Test
---- ----
phpunit -c myapp src/Bundle/WebServiceBundle phpunit -c phpunit.xml.dist
[1]: http://www.symfony-project.org/plugins/ckWebServicePlugin [1]: http://www.symfony-project.org/plugins/ckWebServicePlugin

View File

@ -1,30 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="webservice.annotations.reader.class">Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationReader</parameter>
<parameter key="webservice.annotations.parser.class">Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationParser</parameter>
</parameters>
<services>
<service id="webservice.annotations.parser" class="%webservice.annotations.parser.class%">
<call method="setAutoloadAnnotations"><argument>true</argument></call>
<call method="setAnnotationNamespaceAlias">
<argument>Bundle\WebServiceBundle\ServiceDefinition\Annotation\</argument>
<argument>ws</argument>
</call>
</service>
<service id="webservice.annotations.reader" class="%webservice.annotations.reader.class%">
<argument type="service" id="webservice.annotations.cache" strict="false" />
<argument type="service" id="webservice.annotations.parser" />
</service>
<service id="webservice.annotations.cache.array" class="Doctrine\Common\Cache\ArrayCache" scope="prototype" />
<service id="webservice.annotations.cache" alias="webservice.annotations.cache.array" />
</services>
</container>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="webservice.definition.controller.listener.class">Bundle\WebServiceBundle\EventListener\ControllerListener</parameter>
<parameter key="webservice.definition.loader.resolver.class">Symfony\Component\Config\Loader\LoaderResolver</parameter>
<parameter key="webservice.definition.loader.class">Symfony\Component\Config\Loader\DelegatingLoader</parameter>
<parameter key="webservice.definition.loader.annot_dir.class">Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationDirectoryLoader</parameter>
<parameter key="webservice.definition.loader.annot_file.class">Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationFileLoader</parameter>
<parameter key="webservice.definition.loader.annot_class.class">Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationClassLoader</parameter>
</parameters>
<services>
<service id="webservice.definition.controller.listener" class="%webservice.definition.controller.listener.class%">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
<argument type="service" id="annotation_reader" />
</service>
<service id="webservice.definition.loader.resolver" class="%webservice.definition.loader.resolver.class%" public="false" />
<service id="webservice.definition.loader" class="%webservice.definition.loader.class%">
<argument type="service" id="webservice.definition.loader.resolver" />
</service>
<service id="webservice.definition.loader.annot_file" class="%webservice.definition.loader.annot_file.class%" public="false">
<tag name="webservice.definition.loader" />
<argument type="service" id="file_locator" />
<argument type="service" id="webservice.definition.loader.annot_class" />
</service>
<service id="webservice.definition.loader.annot_class" class="%webservice.definition.loader.annot_class.class%" public="false">
<tag name="webservice.definition.loader" />
<argument type="service" id="annotation_reader" />
</service>
</services>
</container>

View File

@ -5,12 +5,14 @@
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="_webservice_call" pattern="/{webservice}"> <route id="_webservice_call" pattern="/{webservice}">
<default key="_controller">webservice.controller:call</default> <default key="_controller">WebServiceBundle:SoapWebService:Call</default>
<default key="_format">xml</default>
<requirement key="_method">POST</requirement> <requirement key="_method">POST</requirement>
</route> </route>
<route id="_webservice_definition" pattern="/{webservice}"> <route id="_webservice_definition" pattern="/{webservice}">
<default key="_controller">webservice.controller:definition</default> <default key="_controller">WebServiceBundle:SoapWebService:Definition</default>
<default key="_format">xml</default>
<requirement key="_method">GET</requirement> <requirement key="_method">GET</requirement>
</route> </route>
</routes> </routes>

View File

@ -4,64 +4,44 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters> <parameters>
<parameter key="webservice.file_locator.class">Symfony\Component\HttpKernel\Config\FileLocator</parameter> <parameter key="webservice.controller.class">Bundle\WebServiceBundle\Controller\SoapWebServiceController</parameter>
<parameter key="webservice.context.class">Bundle\WebServiceBundle\WebServiceContext</parameter>
<parameter key="webservice.cache_dir">%kernel.cache_dir%/webservice</parameter> <parameter key="webservice.cache_dir">%kernel.cache_dir%/webservice</parameter>
<parameter key="webservice.binder.request.rpcliteral.class">Bundle\WebServiceBundle\ServiceBinding\RpcLiteralRequestMessageBinder</parameter>
<parameter key="webservice.binder.response.rpcliteral.class">Bundle\WebServiceBundle\ServiceBinding\RpcLiteralResponseMessageBinder</parameter>
<parameter key="webservice.binder.request.documentwrapped.class">Bundle\WebServiceBundle\ServiceBinding\DocumentLiteralWrappedRequestMessageBinder</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.converter.repository.class">Bundle\WebServiceBundle\Converter\ConverterRepository</parameter>
</parameters> </parameters>
<services> <services>
<service id="webservice.controller" class="Bundle\WebServiceBundle\Controller\SoapWebServiceController"> <service id="webservice.context" class="%webservice.context.class%" abstract="true">
<argument type="service" id="service_container" />
<argument type="service" id="http_kernel" />
</service>
<service id="webservice.context" class="Bundle\WebServiceBundle\WebServiceContext" 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.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="collection"> <argument type="collection">
<argument key="cache_dir">%webservice.cache_dir%</argument> <argument key="cache_dir">%webservice.cache_dir%</argument>
</argument> </argument>
</service> </service>
<service id="webservice.binder.request.rpcliteral" class="Bundle\WebServiceBundle\ServiceBinding\RpcLiteralRequestMessageBinder" /> <service id="webservice.binder.request.rpcliteral" class="%webservice.binder.request.rpcliteral.class%" />
<service id="webservice.binder.response.rpcliteral" class="Bundle\WebServiceBundle\ServiceBinding\RpcLiteralResponseMessageBinder" />
<service id="webservice.binder.request.documentwrapped" class="Bundle\WebServiceBundle\ServiceBinding\DocumentLiteralWrappedRequestMessageBinder" />
<service id="webservice.binder.response.documentwrapped" class="Bundle\WebServiceBundle\ServiceBinding\DocumentLiteralWrappedResponseMessageBinder" />
<service id="webservice.converter.repository" class="Bundle\WebServiceBundle\Converter\ConverterRepository"> <service id="webservice.binder.response.rpcliteral" class="%webservice.binder.response.rpcliteral.class%" />
<service id="webservice.binder.request.documentwrapped" class="%webservice.binder.request.documentwrapped.class%" />
<service id="webservice.binder.response.documentwrapped" class="%webservice.binder.response.documentwrapped.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%">
<call method="registerTypeConverterServices"> <call method="registerTypeConverterServices">
<argument type="service" id="service_container" /> <argument type="service" id="service_container" />
</call> </call>
</service> </service>
<service id="webservice.file_locator" class="%webservice.file_locator.class%" public="false">
<argument type="service" id="kernel" />
</service>
<!-- TODO: replace with delegating loader -->
<service id="webservice.definition.loader" alias="webservice.definition.loader.annot_file" />
<service id="webservice.definition.loader.annot_file" class="Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationFileLoader">
<tag name="webservice.definition.loader" />
<argument type="service" id="webservice.file_locator" />
<argument type="service" id="webservice.definition.loader.annot_class" />
</service>
<service id="webservice.definition.loader.annot_class" class="Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationClassLoader">
<tag name="webservice.definition.loader" />
<argument type="service" id="webservice.annotations.reader" />
<argument type="service" id="annotations.configuration_reader" on-invalid="null" />
</service>
<service id="webservice.definition.dumper.wsdl.rpcliteral" class="Bundle\WebServiceBundle\ServiceDefinition\Dumper\WsdlDumper" />
<service id="webservice.definition.dumper.wsdl" alias="webservice.definition.dumper.wsdl.rpcliteral" />
<service id="webservice.binder.request" alias="webservice.binder.request.rpcliteral" />
<service id="webservice.binder.response" alias="webservice.binder.response.rpcliteral" />
</services> </services>
</container> </container>

View File

@ -17,7 +17,7 @@ class DocumentLiteralWrappedResponseMessageBinder implements MessageBinderInterf
public function processMessage(Method $messageDefinition, $message) public function processMessage(Method $messageDefinition, $message)
{ {
$result = new \stdClass(); $result = new \stdClass();
$result->{$messageDefinition->getName() . 'Result'} = $message; $result->{$messageDefinition->getName().'Result'} = $message;
return $result; return $result;
} }

View File

@ -20,7 +20,9 @@ class RpcLiteralRequestMessageBinder implements MessageBinderInterface
$i = 0; $i = 0;
foreach($messageDefinition->getArguments() as $argument) { foreach($messageDefinition->getArguments() as $argument) {
if (isset($message[$i])) {
$result[$argument->getName()] = $message[$i]; $result[$argument->getName()] = $message[$i];
}
$i++; $i++;
} }

View File

@ -10,16 +10,10 @@
namespace Bundle\WebServiceBundle\ServiceBinding; namespace Bundle\WebServiceBundle\ServiceBinding;
use Bundle\WebServiceBundle\Util\QName;
use Bundle\WebServiceBundle\ServiceDefinition\Type;
use Bundle\WebServiceBundle\Soap\SoapHeader;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\ServiceDefinition\Header; use Bundle\WebServiceBundle\ServiceDefinition\Header;
use Bundle\WebServiceBundle\ServiceDefinition\Dumper\DumperInterface; use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\ServiceDefinition\Loader\LoaderInterface; use Bundle\WebServiceBundle\Soap\SoapHeader;
use Bundle\WebServiceBundle\Util\QName;
class ServiceBinder class ServiceBinder
{ {
@ -28,11 +22,6 @@ class ServiceBinder
*/ */
private $definition; private $definition;
/**
* @var \Bundle\WebServiceBundle\ServiceDefinition\Dumper\DumperInterface
*/
private $definitionDumper;
/** /**
* @var \Bundle\WebServiceBundle\ServiceBinding\MessageBinderInterface * @var \Bundle\WebServiceBundle\ServiceBinding\MessageBinderInterface
*/ */

View File

@ -0,0 +1,30 @@
<?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\ServiceDefinition\Annotation;
/**
* Based on \Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationAnnotation
*
* @author Francis Besset <francis.besset@gmail.com>
*/
abstract class Configuration implements ConfigurationInterface
{
public function __construct(array $values)
{
foreach ($values as $k => $v) {
if (!method_exists($this, $name = 'set'.$k)) {
throw new \RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, get_class($this)));
}
$this->$name($v);
}
}
}

View File

@ -0,0 +1,26 @@
<?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\ServiceDefinition\Annotation;
/**
* Based on \Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface
*
* @author Francis Besset <francis.besset@gmail.com>
*/
interface ConfigurationInterface
{
/**
* Returns the alias name for an annotated configuration.
*
* @return string
*/
function getAliasName();
}

View File

@ -10,24 +10,36 @@
namespace Bundle\WebServiceBundle\ServiceDefinition\Annotation; namespace Bundle\WebServiceBundle\ServiceDefinition\Annotation;
class Method /**
* @Annotation
*/
class Method extends Configuration
{ {
private $name; private $value;
private $service; private $service;
public function __construct($values) public function getValue()
{ {
$this->name = isset($values['value']) ? $values['value'] : null; return $this->value;
$this->service = isset($values['service']) ? $values['service'] : null;
}
public function getName($default = null)
{
return $this->name !== null ? $this->name : $default;
} }
public function getService() public function getService()
{ {
return $this->service; return $this->service;
} }
public function setValue($value)
{
$this->value = $value;
}
public function setService($service)
{
$this->service = $service;
}
public function getAliasName()
{
return 'method';
}
} }

View File

@ -10,19 +10,47 @@
namespace Bundle\WebServiceBundle\ServiceDefinition\Annotation; namespace Bundle\WebServiceBundle\ServiceDefinition\Annotation;
class Param extends TypedElement /**
* @Annotation
*/
class Param extends Configuration implements TypedElementInterface
{ {
private $name; private $value;
private $phpType;
private $xmlType;
public function __construct($values) public function getValue()
{ {
parent::__construct($values); return $this->value;
$this->name = $values['value'];
} }
public function getName() public function getPhpType()
{ {
return $this->name; return $this->phpType;
}
public function getXmlType()
{
return $this->xmlType;
}
public function setValue($value)
{
$this->value = $value;
}
public function setPhpType($phpType)
{
$this->phpType = $phpType;
}
public function setXmlType($xmlType)
{
$this->xmlType = $xmlType;
}
public function getAliasName()
{
return 'param';
} }
} }

View File

@ -10,10 +10,36 @@
namespace Bundle\WebServiceBundle\ServiceDefinition\Annotation; namespace Bundle\WebServiceBundle\ServiceDefinition\Annotation;
class Result extends TypedElement /**
* @Annotation
*/
class Result extends Configuration implements TypedElementInterface
{ {
public function __construct($values) private $phpType;
private $xmlType;
public function getPhpType()
{ {
parent::__construct($values); return $this->phpType;
}
public function getXmlType()
{
return $this->xmlType;
}
public function setPhpType($phpType)
{
$this->phpType = $phpType;
}
public function setXmlType($xmlType)
{
$this->xmlType = $xmlType;
}
public function getAliasName()
{
return 'result';
} }
} }

View File

@ -1,40 +0,0 @@
<?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\ServiceDefinition\Annotation;
abstract class TypedElement
{
private $phpType;
private $xmlType;
public function __construct($values)
{
foreach(array('type', 'phpType') as $key)
{
if(isset($values[$key]))
{
$this->phpType = $values[$key];
}
}
$this->xmlType = isset($values['xmlType']) ? $values['xmlType'] : null;
}
public function getPhpType()
{
return $this->phpType;
}
public function getXmlType()
{
return $this->xmlType;
}
}

View File

@ -0,0 +1,19 @@
<?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\ServiceDefinition\Annotation;
interface TypedElementInterface
{
function getPhpType();
function getXmlType();
function setPhpType($phpType);
function setXmlType($xmlType);
}

View File

@ -12,14 +12,11 @@ namespace Bundle\WebServiceBundle\ServiceDefinition\Dumper;
use Bundle\WebServiceBundle\ServiceDefinition\Method; use Bundle\WebServiceBundle\ServiceDefinition\Method;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition; use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\Util\Assert; use Bundle\WebServiceBundle\Util\Assert;
use Zend\Soap\Wsdl; use Zend\Soap\Wsdl;
/** /**
*
* @author Christian Kerl <christian-kerl@web.de> * @author Christian Kerl <christian-kerl@web.de>
*/ */
class WsdlDumper implements DumperInterface class WsdlDumper implements DumperInterface
@ -28,14 +25,12 @@ class WsdlDumper implements DumperInterface
public function dumpServiceDefinition(ServiceDefinition $definition, array $options = array()) public function dumpServiceDefinition(ServiceDefinition $definition, array $options = array())
{ {
Assert::thatArgumentNotNull('definition', $definition);
$options = array_merge(array('endpoint' => ''), $options); $options = array_merge(array('endpoint' => ''), $options);
Assert::thatArgumentNotNull('definition', $definition);
$this->definition = $definition; $this->definition = $definition;
$wsdl = new Wsdl($definition->getName(), $definition->getNamespace()); $wsdl = new Wsdl($definition->getName(), $definition->getNamespace());
$port = $wsdl->addPortType($this->getPortTypeName()); $port = $wsdl->addPortType($this->getPortTypeName());
$binding = $wsdl->addBinding($this->getBindingName(), 'tns:' . $this->getPortTypeName()); $binding = $wsdl->addBinding($this->getBindingName(), 'tns:' . $this->getPortTypeName());
@ -86,36 +81,36 @@ class WsdlDumper implements DumperInterface
protected function getPortName() protected function getPortName()
{ {
return $this->definition->getName() . 'Port'; return $this->definition->getName().'Port';
} }
protected function getPortTypeName() protected function getPortTypeName()
{ {
return $this->definition->getName() . 'PortType'; return $this->definition->getName().'PortType';
} }
protected function getBindingName() protected function getBindingName()
{ {
return $this->definition->getName() . 'Binding'; return $this->definition->getName().'Binding';
} }
protected function getServiceName() protected function getServiceName()
{ {
return $this->definition->getName() . 'Service'; return $this->definition->getName().'Service';
} }
protected function getRequestMessageName(Method $method) protected function getRequestMessageName(Method $method)
{ {
return $method->getName() . 'Request'; return $method->getName().'Request';
} }
protected function getResponseMessageName(Method $method) protected function getResponseMessageName(Method $method)
{ {
return $method->getName() . 'Response'; return $method->getName().'Response';
} }
protected function getSoapOperationName(Method $method) protected function getSoapOperationName(Method $method)
{ {
return $this->definition->getNamespace() . $method->getName(); return $this->definition->getNamespace().$method->getName();
} }
} }

View File

@ -8,17 +8,17 @@
* with this source code in the file LICENSE. * with this source code in the file LICENSE.
*/ */
namespace Bundle\WebServiceBundle\ServiceDefinition\Loader; namespace Bundle\WebServiceBundle\ServiceDefinition\Loader;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\ServiceDefinition\Method;
use Bundle\WebServiceBundle\ServiceDefinition\Argument; use Bundle\WebServiceBundle\ServiceDefinition\Argument;
use Bundle\WebServiceBundle\ServiceDefinition\Method;
use Bundle\WebServiceBundle\ServiceDefinition\Type; use Bundle\WebServiceBundle\ServiceDefinition\Type;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\ServiceDefinition\Annotation\Method as MethodAnnotation; use Bundle\WebServiceBundle\ServiceDefinition\Annotation\Method as MethodAnnotation;
use Bundle\WebServiceBundle\ServiceDefinition\Annotation\Param as ParamAnnotation;
use Bundle\WebServiceBundle\ServiceDefinition\Annotation\Result as ResultAnnotation;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\LoaderResolver;
@ -32,18 +32,18 @@ use Symfony\Component\Config\Loader\LoaderResolver;
*/ */
class AnnotationClassLoader implements LoaderInterface class AnnotationClassLoader implements LoaderInterface
{ {
private $wsMethodAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Method'; private $methodAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Method';
private $wsParamAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Param'; private $paramAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Param';
private $wsResultAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Result'; private $resultAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Result';
protected $reader; protected $reader;
/** /**
* Constructor. * Constructor.
* *
* @param AnnotationReader $reader * @param \Doctrine\Common\Annotations\Reader $reader
*/ */
public function __construct(AnnotationReader $reader) public function __construct(Reader $reader)
{ {
$this->reader = $reader; $this->reader = $reader;
} }
@ -65,30 +65,45 @@ class AnnotationClassLoader implements LoaderInterface
} }
$class = new \ReflectionClass($class); $class = new \ReflectionClass($class);
$definition = new ServiceDefinition(); $definition = new ServiceDefinition();
foreach ($class->getMethods() as $method) { foreach ($class->getMethods() as $method) {
$wsMethodAnnot = $this->reader->getMethodAnnotation($method, $this->wsMethodAnnotationClass); $serviceArguments = array();
$serviceMethod =
$serviceReturn = null;
if($wsMethodAnnot !== null) { foreach ($this->reader->getMethodAnnotations($method) as $i => $annotation) {
$wsParamAnnots = $this->reader->getMethodAnnotations($method, $this->wsParamAnnotationClass); if ($annotation instanceof ParamAnnotation) {
$wsResultAnnot = $this->reader->getMethodAnnotation($method, $this->wsResultAnnotationClass); $serviceArguments[] = new Argument(
$annotation->getValue(),
$serviceMethod = new Method(); new Type($annotation->getPhpType(), $annotation->getXmlType())
$serviceMethod->setName($wsMethodAnnot->getName($method->getName())); );
$serviceMethod->setController($this->getController($method, $wsMethodAnnot)); } elseif ($annotation instanceof MethodAnnotation) {
if ($serviceMethod) {
foreach($wsParamAnnots as $wsParamAnnot) { throw new \LogicException(sprintf('@Method defined twice for "%s".', $method->getName()));
$serviceArgument = new Argument();
$serviceArgument->setName($wsParamAnnot->getName());
$serviceArgument->setType(new Type($wsParamAnnot->getPhpType(), $wsParamAnnot->getXmlType()));
$serviceMethod->getArguments()->add($serviceArgument);
} }
if($wsResultAnnot !== null) { $serviceMethod = new Method(
$serviceMethod->setReturn(new Type($wsResultAnnot->getPhpType(), $wsResultAnnot->getXmlType())); $annotation->getValue(),
$this->getController($method, $annotation)
);
} elseif ($annotation instanceof ResultAnnotation) {
if ($serviceReturn) {
throw new \LogicException(sprintf('@Result defined twice for "%s".', $method->getName()));
}
$serviceReturn = new Type($annotation->getPhpType(), $annotation->getXmlType());
}
}
if (!$serviceMethod && (!empty($serviceArguments) || $serviceReturn)) {
throw new \LogicException(sprintf('@Method non-existent for "%s".', $method->getName()));
}
if ($serviceMethod) {
$serviceMethod->setArguments($serviceArguments);
if ($serviceReturn) {
$serviceMethod->setReturn($serviceReturn);
} }
$definition->getMethods()->add($serviceMethod); $definition->getMethods()->add($serviceMethod);
@ -100,7 +115,7 @@ class AnnotationClassLoader implements LoaderInterface
private function getController(\ReflectionMethod $method, MethodAnnotation $annotation) private function getController(\ReflectionMethod $method, MethodAnnotation $annotation)
{ {
if($annotation->getService() !== null) { if(null !== $annotation->getService()) {
return $annotation->getService() . ':' . $method->name; return $annotation->getService() . ':' . $method->name;
} else { } else {
return $method->class . '::' . $method->name; return $method->class . '::' . $method->name;

View File

@ -8,14 +8,13 @@
* with this source code in the file LICENSE. * with this source code in the file LICENSE.
*/ */
namespace Bundle\WebServiceBundle\ServiceDefinition\Loader; namespace Bundle\WebServiceBundle\ServiceDefinition\Loader;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition; use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Config\Resource\FileResource;
/** /**
* AnnotationFileLoader loads ServiceDefinition from annotations set * AnnotationFileLoader loads ServiceDefinition from annotations set
@ -60,13 +59,11 @@ class AnnotationFileLoader extends FileLoader
{ {
$path = $this->locator->locate($file); $path = $this->locator->locate($file);
$definition = new ServiceDefinition();
if ($class = $this->findClass($path)) { if ($class = $this->findClass($path)) {
$definition = $this->loader->load($class, $type); return $definition = $this->loader->load($class, $type);
} }
return $definition; return null;
} }
/** /**

View File

@ -1,52 +0,0 @@
<?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\ServiceDefinition\Loader;
use Doctrine\Common\Annotations\Lexer;
use Doctrine\Common\Annotations\Parser;
/**
* AnnotationParser allows multiple annotations of the same class to be present.
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class AnnotationParser extends Parser
{
/**
* Annotations ::= Annotation {[ "*" ]* [Annotation]}*
*
* @return array
*/
public function Annotations()
{
$this->isNestedAnnotation = false;
$annotations = array();
$annot = $this->Annotation();
if ($annot !== false) {
$annotations[get_class($annot)][] = $annot;
$this->getLexer()->skipUntil(Lexer::T_AT);
}
while ($this->getLexer()->lookahead !== null && $this->getLexer()->isNextToken(Lexer::T_AT)) {
$this->isNestedAnnotation = false;
$annot = $this->Annotation();
if ($annot !== false) {
$annotations[get_class($annot)][] = $annot;
$this->getLexer()->skipUntil(Lexer::T_AT);
}
}
return $annotations;
}
}

View File

@ -1,39 +0,0 @@
<?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\ServiceDefinition\Loader;
use Doctrine\Common\Annotations\AnnotationReader as BaseAnnotationReader;
/**
* AnnotationReader.
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class AnnotationReader extends BaseAnnotationReader
{
public function getMethodAnnotation(\ReflectionMethod $method, $type)
{
$annotation = parent::getMethodAnnotation($method, $type);
if($annotation !== null && count($annotation) > 1) {
throw new \LogicException(sprintf("There is more than one annotation of type '%s'!", $type));
}
return $annotation !== null ? $annotation[0] : null;
}
public function getMethodAnnotations(\ReflectionMethod $method, $type = null)
{
$annotations = parent::getMethodAnnotations($method);
return $type !== null && isset($annotations[$type]) ? $annotations[$type] : $annotations;
}
}

View File

@ -10,13 +10,13 @@
namespace Bundle\WebServiceBundle\ServiceDefinition\Loader; namespace Bundle\WebServiceBundle\ServiceDefinition\Loader;
use Symfony\Component\Config\Loader\FileLoader; use Bundle\WebServiceBundle\ServiceDefinition\Argument;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\ServiceDefinition\Header; use Bundle\WebServiceBundle\ServiceDefinition\Header;
use Bundle\WebServiceBundle\ServiceDefinition\Method; use Bundle\WebServiceBundle\ServiceDefinition\Method;
use Bundle\WebServiceBundle\ServiceDefinition\Argument;
use Bundle\WebServiceBundle\ServiceDefinition\Type; use Bundle\WebServiceBundle\ServiceDefinition\Type;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Symfony\Component\Config\Loader\FileLoader;
class XmlFileLoader extends FileLoader class XmlFileLoader extends FileLoader
{ {
@ -28,7 +28,6 @@ class XmlFileLoader extends FileLoader
public function load($file, $type = null) public function load($file, $type = null)
{ {
$path = $this->locator->locate($file); $path = $this->locator->locate($file);
$xml = $this->parseFile($path); $xml = $this->parseFile($path);
$definition = new ServiceDefinition(); $definition = new ServiceDefinition();
@ -53,9 +52,7 @@ class XmlFileLoader extends FileLoader
*/ */
protected function parseHeader(\SimpleXMLElement $node) protected function parseHeader(\SimpleXMLElement $node)
{ {
$header = new Header((string)$node['name'], $this->parseType($node->type)); return new Header((string)$node['name'], $this->parseType($node->type));
return $header;
} }
/** /**
@ -99,9 +96,7 @@ class XmlFileLoader extends FileLoader
$qname = explode(':', $node['xml-type'], 2); $qname = explode(':', $node['xml-type'], 2);
$xmlType = sprintf('{%s}%s', $namespaces[$qname[0]], $qname[1]); $xmlType = sprintf('{%s}%s', $namespaces[$qname[0]], $qname[1]);
$type = new Type((string)$node['php-type'], $xmlType, (string)$node['converter']); return new Type((string)$node['php-type'], $xmlType, (string)$node['converter']);
return $type;
} }
/** /**

View File

@ -19,13 +19,16 @@ class Method
private $arguments; private $arguments;
private $return; private $return;
public function __construct($name = null, $controller = null, array $arguments = array(), $return = null) public function __construct($name = null, $controller = null, array $arguments = array(), Type $return = null)
{ {
$this->setName($name); $this->setName($name);
$this->setController($controller); $this->setController($controller);
$this->setArguments($arguments); $this->setArguments($arguments);
if ($return) {
$this->setReturn($return); $this->setReturn($return);
} }
}
public function getName() public function getName()
{ {
@ -52,9 +55,9 @@ class Method
return $this->arguments; return $this->arguments;
} }
public function setArguments($arguments) public function setArguments(array $arguments)
{ {
$this->arguments = new Collection('getName'); $this->arguments = new Collection('getName', 'Bundle\WebServiceBundle\ServiceDefinition\Argument');
$this->arguments->addAll($arguments); $this->arguments->addAll($arguments);
} }
@ -63,7 +66,7 @@ class Method
return $this->return; return $this->return;
} }
public function setReturn($return) public function setReturn(Type $return)
{ {
$this->return = $return; $this->return = $return;
} }

View File

@ -38,6 +38,10 @@ class ServiceDefinition
{ {
$this->setName($name); $this->setName($name);
$this->setNamespace($namespace); $this->setNamespace($namespace);
$this->methods = new Collection('getName', 'Bundle\WebServiceBundle\ServiceDefinition\Method');
$this->headers = new Collection('getName', 'Bundle\WebServiceBundle\ServiceDefinition\Header');
$this->setMethods($methods); $this->setMethods($methods);
$this->setHeaders($headers); $this->setHeaders($headers);
} }
@ -85,9 +89,8 @@ class ServiceDefinition
/** /**
* @param array $methods * @param array $methods
*/ */
public function setMethods($methods) public function setMethods(array $methods)
{ {
$this->methods = new Collection('getName');
$this->methods->addAll($methods); $this->methods->addAll($methods);
} }
@ -102,9 +105,8 @@ class ServiceDefinition
/** /**
* @param array $headers * @param array $headers
*/ */
public function setHeaders($headers) public function setHeaders(array $headers)
{ {
$this->headers = new Collection('getName');
$this->headers->addAll($headers); $this->headers->addAll($headers);
} }
} }

View File

@ -54,8 +54,8 @@ class SoapRequest extends Request
parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content); parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content);
$this->soapMessage = null; $this->soapMessage = null;
$this->soapHeaders = new Collection('getName'); $this->soapHeaders = new Collection('getName', 'Bundle\WebServiceBundle\Soap\SoapHeader');
$this->soapAttachments = new Collection('getId'); $this->soapAttachments = new Collection('getId', 'Bundle\WebServiceBundle\Soap\SoapAttachment');
$this->setRequestFormat('soap'); $this->setRequestFormat('soap');
} }
@ -67,7 +67,7 @@ class SoapRequest extends Request
*/ */
public function getSoapMessage() public function getSoapMessage()
{ {
if($this->soapMessage === null) { if(null === $this->soapMessage) {
$this->soapMessage = $this->initializeSoapMessage(); $this->soapMessage = $this->initializeSoapMessage();
} }

View File

@ -35,7 +35,7 @@ class SoapResponse extends Response
{ {
parent::__construct(); parent::__construct();
$this->soapHeaders = new Collection('getName'); $this->soapHeaders = new Collection('getName', 'Bundle\WebServiceBundle\Soap\SoapHeader');
$this->setReturnValue($returnValue); $this->setReturnValue($returnValue);
} }

View File

@ -10,22 +10,14 @@
namespace Bundle\WebServiceBundle\Soap; namespace Bundle\WebServiceBundle\Soap;
/**
*
* @author Christian Kerl <christian-kerl@web.de>
*/
use Bundle\WebServiceBundle\ServiceDefinition\Type;
use Bundle\WebServiceBundle\ServiceDefinition\Dumper\FileDumper;
use Bundle\WebServiceBundle\Converter\ConverterRepository; use Bundle\WebServiceBundle\Converter\ConverterRepository;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
use Bundle\WebServiceBundle\SoapKernel; use Bundle\WebServiceBundle\ServiceDefinition\Type;
use Bundle\WebServiceBundle\Util\QName; use Bundle\WebServiceBundle\Util\QName;
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition; /**
* @author Christian Kerl <christian-kerl@web.de>
*/
class SoapServerFactory class SoapServerFactory
{ {
private $definition; private $definition;
@ -39,7 +31,7 @@ class SoapServerFactory
$this->converters = $converters; $this->converters = $converters;
} }
public function create(&$request, &$response) public function create($request, $response)
{ {
$server = new \SoapServer( $server = new \SoapServer(
$this->wsdlFile, $this->wsdlFile,
@ -53,7 +45,7 @@ class SoapServerFactory
return $server; return $server;
} }
private function createSoapServerTypemap(&$request, &$response) private function createSoapServerTypemap($request, $response)
{ {
$result = array(); $result = array();
@ -61,12 +53,12 @@ class SoapServerFactory
$result[] = array( $result[] = array(
'type_name' => $typeConverter->getTypeName(), 'type_name' => $typeConverter->getTypeName(),
'type_ns' => $typeConverter->getTypeNamespace(), 'type_ns' => $typeConverter->getTypeNamespace(),
'from_xml' => function($input) use (&$request, $typeConverter) { 'from_xml' => function($input) use ($request, $typeConverter) {
return $typeConverter->convertXmlToPhp($request, $input); return $typeConverter->convertXmlToPhp($request, $input);
}, },
'to_xml' => function($input) use (&$response, $typeConverter) { 'to_xml' => function($input) use ($response, $typeConverter) {
return $typeConverter->convertPhpToXml($response, $input); return $typeConverter->convertPhpToXml($response, $input);
} },
); );
} }
@ -93,7 +85,7 @@ class SoapServerFactory
private function addSoapServerClassmapEntry(&$classmap, Type $type) private function addSoapServerClassmapEntry(&$classmap, Type $type)
{ {
// TODO: fix this hack // TODO: fix this hack
if($type->getXmlType() === null) return; if(null === $type->getXmlType()) return;
$xmlType = QName::fromPackedQName($type->getXmlType())->getName(); $xmlType = QName::fromPackedQName($type->getXmlType())->getName();
$phpType = $type->getPhpType(); $phpType = $type->getPhpType();

View File

@ -40,6 +40,6 @@ class SoapRequestTest extends \PHPUnit_Framework_TestCase
private function loadRequestContentFixture($name) private function loadRequestContentFixture($name)
{ {
return file_get_contents(__DIR__ . '/fixtures/' . $name); return file_get_contents(__DIR__.'/../Fixtures/Soap/'.$name);
} }
} }

View File

@ -16,8 +16,8 @@ namespace Bundle\WebServiceBundle\Util;
*/ */
class Assert class Assert
{ {
const ARGUMENT_INVALID = "Argument '%s' is invalid!"; const ARGUMENT_INVALID = 'Argument "%s" is invalid.';
const ARGUMENT_NULL = "Argument '%s' can't be null!"; const ARGUMENT_NULL = 'Argument "%s" can not be null.';
public static function thatArgument($name, $condition, $message = self::ARGUMENT_INVALID) public static function thatArgument($name, $condition, $message = self::ARGUMENT_INVALID)
{ {
@ -28,6 +28,6 @@ class Assert
public static function thatArgumentNotNull($name, $value) public static function thatArgumentNotNull($name, $value)
{ {
self::thatArgument($name, $value != null, self::ARGUMENT_NULL); self::thatArgument($name, null !== $value, self::ARGUMENT_NULL);
} }
} }

View File

@ -13,16 +13,23 @@ namespace Bundle\WebServiceBundle\Util;
class Collection implements \IteratorAggregate, \Countable class Collection implements \IteratorAggregate, \Countable
{ {
private $elements = array(); private $elements = array();
private $keyPropertyGetter; private $getter;
private $class;
public function __construct($keyPropertyGetter) public function __construct($getter, $class = null)
{ {
$this->keyPropertyGetter = $keyPropertyGetter; $this->getter = $getter;
$this->class = $class;
} }
public function add($element) public function add($element)
{ {
$this->elements[call_user_func(array($element, $this->keyPropertyGetter))] = $element; if ($this->class && !$element instanceof $this->class) {
throw new \InvalidArgument(sprintf('Cannot add class "%s" because it is not an instance of "%s"', get_class($element), $class));
}
$getter = $this->getter;
$this->elements[$element->$getter()] = $element;
} }
public function addAll($elements) public function addAll($elements)
@ -52,7 +59,7 @@ class Collection implements \IteratorAggregate, \Countable
return count($this->elements); return count($this->elements);
} }
public function getIterator () public function getIterator()
{ {
return new \ArrayIterator($this->elements); return new \ArrayIterator($this->elements);
} }

View File

@ -11,11 +11,13 @@
namespace Bundle\WebServiceBundle\Util; namespace Bundle\WebServiceBundle\Util;
/** /**
*
* @author Christian Kerl <christian-kerl@web.de> * @author Christian Kerl <christian-kerl@web.de>
*/ */
class QName class QName
{ {
private $namespace;
private $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));
@ -23,9 +25,6 @@ class QName
return new self($matches[1], $matches[2]); return new self($matches[1], $matches[2]);
} }
private $namespace;
private $name;
public function __construct($namespace, $name) public function __construct($namespace, $name)
{ {
$this->namespace = $namespace; $this->namespace = $namespace;

View File

@ -42,8 +42,7 @@ class String
*/ */
public static function endsWith($str, $substr) public static function endsWith($str, $substr)
{ {
if(is_string($str) && is_string($substr) && strlen($str) >= strlen($substr)) if(is_string($str) && is_string($substr) && strlen($str) >= strlen($substr)) {
{
return $substr == substr($str, strlen($str) - strlen($substr)); return $substr == substr($str, strlen($str) - strlen($substr));
} }
} }

View File

@ -10,8 +10,9 @@
namespace Bundle\WebServiceBundle; namespace Bundle\WebServiceBundle;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Bundle\WebServiceBundle\DependencyInjection\Compiler\WebServiceResolverPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\HttpKernel\Bundle\Bundle;
/** /**
@ -21,4 +22,10 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/ */
class WebServiceBundle extends Bundle class WebServiceBundle extends Bundle
{ {
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new WebServiceResolverPass());
}
} }

View File

@ -10,16 +10,15 @@
namespace Bundle\WebServiceBundle; namespace Bundle\WebServiceBundle;
use Bundle\WebServiceBundle\Converter\ConverterRepository;
use Bundle\WebServiceBundle\ServiceBinding\MessageBinderInterface;
use Bundle\WebServiceBundle\ServiceBinding\ServiceBinder;
use Bundle\WebServiceBundle\ServiceDefinition\Dumper\DumperInterface;
use Bundle\WebServiceBundle\Soap\SoapServerFactory;
use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderInterface;
use Bundle\WebServiceBundle\Converter\ConverterRepository;
use Bundle\WebServiceBundle\ServiceBinding\ServiceBinder;
use Bundle\WebServiceBundle\ServiceBinding\MessageBinderInterface;
use Bundle\WebServiceBundle\ServiceDefinition\Dumper\DumperInterface;
use Bundle\WebServiceBundle\Soap\SoapServerFactory;
/** /**
* WebServiceContext. * WebServiceContext.
* *
@ -31,7 +30,6 @@ class WebServiceContext
private $requestMessageBinder; private $requestMessageBinder;
private $responseMessageBinder; private $responseMessageBinder;
private $serviceDefinitionLoader;
private $wsdlFileDumper; private $wsdlFileDumper;
private $options; private $options;
@ -40,9 +38,9 @@ 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) public function __construct(LoaderInterface $loader, DumperInterface $dumper, ConverterRepository $converterRepository, MessageBinderInterface $requestMessageBinder, MessageBinderInterface $responseMessageBinder, array $options = array())
{ {
$this->serviceDefinitionLoader = $loader; $this->loader = $loader;
$this->wsdlFileDumper = $dumper; $this->wsdlFileDumper = $dumper;
$this->converterRepository = $converterRepository; $this->converterRepository = $converterRepository;
@ -54,12 +52,12 @@ class WebServiceContext
public function getServiceDefinition() public function getServiceDefinition()
{ {
if($this->serviceDefinition === null) { if (null === $this->serviceDefinition) {
if(!$this->serviceDefinitionLoader->supports($this->options['resource'], $this->options['resource_type'])) { if (!$this->loader->supports($this->options['resource'], $this->options['resource_type'])) {
throw new \LogicException(); throw new \LogicException(sprintf('Cannot load "%s" (%s)', $this->options['resource'], $this->options['resource_type']));
} }
$this->serviceDefinition = $this->serviceDefinitionLoader->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']);
} }
@ -69,8 +67,8 @@ class WebServiceContext
public function getWsdlFile($endpoint = null) public function getWsdlFile($endpoint = null)
{ {
$id = $endpoint !== null ? '.' . md5($endpoint) : ''; $id = null !== $endpoint ? '.'. md5($endpoint) : '';
$file = sprintf('%s/%s.wsdl', $this->options['cache_dir'], $this->options['name'] . $id); $file = sprintf('%s/%s.wsdl', $this->options['cache_dir'], $this->options['name'].$id);
$cache = new ConfigCache($file, true); $cache = new ConfigCache($file, true);
if(!$cache->isFresh()) { if(!$cache->isFresh()) {
@ -87,7 +85,7 @@ class WebServiceContext
public function getServiceBinder() public function getServiceBinder()
{ {
if($this->serviceBinder === null) { if (null === $this->serviceBinder) {
$this->serviceBinder = new ServiceBinder($this->getServiceDefinition(), $this->requestMessageBinder, $this->responseMessageBinder); $this->serviceBinder = new ServiceBinder($this->getServiceDefinition(), $this->requestMessageBinder, $this->responseMessageBinder);
} }
@ -96,7 +94,7 @@ class WebServiceContext
public function getServerFactory() public function getServerFactory()
{ {
if($this->serverFactory === null) { if (null === $this->serverFactory) {
$this->serverFactory = new SoapServerFactory($this->getServiceDefinition(), $this->getWsdlFile(), $this->converterRepository); $this->serverFactory = new SoapServerFactory($this->getServiceDefinition(), $this->getWsdlFile(), $this->converterRepository);
} }

View File

@ -1,20 +1,19 @@
#!/usr/bin/env php #!/usr/bin/env php
<?php <?php
/* /*
* This file is part of the Symfony framework. * This file is part of the WebServiceBundle.
* *
* (c) Fabien Potencier <fabien@symfony.com> * (c) Christian Kerl <christian-kerl@web.de>
* *
* For the full copyright and license information, please view the LICENSE * This source file is subject to the MIT license that is bundled
* file that was distributed with this source code. * with this source code in the file LICENSE.
*/ */
/* /*
CAUTION: This file installs the dependencies needed to run the I18nRoutingBundle test suite. CAUTION: This file installs the dependencies needed to run the WebServiceBundle test suite.
https://github.com/BeSimple/I18nRoutingBundle https://github.com/BeSimple/BeSimpleSoapBundle
*/ */