Add 'src/BeSimple/SoapBundle/' from commit 'e99f707b105c0a0472260d8d42a5a14a7fb7a211'

git-subtree-dir: src/BeSimple/SoapBundle
git-subtree-mainline: 9a8d23fa23
git-subtree-split: e99f707b10
This commit is contained in:
Francis Besset 2013-07-19 17:00:11 +02:00
commit 9328c98935
94 changed files with 5786 additions and 0 deletions

4
src/BeSimple/SoapBundle/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build/*
phpunit.xml
vendor
*.swp

View File

@ -0,0 +1,17 @@
language: php
php:
- 5.3
- 5.4
- 5.5
matrix:
allow_failures:
- php: 5.5
before_script:
- composer self-update
- composer install --dev --prefer-source
notifications:
email: francis.besset@gmail.com

View File

@ -0,0 +1,33 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle;
use BeSimple\SoapBundle\DependencyInjection\Compiler\WebServiceResolverPass;
use BeSimple\SoapBundle\DependencyInjection\Compiler\TypeConverterPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* BeSimpleSoapBundle.
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class BeSimpleSoapBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new WebServiceResolverPass());
$container->addCompilerPass(new TypeConverterPass());
}
}

View File

@ -0,0 +1,4 @@
* [Christian Kerl](https://github.com/christiankerl)
* [Francis Besset](https://github.com/francisbesset)
* [Šarūnas Dubinskas](https://github.com/sarunas)
* [Craig Marvelley](https://github.com/craigmarvelley)

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle;
use BeSimple\SoapCommon\Cache as BaseCache;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class Cache
{
public function __construct($cacheDisabled, $type, $directory, $lifetime = null, $limit = null)
{
$isEnabled = (Boolean) $cacheDisabled ? BaseCache::DISABLED : BaseCache::ENABLED;
BaseCache::setEnabled($isEnabled);
if (BaseCache::ENABLED == BaseCache::isEnabled()) {
BaseCache::setType($type);
BaseCache::setDirectory($directory);
if (null !== $lifetime) {
BaseCache::setLifetime($lifetime);
}
if (null !== $limit) {
BaseCache::setLimit($limit);
}
}
}
}

View File

@ -0,0 +1,198 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\Controller;
use BeSimple\SoapBundle\Soap\SoapRequest;
use BeSimple\SoapBundle\Soap\SoapResponse;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class SoapWebServiceController extends ContainerAware
{
/**
* @var \SoapServer
*/
protected $soapServer;
/**
* @var \BeSimple\SoapBundle\Soap\SoapRequest
*/
protected $soapRequest;
/**
* @var \BeSimple\SoapBundle\Soap\SoapResponse
*/
protected $soapResponse;
/**
* @var \BeSimple\SoapBundle\ServiceBinding\ServiceBinder
*/
protected $serviceBinder;
/**
* @var array
*/
private $headers = array();
/**
* @return \BeSimple\SoapBundle\Soap\SoapResponse
*/
public function callAction($webservice)
{
$webServiceContext = $this->getWebServiceContext($webservice);
$this->serviceBinder = $webServiceContext->getServiceBinder();
$this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request'));
$this->soapServer = $webServiceContext
->getServerBuilder()
->withHandler($this)
->build()
;
ob_start();
$this->soapServer->handle($this->soapRequest->getSoapMessage());
return $this->getResponse()->setContent(ob_get_clean());
}
/**
* @return Symfony\Component\HttpFoundation\Response
*/
public function definitionAction($webservice)
{
$response = new Response($this->getWebServiceContext($webservice)->getWsdlFileContent(
$this->container->get('router')->generate(
'_webservice_call',
array('webservice' => $webservice),
true
)
));
$query = $this->container->get('request')->query;
if ($query->has('wsdl') || $query->has('WSDL')) {
$response->headers->set('Content-Type', 'application/wsdl+xml');
} else {
$response->headers->set('Content-Type', 'text/xml');
}
return $response;
}
/**
* This method gets called once for every SOAP header the \SoapServer received
* and afterwards once for the called SOAP operation.
*
* @param string $method The SOAP header or SOAP operation name
* @param array $arguments
*
* @return mixed
*/
public function __call($method, $arguments)
{
if ($this->serviceBinder->isServiceMethod($method)) {
// @TODO Add all SoapHeaders in SoapRequest
foreach ($this->headers as $name => $value) {
if ($this->serviceBinder->isServiceHeader($method, $name)) {
$this->soapRequest->getSoapHeaders()->add($this->serviceBinder->processServiceHeader($method, $name, $value));
}
}
$this->headers = null;
$this->soapRequest->attributes->add(
$this->serviceBinder->processServiceMethodArguments($method, $arguments)
);
// forward to controller
try {
$response = $this->container->get('http_kernel')->handle($this->soapRequest, HttpKernelInterface::SUB_REQUEST, false);
} catch (\Exception $e) {
$this->soapResponse = new Response(null, 500);
if ($e instanceof \SoapFault || $this->container->getParameter('kernel.debug')) {
throw $e;
}
throw new \SoapFault('Receiver', $e->getMessage());
}
$this->setResponse($response);
// add response soap headers to soap server
foreach ($response->getSoapHeaders() as $header) {
$this->soapServer->addSoapHeader($header->toNativeSoapHeader());
}
// return operation return value to soap server
return $this->serviceBinder->processServiceMethodReturnValue(
$method,
$response->getReturnValue()
);
} else {
// collect request soap headers
$this->headers[$method] = $arguments[0];
}
}
/**
* @return \BeSimple\SoapBundle\Soap\SoapRequest
*/
protected function getRequest()
{
return $this->soapRequest;
}
/**
* @return \BeSimple\SoapBundle\Soap\SoapResponse
*/
protected function getResponse()
{
return $this->soapResponse ?: $this->soapResponse = $this->container->get('besimple.soap.response');
}
/**
* Set the SoapResponse
*
* @param Response $response A response to check and set
*
* @return \BeSimple\SoapBundle\Soap\SoapResponse A valid SoapResponse
*
* @throws InvalidArgumentException If the given Response is not an instance of SoapResponse
*/
protected function setResponse(Response $response)
{
if (!$response instanceof SoapResponse) {
throw new \InvalidArgumentException('You must return an instance of BeSimple\SoapBundle\Soap\SoapResponse');
}
return $this->soapResponse = $response;
}
private function getWebServiceContext($webservice)
{
$context = sprintf('besimple.soap.context.%s', $webservice);
if (!$this->container->has($context)) {
throw new NotFoundHttpException(sprintf('No WebService with name "%s" found.', $webservice));
}
return $this->container->get($context);
}
}

View File

@ -0,0 +1,70 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Converter;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\Util\Assert;
use BeSimple\SoapBundle\Util\QName;
use BeSimple\SoapBundle\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] = $xmlType;
}
public function getXmlTypeMapping($phpType)
{
return isset($this->defaultTypeMap[$phpType]) ? $this->defaultTypeMap[$phpType] : null;
}
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) {
$xmlType = $this->getXmlTypeMapping($phpType);
}
$type->setXmlType($xmlType);
}
}
}

View File

@ -0,0 +1,55 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Converter;
use BeSimple\SoapBundle\Soap\SoapRequest;
use BeSimple\SoapBundle\Soap\SoapResponse;
use BeSimple\SoapBundle\Util\String;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class XopIncludeTypeConverter implements TypeConverterInterface
{
public function getTypeNamespace()
{
return 'http://www.w3.org/2001/XMLSchema';
}
public function getTypeName()
{
return 'base64Binary';
}
public function convertXmlToPhp(SoapRequest $request, $data)
{
$doc = new \DOMDocument();
$doc->loadXML($data);
$includes = $doc->getElementsByTagNameNS('http://www.w3.org/2004/08/xop/include', 'Include');
$include = $includes->item(0);
$ref = $include->getAttribute('href');
if (String::startsWith($ref, 'cid:')) {
$cid = urldecode(substr($ref, 4));
return $request->getSoapAttachments()->get($cid)->getContent();
}
return $data;
}
public function convertPhpToXml(SoapResponse $response, $data)
{
return $data;
}
}

View File

@ -0,0 +1,170 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\DependencyInjection;
use BeSimple\SoapCommon\Cache;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
* BeSimpleSoapExtension.
*
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class BeSimpleSoapExtension extends Extension
{
// maps config options to service suffix
private $bindingConfigToServiceSuffixMap = array(
'rpc-literal' => 'rpcliteral',
'document-wrapped' => 'documentwrapped',
);
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('loaders.xml');
$loader->load('converters.xml');
$loader->load('webservice.xml');
$processor = new Processor();
$configuration = new Configuration();
$config = $processor->process($configuration->getConfigTree(), $configs);
$this->registerCacheConfiguration($config['cache'], $container, $loader);
if (!empty($config['clients'])) {
$this->registerClientConfiguration($config['clients'], $container, $loader);
}
$container->setParameter('besimple.soap.definition.dumper.options.stylesheet', $config['wsdl_dumper']['stylesheet']);
foreach($config['services'] as $name => $serviceConfig) {
$serviceConfig['name'] = $name;
$this->createWebServiceContext($serviceConfig, $container);
}
}
private function registerCacheConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('soap.xml');
$config['type'] = $this->getCacheType($config['type']);
foreach (array('type', 'lifetime', 'limit') as $key) {
$container->setParameter('besimple.soap.cache.'.$key, $config[$key]);
}
}
private function registerClientConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('client.xml');
foreach ($config as $client => $options) {
$definition = new DefinitionDecorator('besimple.soap.client.builder');
$context = $container->setDefinition(sprintf('besimple.soap.client.builder.%s', $client), $definition);
$definition->replaceArgument(0, $options['wsdl']);
$defOptions = $container
->getDefinition('besimple.soap.client.builder')
->getArgument(1);
foreach (array('cache_type', 'user_agent') as $key) {
if (isset($options[$key])) {
$defOptions[$key] = $options[$key];
}
}
if (isset($defOptions['cache_type'])) {
$defOptions['cache_type'] = $this->getCacheType($defOptions['cache_type']);
}
$definition->replaceArgument(1, $defOptions);
if (!empty($options['classmap'])) {
$classmap = $this->createClientClassmap($client, $options['classmap'], $container);
$definition->replaceArgument(2, new Reference($classmap));
} else {
$definition->replaceArgument(2, null);
}
$this->createClient($client, $container);
}
}
private function createClientClassmap($client, array $classmap, ContainerBuilder $container)
{
$definition = new DefinitionDecorator('besimple.soap.classmap');
$context = $container->setDefinition(sprintf('besimple.soap.classmap.%s', $client), $definition);
$definition->setMethodCalls(array(
array('set', array($classmap)),
));
return sprintf('besimple.soap.classmap.%s', $client);
}
private function createClient($client, ContainerBuilder $container)
{
$definition = new DefinitionDecorator('besimple.soap.client');
$context = $container->setDefinition(sprintf('besimple.soap.client.%s', $client), $definition);
$definition->setFactoryService(sprintf('besimple.soap.client.builder.%s', $client));
}
private function createWebServiceContext(array $config, ContainerBuilder $container)
{
$bindingSuffix = $this->bindingConfigToServiceSuffixMap[$config['binding']];
unset($config['binding']);
$contextId = 'besimple.soap.context.'.$config['name'];
$definition = new DefinitionDecorator('besimple.soap.context.'.$bindingSuffix);
$context = $container->setDefinition($contextId, $definition);
if (isset($config['cache_type'])) {
$config['cache_type'] = $this->getCacheType($config['cache_type']);
}
$options = $container
->getDefinition('besimple.soap.context.'.$bindingSuffix)
->getArgument(5);
$definition->replaceArgument(5, array_merge($options, $config));
}
private function getCacheType($type)
{
switch ($type) {
case 'none':
return Cache::TYPE_NONE;
case 'disk':
return Cache::TYPE_DISK;
case 'memory':
return Cache::TYPE_MEMORY;
case 'disk_memory':
return Cache::TYPE_DISK_MEMORY;
}
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds tagged besimple.soap.converter services to besimple.soap.converter.repository service
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class TypeConverterPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('besimple.soap.converter.collection')) {
return;
}
$definition = $container->getDefinition('besimple.soap.converter.collection');
foreach ($container->findTaggedServiceIds('besimple.soap.converter') as $id => $attributes) {
$definition->addMethodCall('add', array(new Reference($id)));
}
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds tagged besimple.soap.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('besimple.soap.definition.loader.resolver')) {
return;
}
$definition = $container->getDefinition('besimple.soap.definition.loader.resolver');
foreach ($container->findTaggedServiceIds('besimple.soap.definition.loader') as $id => $attributes) {
$definition->addMethodCall('addLoader', array(new Reference($id)));
}
}
}

View File

@ -0,0 +1,143 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
/**
* WebServiceExtension configuration structure.
*
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class Configuration
{
private $cacheTypes = array('none', 'disk', 'memory', 'disk_memory');
/**
* Generates the configuration tree.
*
* @return \Symfony\Component\Config\Definition\ArrayNode The config tree
*/
public function getConfigTree()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('be_simple_soap');
$this->addCacheSection($rootNode);
$this->addClientSection($rootNode);
$this->addServicesSection($rootNode);
$this->addWsdlDumperSection($rootNode);
return $treeBuilder->buildTree();
}
private function addCacheSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->children()
->arrayNode('cache')
->addDefaultsIfNotSet()
->children()
->scalarNode('type')
->defaultValue('disk')
->validate()
->ifNotInArray($this->cacheTypes)
->thenInvalid(sprintf('The cache type has to be either %s', implode(', ', $this->cacheTypes)))
->end()
->end()
->scalarNode('lifetime')->defaultNull()->end()
->scalarNode('limit')->defaultNull()->end()
->end()
->end()
->end()
;
}
private function addClientSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->children()
->arrayNode('clients')
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('wsdl')->isRequired()->end()
->scalarNode('user_agent')->end()
->scalarNode('cache_type')
->validate()
->ifNotInArray($this->cacheTypes)
->thenInvalid(sprintf('The cache type has to be either %s', implode(', ', $this->cacheTypes)))
->end()
->end()
->arrayNode('classmap')
->useAttributeAsKey('name')
->prototype('scalar')
->end()
->end()
->end()
->end()
;
}
private function addServicesSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->children()
->arrayNode('services')
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('namespace')
->isRequired()
->end()
->scalarNode('resource')
->defaultValue('*')
->end()
->scalarNode('resource_type')
->defaultValue('annotation')
->end()
->scalarNode('binding')
->defaultValue('document-wrapped')
->validate()
->ifNotInArray(array('rpc-literal', 'document-wrapped'))
->thenInvalid("Service binding style has to be either 'rpc-literal' or 'document-wrapped'")
->end()
->end()
->scalarNode('cache_type')
->validate()
->ifNotInArray($this->cacheTypes)
->thenInvalid(sprintf('The cache type has to be either %s', implode(', ', $this->cacheTypes)))
->end()
->end()
->end()
->end()
->end()
;
}
private function addWsdlDumperSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->children()
->arrayNode('wsdl_dumper')
->addDefaultsIfNotSet()
->children()
->scalarNode('stylesheet')->defaultNull()
->end()
->end()
->end()
;
}
}

View File

@ -0,0 +1,56 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\EventListener;
use BeSimple\SoapBundle\Soap\SoapRequest;
use BeSimple\SoapBundle\Soap\SoapResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
/**
* SoapResponseListener.
*
* @author Francis Besset <francis.besset@gmail.com>
*/
class SoapResponseListener
{
/**
* @var SoapResponse
*/
protected $response;
/**
* Constructor.
*
* @param SoapResponse $response The SoapResponse instance
*/
public function __construct(SoapResponse $response)
{
$this->response = $response;
}
/**
* Set the controller result in SoapResponse.
*
* @param GetResponseForControllerResultEvent $event A GetResponseForControllerResultEvent instance
*/
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$request = $event->getRequest();
if (!$request instanceof SoapRequest) {
return;
}
$this->response->setReturnValue($event->getControllerResult());
$event->setResponse($this->response);
}
}

View File

@ -0,0 +1,6 @@
# BeSimpleSoapBundle [![Build Status](https://travis-ci.org/BeSimple/BeSimpleSoapBundle.png?branch=master)](https://travis-ci.org/BeSimple/BeSimpleSoapBundle)
The BeSimpleSoapBundle is a Symfony2 bundle to build WSDL and SOAP based web services.
It is based on the [ckWebServicePlugin](http://www.symfony-project.org/plugins/ckWebServicePlugin) for symfony.
Read about it on its [official homepage](http://besim.pl/SoapBundle/).

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<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="besimple.soap.client.builder.class">BeSimple\SoapBundle\Soap\SoapClientBuilder</parameter>
<parameter key="besimple.soap.classmap.class">BeSimple\SoapCommon\Classmap</parameter>
</parameters>
<services>
<service id="besimple.soap.client.builder" class="%besimple.soap.client.builder.class%" abstract="true">
<argument /> <!-- wsdl URI -->
<argument type="collection">
<argument key="debug">%kernel.debug%</argument>
</argument>
<argument type="service" id="besimple.soap.classmap" />
<argument type="service" id="besimple.soap.converter.collection" />
<argument type="service" id="besimple.soap.cache" />
</service>
<service id="besimple.soap.client" factory-service="besimple.soap.client.builder" factory-method="build" class="%besimple.soap.client.builder.class%" abstract="true" />
<service id="besimple.soap.classmap" class="%besimple.soap.classmap.class%" abstract="true" />
</services>
</container>

View File

@ -0,0 +1,24 @@
<?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="besimple.soap.converter.collection.class">BeSimple\SoapCommon\Converter\TypeConverterCollection</parameter>
<parameter key="besimple.soap.converter.date_time.class">BeSimple\SoapCommon\Converter\DateTimeTypeConverter</parameter>
<parameter key="besimple.soap.converter.date.class">BeSimple\SoapCommon\Converter\DateTypeConverter</parameter>
</parameters>
<services>
<service id="besimple.soap.converter.collection" class="%besimple.soap.converter.collection.class%" />
<service id="besimple.soap.converter.date_time" class="%besimple.soap.converter.date_time.class%" public="false">
<tag name="besimple.soap.converter" />
</service>
<service id="besimple.soap.converter.date" class="%besimple.soap.converter.date.class%" public="false">
<tag name="besimple.soap.converter" />
</service>
</services>
</container>

View File

@ -0,0 +1,39 @@
<?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="besimple.soap.definition.loader.resolver.class">Symfony\Component\Config\Loader\LoaderResolver</parameter>
<parameter key="besimple.soap.definition.loader.class">Symfony\Component\Config\Loader\DelegatingLoader</parameter>
<parameter key="besimple.soap.definition.loader.annot_dir.class">BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationDirectoryLoader</parameter>
<parameter key="besimple.soap.definition.loader.annot_file.class">BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationFileLoader</parameter>
<parameter key="besimple.soap.definition.loader.annot_class.class">BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationClassLoader</parameter>
<parameter key="besimple.soap.definition.loader.annot_complextype.class">BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader</parameter>
</parameters>
<services>
<service id="besimple.soap.definition.loader.resolver" class="%besimple.soap.definition.loader.resolver.class%" public="false" />
<service id="besimple.soap.definition.loader" class="%besimple.soap.definition.loader.class%">
<argument type="service" id="besimple.soap.definition.loader.resolver" />
</service>
<service id="besimple.soap.definition.loader.annot_file" class="%besimple.soap.definition.loader.annot_file.class%" public="false">
<tag name="besimple.soap.definition.loader" />
<argument type="service" id="file_locator" />
<argument type="service" id="besimple.soap.definition.loader.annot_class" />
</service>
<service id="besimple.soap.definition.loader.annot_class" class="%besimple.soap.definition.loader.annot_class.class%" public="false">
<tag name="besimple.soap.definition.loader" />
<argument type="service" id="annotation_reader" />
</service>
<service id="besimple.soap.definition.loader.annot_complextype" class="%besimple.soap.definition.loader.annot_complextype.class%" public="false">
<argument type="service" id="annotation_reader" />
</service>
</services>
</container>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="_webservice_call" pattern="/{webservice}">
<default key="_controller">BeSimpleSoapBundle:SoapWebService:Call</default>
<default key="_format">xml</default>
<requirement key="_method">POST</requirement>
</route>
<route id="_webservice_definition" pattern="/{webservice}">
<default key="_controller">BeSimpleSoapBundle:SoapWebService:Definition</default>
<default key="_format">xml</default>
<requirement key="_method">GET</requirement>
</route>
</routes>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<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="besimple.soap.cache.class">BeSimple\SoapBundle\Cache</parameter>
<parameter key="besimple.soap.cache.dir">%kernel.cache_dir%/besimple/soap</parameter>
</parameters>
<services>
<service id="besimple.soap.cache" class="%besimple.soap.cache.class%">
<argument>%kernel.debug%</argument>
<argument>%besimple.soap.cache.type%</argument>
<argument>%besimple.soap.cache.dir%/php</argument>
<argument>%besimple.soap.cache.lifetime%</argument>
<argument>%besimple.soap.cache.limit%</argument>
</service>
</services>
</container>

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<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="besimple.soap.response.class">BeSimple\SoapBundle\Soap\SoapResponse</parameter>
<parameter key="besimple.soap.response.listener.class">BeSimple\SoapBundle\EventListener\SoapResponseListener</parameter>
<parameter key="besimple.soap.context.class">BeSimple\SoapBundle\WebServiceContext</parameter>
<parameter key="besimple.soap.binder.request_header.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestHeaderMessageBinder</parameter>
<parameter key="besimple.soap.binder.request.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder</parameter>
<parameter key="besimple.soap.binder.response.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralResponseMessageBinder</parameter>
<parameter key="besimple.soap.binder.request.documentwrapped.class">BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedRequestMessageBinder</parameter>
<parameter key="besimple.soap.binder.request_header.documentwrapped.class">BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedRequestHeaderMessageBinder</parameter>
<parameter key="besimple.soap.binder.response.documentwrapped.class">BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedResponseMessageBinder</parameter>
<parameter key="besimple.soap.definition.dumper.wsdl.rpcliteral.class">BeSimple\SoapBundle\ServiceDefinition\Dumper\WsdlDumper</parameter>
<parameter key="besimple.soap.type.repository.class">BeSimple\SoapBundle\Converter\TypeRepository</parameter>
<parameter key="besimple.soap.server.classmap.class">BeSimple\SoapCommon\Classmap</parameter>
</parameters>
<services>
<service id="besimple.soap.response" class="%besimple.soap.response.class%" />
<service id="besimple.soap.response.listener" class="%besimple.soap.response.listener.class%">
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" />
<argument type="service" id="besimple.soap.response" />
</service>
<service id="besimple.soap.context.rpcliteral" class="%besimple.soap.context.class%" abstract="true">
<argument type="service" id="besimple.soap.definition.loader" />
<argument type="service" id="besimple.soap.definition.dumper.wsdl.rpcliteral" />
<argument type="service" id="besimple.soap.server.classmap" />
<argument type="service" id="besimple.soap.type.repository" />
<argument type="service" id="besimple.soap.converter.collection" />
<argument type="collection">
<argument key="cache_dir">%besimple.soap.cache.dir%</argument>
<argument key="debug">%kernel.debug%</argument>
<argument key="cache_type">null</argument>
<argument key="binder_request_header_class">%besimple.soap.binder.request_header.rpcliteral.class%</argument>
<argument key="binder_request_class">%besimple.soap.binder.request.rpcliteral.class%</argument>
<argument key="binder_response_class">%besimple.soap.binder.response.rpcliteral.class%</argument>
</argument>
<argument type="service" id="besimple.soap.cache" />
</service>
<service id="besimple.soap.context.documentwrapped" class="%besimple.soap.context.class%" abstract="true">
<argument type="service" id="besimple.soap.definition.loader" />
<argument type="service" id="besimple.soap.definition.dumper.wsdl.documentwrapped" />
<argument type="service" id="besimple.soap.server.classmap" />
<argument type="service" id="besimple.soap.type.repository" />
<argument type="service" id="besimple.soap.converter.collection" />
<argument type="collection">
<argument key="cache_dir">%besimple.soap.cache.dir%</argument>
<argument key="debug">%kernel.debug%</argument>
<argument key="cache_type">null</argument>
<argument key="binder_request_header_class">%besimple.soap.binder.request_header.documentwrapped.class%</argument>
<argument key="binder_request_class">%besimple.soap.binder.request.documentwrapped.class%</argument>
<argument key="binder_response_class">%besimple.soap.binder.response.documentwrapped.class%</argument>
</argument>
<argument type="service" id="besimple.soap.cache" />
</service>
<service id="besimple.soap.definition.dumper.wsdl.rpcliteral" class="%besimple.soap.definition.dumper.wsdl.rpcliteral.class%">
<argument type="service" id="besimple.soap.definition.loader.annot_complextype" />
<argument type="service" id="besimple.soap.type.repository" />
<argument type="collection">
<argument key="stylesheet">%besimple.soap.definition.dumper.options.stylesheet%</argument>
</argument>
</service>
<service id="besimple.soap.server.classmap" class="%besimple.soap.server.classmap.class%" public="false" />
<service id="besimple.soap.type.repository" class="%besimple.soap.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>boolean</argument>
<argument>xsd:boolean</argument>
</call>
<call method="addDefaultTypeMapping">
<argument>int</argument>
<argument>xsd:int</argument>
</call>
<call method="addDefaultTypeMapping">
<argument>float</argument>
<argument>xsd:float</argument>
</call>
<call method="addDefaultTypeMapping">
<argument>date</argument>
<argument>xsd:date</argument>
</call>
<call method="addDefaultTypeMapping">
<argument>dateTime</argument>
<argument>xsd:dateTime</argument>
</call>
</service>
</services>
</container>

View File

View File

@ -0,0 +1,26 @@
Cache
=====
Configuration
-------------
Configure the cache of PHP Soap WSDL in your config file:
.. code-block:: yaml
# app/config/config.yml
be_simple_soap:
cache:
type: disk
lifetime: 86400
limit: 5
The cache type can be: **none**, **disk** (default value), **memory**, **disk_memory**.
The lifetime in seconds of a WSDL file in the cache (**86400 is the default value by PHP**).
The limit is the maximum number of in-memory cached WSDL files (**5 is the default value by PHP**).
The WSDL files cached are written in cache folder of your Symfony2 application.
If you want more information you can visit the following page `PHP Soap runtime configuration <http://www.php.net/manual/en/soap.configuration.php>`_.

View File

@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
#
# BeSimpleSoapBundle documentation build configuration file, created by
# sphinx-quickstart on Mon Aug 1 22:24:10 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
from sphinx.highlighting import lexers
from pygments.lexers.web import PhpLexer
lexers['php'] = PhpLexer(startinline=True)
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath('_exts'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'BeSimpleSoapBundle'
copyright = u'2011, Christian Kerl, Francis Besset'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0.0'
# The full version, including alpha/beta/rc tags.
release = '1.0.0-DEV'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
#pygments_style = 'sphinx'
pygments_style = 'perldoc'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'BeSimpleSoapBundledoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'BeSimpleSoapBundle.tex', u'BeSimpleSoapBundle Documentation',
u'Christian Kerl, Francis Besset', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'besimplesoapbundle', u'BeSimpleSoapBundle Documentation',
[u'Christian Kerl, Francis Besset'], 1)
]
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}

View File

@ -0,0 +1,37 @@
==================
BeSimpleSoapBundle
==================
The BeSimpleSoapBundle is a Symfony2 bundle to build WSDL and SOAP based web services.
It is based on the `ckWebServicePlugin <http://www.symfony-project.org/plugins/ckWebServicePlugin>`_ for symfony.
---------------
Reference Guide
---------------
.. toctree::
:maxdepth: 1
:numbered:
requirements
installation_composer
installation_deps
cache
----------
SoapServer
----------
.. toctree::
:maxdepth: 1
:numbered:
soapserver/configuration
soapserver/types
soapserver/tutorials
----------
SoapClient
----------
Coming soon.

View File

@ -0,0 +1,55 @@
Installation with Composer
==========================
Add `besimple/soap-bundle <https://packagist.org/packages/besimple/soap-bundle>`_ (with vendors) in your composer.json:
.. code-block:: json
{
"require": {
"besimple/soap-bundle": "dev-master",
"besimple/soap-common": "dev-master",
"ass/xmlsecurity": "dev-master"
}
}
To install the server please add `besimple/soap-server <https://packagist.org/packages/besimple/soap-server>`_ in your composer.json:
.. code-block:: json
{
"require": {
"besimple/soap-server": "dev-master"
}
}
To install the client please add `besimple/soap-client <https://packagist.org/packages/besimple/soap-client>`_ in your composer.json:
.. code-block:: json
{
"require": {
"besimple/soap-client": "dev-master"
}
}
Run this command to download the new vendors:
.. code-block:: bash
$ php composer.phar self-update
$ php composer.phar update
Enable the `BeSimpleSoapBundle <https://github.com/BeSimple/BeSimpleSoapBundle>`_ in your Kernel class
.. code-block:: php
// app/AppKernel.php
public function registerBundles()
{
return array(
// ...
new BeSimple\SoapBundle\BeSimpleSoapBundle(),
// ...
);
}

View File

@ -0,0 +1,91 @@
Installation with deps file (deprecated)
========================================
**The installation with deps file is deprecated.
Please prefer install the** `BeSimpleSoapBundle <https://github.com/BeSimple/BeSimpleSoapBundle>`_ **with** `Composer <http://getcomposer.org>`_.
Download `BeSimple\\SoapCommon <http://github.com/BeSimple/BeSimpleSoapCommon>`_ and `BeSimple\\SoapServer <http://github.com/BeSimple/BeSimpleSoapServer>`_ (only for the server part) and/or `BeSimple\\SoapClient <http://github.com/BeSimple/BeSimpleSoapClient>`_ (only for ther client part).
.. code-block:: ini
; deps file
[BeSimple\SoapCommon]
git=https://github.com/BeSimple/BeSimpleSoapCommon.git
target=besimple-soapcommon
[BeSimple\SoapClient]
git=https://github.com/BeSimple/BeSimpleSoapClient.git
target=besimple-soapclient
[BeSimple\SoapServer]
git=https://github.com/BeSimple/BeSimpleSoapServer.git
target=besimple-soapserver
Add `BeSimple` libraries in autoload.php
.. code-block:: php
// app/autoload.php
$loader->registerNamespaces(array(
'BeSimple\\SoapCommon' => __DIR__.'/../vendor/besimple-soapcommon/src',
'BeSimple\\SoapServer' => __DIR__.'/../vendor/besimple-soapserver/src',
'BeSimple\\SoapClient' => __DIR__.'/../vendor/besimple-soapclient/src',
// your other namespaces
));
Download `Zend\\Soap <http://github.com/BeSimple/zend-soap>`_ and `Zend\\Mime <http://github.com/BeSimple/zend-mime>`_ or add in `deps` file. `Zend` library is required only for the server part.
.. code-block:: ini
; deps file
[Zend\Soap]
git=http://github.com/BeSimple/zend-soap.git
target=/zend-framework/library/Zend/Soap
[Zend\Mime]
git=http://github.com/BeSimple/zend-mime.git
target=/zend-framework/library/Zend/Mime
Add `Zend` library in autoload.php
.. code-block:: php
// app/autoload.php
$loader->registerNamespaces(array(
'Zend' => __DIR__.'/../vendor/zend-framework/library',
// your other namespaces
));
`Download <http://github.com/BeSimple/BeSimpleSoapBundle>`_ the bundle or add in `deps` file
.. code-block:: ini
; deps file
[BeSimpleSoapBundle]
git=http://github.com/BeSimple/BeSimpleSoapBundle.git
target=/bundles/BeSimple/SoapBundle
Add `BeSimple` in autoload.php
.. code-block:: php
// app/autoload.php
$loader->registerNamespaces(array(
'BeSimple' => __DIR__.'/../vendor/bundles',
// your other namespaces
));
Add `BeSimpleSoapBundle` in your Kernel class
.. code-block:: php
// app/AppKernel.php
public function registerBundles()
{
return array(
// ...
new BeSimple\SoapBundle\BeSimpleSoapBundle(),
// ...
);
}

View File

@ -0,0 +1,4 @@
Requirements
============
Install and enable PHP's `SOAP extension <http://www.php.net/manual/en/soap.setup.php>`.

View File

@ -0,0 +1,70 @@
Configuration
=============
Routing
-------
Include the `BeSimpleSoapBundle`'s routing configuration in your routing file (you can choose the prefix arbitrarily):
.. code-block:: yaml
# app/config/routing.yml
_besimple_soap:
resource: "@BeSimpleSoapBundle/Resources/config/routing/webservicecontroller.xml"
prefix: /ws
Config
------
Configure your first web service in your config file:
.. code-block:: yaml
# app/config/config.yml
be_simple_soap:
services:
DemoApi:
namespace: http://mysymfonyapp.com/ws/DemoApi/1.0/
binding: rpc-literal
resource: "@AcmeDemoBundle/Controller/DemoController.php"
resource_type: annotation
Annotations for Controllers
---------------------------
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use Symfony\Component\DependencyInjection\ContainerAware;
class DemoController extends ContainerAware
{
/**
* @Soap\Method("hello")
* @Soap\Param("name", phpType = "string")
* @Soap\Result(phpType = "string")
*/
public function helloAction($name)
{
return sprintf('Hello %s!', $name);
}
/**
* @Soap\Method("goodbye")
* @Soap\Param("name", phpType = "string")
* @Soap\Result(phpType = "string")
*/
public function goodbyeAction($name)
{
return $this->container->get('besimple.soap.response')->setReturnValue(sprintf('Goodbye %s!', $name));
}
}
Get your WSDL
-------------
To access your WSDL go to the following address: http://localhost/app_dev.php/ws/DemoApi?wsdl
To read the WSDL in your browser you can call this address (without `wsdl` parameter): http://localhost/app_dev.php/ws/DemoApi

View File

@ -0,0 +1,25 @@
Array
=====
Controller
----------
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use Symfony\Component\DependencyInjection\ContainerAware;
class DemoController extends ContainerAware
{
/**
* @Soap\Method("hello")
* @Soap\Param("names", phpType = "string[]")
* @Soap\Result(phpType = "string")
*/
public function helloAction(array $names)
{
return "Hello ".implode(', ', $names);
}
}

View File

@ -0,0 +1,99 @@
Associative Array
=================
Pre-existent Type
-----------------
+------------------------------------------------+-----------------+
| Php Type | Value Type |
+================================================+=================+
| BeSimple\\SoapCommon\\Type\\KeyValue\\String | String |
+------------------------------------------------+-----------------+
| BeSimple\\SoapCommon\\Type\\KeyValue\\Boolean | Boolean |
+------------------------------------------------+-----------------+
| BeSimple\\SoapCommon\\Type\\KeyValue\\Int | Int |
+------------------------------------------------+-----------------+
| BeSimple\\SoapCommon\\Type\\KeyValue\\Float | Float |
+------------------------------------------------+-----------------+
| BeSimple\\SoapCommon\\Type\\KeyValue\\Date | DateTime object |
+------------------------------------------------+-----------------+
| BeSimple\\SoapCommon\\Type\\KeyValue\\DateTime | DateTime object |
+------------------------------------------------+-----------------+
Controller
----------
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use Symfony\Component\DependencyInjection\ContainerAware;
class DemoController extends ContainerAware
{
/**
* @Soap\Method("returnAssocArray")
* @Soap\Result(phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]")
*/
public function assocArrayOfStringAction()
{
return array(
'foo' => 'bar',
'bar' => 'foo',
);
}
/**
* @Soap\Method("sendAssocArray")
* @Soap\Param("assocArray", phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]")
* @Soap\Return(phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]")
*/
public function assocArrayOfStringAction(array $assocArray)
{
// The $assocArray it's a real associative array
// var_dump($assocArray);die;
return $assocArray;
}
}
How to create my Associative Array?
-----------------------------------
.. code-block:: php
namespace Acme\DemoBundle\Soap\Type;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use BeSimple\SoapCommon\Type\AbstractKeyValue;
class User extends AbstractKeyValue
{
/**
* @Soap\ComplexType("Acme\DemoBundle\Entity\User")
*/
protected $value;
}
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use Acme\DemoBundle\Entity\User;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use Symfony\Component\DependencyInjection\ContainerAware;
class DemoController extends ContainerAware
{
/**
* @Soap\Method("getUsers")
* @Soap\Result(phpType = "Acme\DemoBundle\Soap\Type\User[]")
*/
public function getUsers()
{
return array(
'user1' => new User('user1', 'user1@user.com'),
'user2' => new User('user2', 'user2@user.com'),
);
}

View File

@ -0,0 +1,137 @@
Complex Type
============
This tutorial explains how to do to return a complex type.
If your SOAP function takes a complex type as input, this tutorial is
valid. You'll just have to adapt the input parameters of your method.
Controller
----------
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use Symfony\Component\DependencyInjection\ContainerAware;
class DemoController extends ContainerAware
{
/**
* @Soap\Method("getUser")
* @Soap\Param("name", phpType = "string")
* @Soap\Result(phpType = "Acme\DemoBundle\Entity\User")
*/
public function getUserAction($name)
{
$user = $this->container->getDoctrine()->getRepository('MyApp:User')->findOneBy(array(
'name' => $name,
));
if (!$user) {
throw new \SoapFault('USER_NOT_FOUND', sprintf('The user with the name "%s" can not be found', $name));
}
return $user;
}
}
User class
----------
You can expose only the properties (public, protected or private) of a complex type.
**For performance reasons, we advise to create getter and setter for each property.**
.. code-block:: php
namespace Acme\DemoBundle\Entity;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
class User
{
/**
* @Soap\ComplexType("string")
*/
public $firstname;
/**
* @Soap\ComplexType("string")
*/
public $lastname;
/**
* @Soap\ComplexType("int", nillable=true)
*/
private $id;
/**
* @Soap\ComplexType("string")
*/
private $username;
/**
* @Soap\ComplexType("string")
*/
private $email;
/**
* @Soap\ComplexType("boolean")
*/
private $newsletter;
public function getId()
{
return $this->id;
}
public function getUsername()
{
return $this->username;
}
public function getEmail()
{
return $this->email;
}
public function getFirstname()
{
return $this->firstname;
}
public function setFirstname($firstname)
{
$this->firstname = $firstname;
}
public function getLastname()
{
return $this->lastname;
}
public function setLastname($lastname)
{
$this->lastname = $lastname;
}
public function hasNewsletter()
{
return $this->newsletter;
}
public function setNewsletter($newsletter)
{
$this->newletter = (Boolean) $newsletter
}
}
ComplexType
-----------
`ComplexType` accepts the following options:
* nillable: To specify that the value can be null

View File

@ -0,0 +1,89 @@
Header
======
Controller
----------
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use Symfony\Component\DependencyInjection\ContainerAware;
class DemoController extends ContainerAware
{
/**
* @Soap\Method("hello")
* @Soap\Header("api_key", phpType = "string")
* @Soap\Param("names", phpType = "string[]")
* @Soap\Result(phpType = "string")
*/
public function helloAction(array $names)
{
$soapHeaders = $this->container->get('request')->getSoapHeaders();
// You can use '1234' !== (string) $soapHeaders->get('api_key')
if (!$soapHeaders->has('api_key') || '1234' !== $soapHeaders->get('api_key')->getData()) {
throw new \SoapFault("INVALID_API_KEY", "The api_key is invalid.");
}
return "Hello ".implode(', ', $names);
}
}
Global header
-------------
If you want use a header for all actions of your controller you can declare the header like this:
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @Soap\Header("api_key", phpType = "string")
*/
class DemoController extends ContainerAware
{
/**
* @Soap\Method("hello")
* @Soap\Param("names", phpType = "string[]")
* @Soap\Result(phpType = "string")
*/
public function helloAction(array $names)
{
return "Hello ".implode(', ', $names);
}
/**
* @Soap\Method("welcome")
* @Soap\Param("names", phpType = "string[]")
* @Soap\Result(phpType = "string")
*/
public function welcomeAction($names)
{
return "Welcome ".implode(', ', $names);
}
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
$this->checkApiKeyHeader();
}
private function checkApiKeyHeader()
{
$soapHeaders = $this->container->get('request')->getSoapHeaders();
// You can use '1234' !== (string) $soapHeaders->get('api_key')
if (!$soapHeaders->has('api_key') || '1234' !== $soapHeaders->get('api_key')->getData()) {
throw new \SoapFault("INVALID_API_KEY", "The api_key is invalid.");
}
}
}

View File

@ -0,0 +1,10 @@
Tutorials
=========
.. toctree::
:maxdepth: 1
tutorial/array
tutorial/associative_array
tutorial/complex_type
tutorial/header

View File

@ -0,0 +1,25 @@
Types available
===============
+----------+-----------------+
| Php Type | XML Type |
+==========+=================+
| string | `xsd:string`_ |
+----------+-----------------+
| boolean | `xsd:boolean`_ |
+----------+-----------------+
| int | `xsd:int`_ |
+----------+-----------------+
| float | `xsd:float`_ |
+----------+-----------------+
| date | `xsd:date`_ |
+----------+-----------------+
| dateTime | `xsd:dateTime`_ |
+----------+-----------------+
.. _`xsd:string`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#string
.. _`xsd:boolean`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#boolean
.. _`xsd:int`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#int
.. _`xsd:float`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#float
.. _`xsd:date`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#date
.. _`xsd:dateTime`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#dateTime

View File

@ -0,0 +1,7 @@
Copyright (c) 2010-2013 Christian Kerl, Francis Besset
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,35 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class DocumentLiteralWrappedRequestMessageBinder implements MessageBinderInterface
{
public function processMessage(Method $messageDefinition, $message)
{
if(count($message) > 1) {
throw new \InvalidArgumentException();
}
$result = array();
$message = $message[0];
foreach($messageDefinition->getArguments() as $argument) {
$result[$argument->getName()] = $message->{$argument->getName()};
}
return $result;
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class DocumentLiteralWrappedResponseMessageBinder implements MessageBinderInterface
{
public function processMessage(Method $messageDefinition, $message)
{
$result = new \stdClass();
$result->{$messageDefinition->getName().'Result'} = $message;
return $result;
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
interface MessageBinderInterface
{
/**
* @param Method $messageDefinition
* @param mixed $message
*
* @return mixed
*/
function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array());
}

View File

@ -0,0 +1,33 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class RpcLiteralRequestHeaderMessageBinder extends RpcLiteralRequestMessageBinder
{
private $header;
public function setHeader($header)
{
$this->header = $header;
}
public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
{
$headerDefinition = $messageDefinition->getHeaders()->get($this->header);
return $this->processType($headerDefinition->getType()->getPhpType(), $message, $definitionComplexTypes);
}
}

View File

@ -0,0 +1,118 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
use BeSimple\SoapCommon\Util\MessageBinder;
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class RpcLiteralRequestMessageBinder implements MessageBinderInterface
{
private $messageRefs = array();
private $definitionComplexTypes;
public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
{
$this->definitionComplexTypes = $definitionComplexTypes;
$result = array();
$i = 0;
foreach ($messageDefinition->getArguments() as $argument) {
if (isset($message[$i])) {
$result[$argument->getName()] = $this->processType($argument->getType()->getPhpType(), $message[$i]);
}
$i++;
}
return $result;
}
protected function processType($phpType, $message)
{
$isArray = false;
if (preg_match('/^([^\[]+)\[\]$/', $phpType, $match)) {
$isArray = true;
$array = array();
$phpType = $match[1];
}
// @TODO Fix array reference
if (isset($this->definitionComplexTypes[$phpType])) {
if ($isArray) {
if (isset($message->item)) {
foreach ($message->item as $complexType) {
$array[] = $this->checkComplexType($phpType, $complexType);
}
// See https://github.com/BeSimple/BeSimpleSoapBundle/issues/29
if (in_array('BeSimple\SoapCommon\Type\AbstractKeyValue', class_parents($phpType))) {
$assocArray = array();
foreach ($array as $keyValue) {
$assocArray[$keyValue->getKey()] = $keyValue->getValue();
}
$array = $assocArray;
}
}
$message = $array;
} else {
$message = $this->checkComplexType($phpType, $message);
}
} elseif ($isArray) {
if (isset($message->item)) {
$message = $message->item;
} else {
$message = $array;
}
}
return $message;
}
protected function checkComplexType($phpType, $message)
{
$hash = spl_object_hash($message);
if (isset($this->messageRefs[$hash])) {
return $this->messageRefs[$hash];
}
$this->messageRefs[$hash] = $message;
$messageBinder = new MessageBinder($message);
foreach ($this->definitionComplexTypes[$phpType] as $type) {
$property = $type->getName();
$value = $messageBinder->readProperty($property);
if (null !== $value) {
$value = $this->processType($type->getValue(), $value);
$messageBinder->writeProperty($property, $value);
}
if (!$type->isNillable() && null === $value) {
throw new \SoapFault('SOAP_ERROR_COMPLEX_TYPE', sprintf('"%s:%s" cannot be null.', ucfirst($phpType), $type->getName()));
}
}
return $message;
}
}

View File

@ -0,0 +1,103 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
use BeSimple\SoapCommon\Util\MessageBinder;
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class RpcLiteralResponseMessageBinder implements MessageBinderInterface
{
private $messageRefs = array();
private $definitionComplexTypes;
public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
{
$this->definitionComplexTypes = $definitionComplexTypes;
return $this->processType($messageDefinition->getReturn()->getPhpType(), $message);
}
private function processType($phpType, $message)
{
$isArray = false;
if (preg_match('/^([^\[]+)\[\]$/', $phpType, $match)) {
$isArray = true;
$phpType = $match[1];
}
if (isset($this->definitionComplexTypes[$phpType])) {
if ($isArray) {
$array = array();
// See https://github.com/BeSimple/BeSimpleSoapBundle/issues/29
if (is_array($message) && in_array('BeSimple\SoapCommon\Type\AbstractKeyValue', class_parents($phpType))) {
$keyValue = array();
foreach ($message as $key => $value) {
$keyValue[] = new $phpType($key, $value);
}
$message = $keyValue;
}
foreach ($message as $complexType) {
$array[] = $this->checkComplexType($phpType, $complexType);
}
$message = $array;
} else {
$message = $this->checkComplexType($phpType, $message);
}
}
return $message;
}
private function checkComplexType($phpType, $message)
{
$hash = spl_object_hash($message);
if (isset($this->messageRefs[$hash])) {
return $this->messageRefs[$hash];
}
$this->messageRefs[$hash] = $message;
if (!$message instanceof $phpType) {
throw new \InvalidArgumentException(sprintf('The instance class must be "%s", "%s" given.', get_class($message), $phpType));
}
$messageBinder = new MessageBinder($message);
foreach ($this->definitionComplexTypes[$phpType] as $type) {
$property = $type->getName();
$value = $messageBinder->readProperty($property);
if (null !== $value) {
$value = $this->processType($type->getValue(), $value);
$messageBinder->writeProperty($property, $value);
}
if (!$type->isNillable() && null === $value) {
throw new \InvalidArgumentException(sprintf('"%s::%s" cannot be null.', $phpType, $type->getName()));
}
}
return $message;
}
}

View File

@ -0,0 +1,124 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Header;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\Soap\SoapHeader;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class ServiceBinder
{
/**
* @var \BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition
*/
private $definition;
/**
* @var \BeSimple\SoapBundle\ServiceBinding\MessageBinderInterface
*/
private $requestHeaderMessageBinder;
/**
* @var \BeSimple\SoapBundle\ServiceBinding\MessageBinderInterface
*/
private $requestMessageBinder;
/**
* @var \BeSimple\SoapBundle\ServiceBinding\MessageBinderInterface
*/
private $responseMessageBinder;
/**
* @param ServiceDefinition $definition
* @param MessageBinderInterface $requestHeaderMessageBinder
* @param MessageBinderInterface $requestMessageBinder
* @param MessageBinderInterface $responseMessageBinder
*/
public function __construct(ServiceDefinition $definition, MessageBinderInterface $requestHeaderMessageBinder, MessageBinderInterface $requestMessageBinder, MessageBinderInterface $responseMessageBinder) {
$this->definition = $definition;
$this->requestHeaderMessageBinder = $requestHeaderMessageBinder;
$this->requestMessageBinder = $requestMessageBinder;
$this->responseMessageBinder = $responseMessageBinder;
}
/**
* @param string $method
* @param string $header
*
* @return boolean
*/
public function isServiceHeader($method, $header)
{
return $this->definition->getMethods()->get($method)->getHeaders()->has($header);
}
/**
* @param $string
*
* @return boolean
*/
public function isServiceMethod($method)
{
return $this->definition->getMethods()->has($method);
}
/**
* @param string $method
* @param string $header
* @param mixed $data
*
* @return SoapHeader
*/
public function processServiceHeader($method, $header, $data)
{
$methodDefinition = $this->definition->getMethods()->get($method);
$headerDefinition = $methodDefinition->getHeaders()->get($header);
$this->requestHeaderMessageBinder->setHeader($header);
$data = $this->requestHeaderMessageBinder->processMessage($methodDefinition, $data, $this->definition->getDefinitionComplexTypes());
return new SoapHeader($this->definition->getNamespace(), $headerDefinition->getName(), $data);
}
/**
* @param string $method
* @param array $arguments
*
* @return array
*/
public function processServiceMethodArguments($method, $arguments)
{
$methodDefinition = $this->definition->getMethods()->get($method);
return array_merge(
array('_controller' => $methodDefinition->getController()),
$this->requestMessageBinder->processMessage($methodDefinition, $arguments, $this->definition->getDefinitionComplexTypes())
);
}
/**
* @param string $name
* @param mixed
*
* @return mixed
*/
public function processServiceMethodReturnValue($name, $return)
{
$methodDefinition = $this->definition->getMethods()->get($name);
return $this->responseMessageBinder->processMessage($methodDefinition, $return, $this->definition->getDefinitionComplexTypes());
}
}

View File

@ -0,0 +1,56 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Annotation;
/**
* @Annotation
*/
class ComplexType extends Configuration
{
private $name;
private $value;
private $isNillable = false;
public function getName()
{
return $this->name;
}
public function getValue()
{
return $this->value;
}
public function isNillable()
{
return $this->isNillable;
}
public function setName($name)
{
$this->name = $name;
}
public function setValue($value)
{
$this->value = $value;
}
public function setNillable($isNillable)
{
$this->isNillable = (bool) $isNillable;
}
public function getAliasName()
{
return 'complextype';
}
}

View File

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\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 BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\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

@ -0,0 +1,22 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Annotation;
/**
* @Annotation
*/
class Header extends Param
{
public function getAliasName()
{
return 'header';
}
}

View File

@ -0,0 +1,45 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Annotation;
/**
* @Annotation
*/
class Method extends Configuration
{
private $value;
private $service;
public function getValue()
{
return $this->value;
}
public function getService()
{
return $this->service;
}
public function setValue($value)
{
$this->value = $value;
}
public function setService($service)
{
$this->service = $service;
}
public function getAliasName()
{
return 'method';
}
}

View File

@ -0,0 +1,56 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Annotation;
/**
* @Annotation
*/
class Param extends Configuration implements TypedElementInterface
{
private $value;
private $phpType;
private $xmlType;
public function getValue()
{
return $this->value;
}
public function getPhpType()
{
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

@ -0,0 +1,45 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Annotation;
/**
* @Annotation
*/
class Result extends Configuration implements TypedElementInterface
{
private $phpType;
private $xmlType;
public function getPhpType()
{
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

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Annotation;
interface TypedElementInterface
{
function getPhpType();
function getXmlType();
function setPhpType($phpType);
function setXmlType($xmlType);
}

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition;
class Argument
{
private $name;
private $type;
public function __construct($name = null, Type $type = null)
{
$this->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 $type)
{
$this->type = $type;
}
}

View File

@ -0,0 +1,51 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class ComplexType
{
private $name;
private $value;
private $isNillable = false;
public function getName()
{
return $this->name;
}
public function getValue()
{
return $this->value;
}
public function isNillable()
{
return $this->isNillable;
}
public function setName($name)
{
$this->name = $name;
}
public function setValue($value)
{
$this->value = $value;
}
public function setNillable($isNillable)
{
$this->isNillable = (bool) $isNillable;
}
}

View File

@ -0,0 +1,18 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
interface DumperInterface
{
function dumpServiceDefinition(ServiceDefinition $definition, $endpoint);
}

View File

@ -0,0 +1,78 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\Converter\TypeRepository;
use BeSimple\SoapBundle\ServiceDefinition\Type;
use Zend\Soap\Wsdl as BaseWsdl;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class Wsdl extends BaseWsdl
{
private $typeRepository;
public function __construct(TypeRepository $typeRepository, $name, $uri, $strategy = true)
{
$this->typeRepository = $typeRepository;
parent::__construct($name, $uri, $strategy);
}
public function getType($type)
{
if ($type instanceof Type) {
$xmlType = $type->getXmlType();
} else {
$xmlType = $this->typeRepository->getXmlTypeMapping($type);
}
return $xmlType ?: $this->addComplexType($type);
}
/**
* Translate PHP type into WSDL QName
*
* @param string $type
* @return string QName
*/
public function translateType($type)
{
if (isset($this->classMap[$type])) {
return $this->classMap[$type];
}
if ($type[0] == '\\') {
$type = substr($type, 1);
}
return str_replace('\\', '.', $type);
}
public function addBindingOperationHeader(\DOMElement $bindingOperation, array $headers, array $baseBinding)
{
foreach ($headers as $header) {
$inputNode = $bindingOperation->getElementsByTagName('input')->item(0);
$headerNode = $this->toDomDocument()->createElement('soap:header');
$headerNode->setAttribute('part', $header);
foreach ($baseBinding as $name => $value) {
$headerNode->setAttribute($name, $value);
}
$inputNode->appendChild($headerNode);
}
return $bindingOperation;
}
}

View File

@ -0,0 +1,177 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\Converter\TypeRepository;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Type;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
use BeSimple\SoapBundle\Util\Assert;
use BeSimple\SoapBundle\Util\QName;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class WsdlDumper implements DumperInterface
{
private $loader;
private $typeRepository;
private $options;
private $wsdl;
private $definition;
public function __construct(AnnotationComplexTypeLoader $loader, TypeRepository $typeRepository, array $options)
{
$this->loader = $loader;
$this->typeRepository = $typeRepository;
$this->options = $options;
}
public function dumpServiceDefinition(ServiceDefinition $definition, $endpoint)
{
Assert::thatArgumentNotNull('definition', $definition);
$this->definition = $definition;
$this->wsdl = new Wsdl($this->typeRepository, $definition->getName(), $definition->getNamespace(), new WsdlTypeStrategy($this->loader, $definition));
$port = $this->wsdl->addPortType($this->getPortTypeName());
$binding = $this->wsdl->addBinding($this->getBindingName(), $this->qualify($this->getPortTypeName()));
$this->wsdl->addSoapBinding($binding, 'rpc');
$this->wsdl->addService($this->getServiceName(), $this->getPortName(), $this->qualify($this->getBindingName()), $endpoint);
foreach ($definition->getMethods() as $method) {
$requestHeaderParts =
$requestParts =
$responseParts = array();
foreach ($method->getHeaders() as $header) {
$requestHeaderParts[$header->getName()] = $this->wsdl->getType($header->getType()->getPhpType());
}
foreach ($method->getArguments() as $argument) {
$requestParts[$argument->getName()] = $this->wsdl->getType($argument->getType()->getPhpType());
}
if ($method->getReturn() !== null) {
$responseParts['return'] = $this->wsdl->getType($method->getReturn()->getPhpType());
}
if (!empty($requestHeaderParts)) {
$this->wsdl->addMessage($this->getRequestHeaderMessageName($method), $requestHeaderParts);
}
$this->wsdl->addMessage($this->getRequestMessageName($method), $requestParts);
$this->wsdl->addMessage($this->getResponseMessageName($method), $responseParts);
$portOperation = $this->wsdl->addPortOperation(
$port,
$method->getName(),
$this->qualify($this->getRequestMessageName($method)),
$this->qualify($this->getResponseMessageName($method))
);
$baseBinding =
$inputBinding =
$outputBinding = array(
'use' => 'literal',
'namespace' => $definition->getNamespace(),
'encodingStyle' => 'http://schemas.xmlsoap.org/soap/encoding/',
);
if (!empty($requestParts)) {
$portOperation->setAttribute('parameterOrder', implode(' ', array_keys($requestParts)));
$inputBinding['parts'] = implode(' ', array_keys($requestParts));
}
if (!empty($responseParts)) {
$outputBinding['parts'] = implode(' ', array_keys($responseParts));
}
$bindingOperation = $this->wsdl->addBindingOperation(
$binding,
$method->getName(),
$inputBinding,
$outputBinding
);
$bindingOperation = $this->wsdl->addBindingOperationHeader(
$bindingOperation,
array_keys($requestHeaderParts),
array_merge(array('message' => $this->qualify($this->getRequestHeaderMessageName($method))), $baseBinding)
);
$this->wsdl->addSoapOperation($bindingOperation, $this->getSoapOperationName($method));
}
$this->definition = null;
$dom = $this->wsdl->toDomDocument();
$dom->formatOutput = true;
if ($this->options['stylesheet']) {
$stylesheet = $dom->createProcessingInstruction('xml-stylesheet', sprintf('type="text/xsl" href="%s"', $this->options['stylesheet']));
$dom->insertBefore($stylesheet, $dom->documentElement);
}
return $this->wsdl->toXml();
}
protected function qualify($name, $namespace = null)
{
if($namespace === null) {
$namespace = $this->definition->getNamespace();
}
return $this->wsdl->toDomDocument()->lookupPrefix($namespace).':'.$name;
}
protected function getPortName()
{
return $this->definition->getName().'Port';
}
protected function getPortTypeName()
{
return $this->definition->getName().'PortType';
}
protected function getBindingName()
{
return $this->definition->getName().'Binding';
}
protected function getServiceName()
{
return $this->definition->getName().'Service';
}
protected function getRequestHeaderMessageName(Method $method)
{
return $method->getName().'Header';
}
protected function getRequestMessageName(Method $method)
{
return $method->getName().'Request';
}
protected function getResponseMessageName(Method $method)
{
return $method->getName().'Response';
}
protected function getSoapOperationName(Method $method)
{
return $this->definition->getNamespace().$method->getName();
}
}

View File

@ -0,0 +1,95 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\ComplexType;
use BeSimple\SoapBundle\Util\String;
use Zend\Soap\Exception;
use Zend\Soap\Wsdl as BaseWsdl;
use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface;
use Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence;
class WsdlTypeStrategy implements ComplexTypeStrategyInterface
{
/**
* Context WSDL file
*
* @var \Zend\Soap\Wsdl|null
*/
private $context;
private $loader;
private $definition;
private $typeStrategy;
private $arrayStrategy;
public function __construct(AnnotationComplexTypeLoader $loader, ServiceDefinition $definition)
{
$this->loader = $loader;
$this->definition = $definition;
}
/**
* Method accepts the current WSDL context file.
*
* @param \Zend\Soap\Wsdl $context
*/
public function setContext(BaseWsdl $context)
{
$this->context = $context;
return $this;
}
/**
* Create a complex type based on a strategy
*
* @param string $type
*
* @return string XSD type
*
* @throws \Zend\Soap\WsdlException
*/
public function addComplexType($type)
{
if (!$this->context) {
throw new \LogicException(sprintf('Cannot add complex type "%s", no context is set for this composite strategy.', $type));
}
$strategy = String::endsWith($type, '[]') ? $this->getArrayStrategy() : $this->getTypeStrategy();
return $strategy->addComplexType($type);
}
private function getArrayStrategy()
{
if (!$this->arrayStrategy) {
$this->arrayStrategy = new ArrayOfTypeSequence();
$this->arrayStrategy->setContext($this->context);
}
return $this->arrayStrategy;
}
private function getTypeStrategy()
{
if (!$this->typeStrategy) {
$this->typeStrategy = new ComplexType($this->loader, $this->definition);
$this->typeStrategy->setContext($this->context);
}
return $this->typeStrategy;
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition;
class Header
{
private $name;
private $type;
public function __construct($name = null, Type $type = null)
{
$this->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;
}
}

View File

@ -0,0 +1,200 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Loader;
use BeSimple\SoapBundle\ServiceDefinition as Definition;
use BeSimple\SoapBundle\ServiceDefinition\Annotation;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
/**
* AnnotationClassLoader loads ServiceDefinition from a PHP class and its methods.
*
* Based on \Symfony\Component\Routing\Loader\AnnotationClassLoader
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class AnnotationClassLoader implements LoaderInterface
{
protected $reader;
/**
* Constructor.
*
* @param \Doctrine\Common\Annotations\Reader $reader
*/
public function __construct(Reader $reader)
{
$this->reader = $reader;
}
/**
* Loads a ServiceDefinition from annotations from a class.
*
* @param string $class A class name
* @param string $type The resource type
*
* @return \BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition A ServiceDefinition instance
*
* @throws \InvalidArgumentException When route can't be parsed
*/
public function load($class, $type = null)
{
if (!class_exists($class)) {
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
}
$class = new \ReflectionClass($class);
$definition = new Definition\ServiceDefinition();
$serviceMethodHeaders = array();
foreach ($this->reader->getClassAnnotations($class) as $annotation) {
if ($annotation instanceof Annotation\Header) {
$serviceMethodHeaders[$annotation->getValue()] = $annotation;
}
}
foreach ($class->getMethods() as $method) {
$serviceArguments =
$serviceHeaders = array();
$serviceMethod =
$serviceReturn = null;
foreach ($serviceMethodHeaders as $annotation) {
$serviceHeaders[$annotation->getValue()] = new Definition\Header(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
}
foreach ($this->reader->getMethodAnnotations($method) as $annotation) {
if ($annotation instanceof Annotation\Header) {
$serviceHeaders[$annotation->getValue()] = new Definition\Header(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
} elseif ($annotation instanceof Annotation\Param) {
$serviceArguments[] = new Definition\Argument(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
} elseif ($annotation instanceof Annotation\Method) {
if ($serviceMethod) {
throw new \LogicException(sprintf('@Soap\Method defined twice for "%s".', $method->getName()));
}
$serviceMethod = new Definition\Method(
$annotation->getValue(),
$this->getController($class, $method, $annotation)
);
} elseif ($annotation instanceof Annotation\Result) {
if ($serviceReturn) {
throw new \LogicException(sprintf('@Soap\Result defined twice for "%s".', $method->getName()));
}
$serviceReturn = new Definition\Type($annotation->getPhpType(), $annotation->getXmlType());
}
}
if (!$serviceMethod && (!empty($serviceArguments) || $serviceReturn)) {
throw new \LogicException(sprintf('@Soap\Method non-existent for "%s".', $method->getName()));
}
if ($serviceMethod) {
$serviceMethod->setArguments($serviceArguments);
$serviceMethod->setHeaders($serviceHeaders);
if (!$serviceReturn) {
throw new \LogicException(sprintf('@Soap\Result non-existent for "%s".', $method->getName()));
}
$serviceMethod->setReturn($serviceReturn);
$definition->getMethods()->add($serviceMethod);
}
}
return $definition;
}
/**
* @param \ReflectionMethod $method
* @param \BeSimple\SoapBundle\ServiceDefinition\Annotation\Method $annotation
*
* @return string
*/
private function getController(\ReflectionClass $class, \ReflectionMethod $method, Annotation\Method $annotation)
{
if(null !== $annotation->getService()) {
return $annotation->getService() . ':' . $method->name;
} else {
return $class->name . '::' . $method->name;
}
}
/**
* @param \ReflectionMethod $method
* @param \BeSimple\SoapBundle\ServiceDefinition\Annotation\Param $annotation
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Type
*/
private function getArgumentType(\ReflectionMethod $method, Annotation\Param $annotation)
{
$phpType = $annotation->getPhpType();
$xmlType = $annotation->getXmlType();
if (null === $phpType) {
foreach ($method->getParameters() as $param) {
if ($param->name === $annotation->getName()) {
$phpType = $param->getClass()->name;
break;
}
}
}
return new Definition\Type($phpType, $xmlType);
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return Boolean True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null)
{
return is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type);
}
/**
* Sets the loader resolver.
*
* @param LoaderResolverInterface $resolver A LoaderResolverInterface instance
*/
public function setResolver(LoaderResolverInterface $resolver)
{
}
/**
* Gets the loader resolver.
*
* @return LoaderResolverInterface A LoaderResolverInterface instance
*/
public function getResolver()
{
}
}

View File

@ -0,0 +1,60 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Loader;
use BeSimple\SoapBundle\ServiceDefinition\ComplexType;
use BeSimple\SoapBundle\Util\Collection;
/**
* AnnotationComplexTypeLoader loads ServiceDefinition from a PHP class and its methods.
*
* Based on \Symfony\Component\Routing\Loader\AnnotationClassLoader
*
* @author Francis Besset <francis.besset@gmail.com>
*/
class AnnotationComplexTypeLoader extends AnnotationClassLoader
{
private $complexTypeClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\ComplexType';
/**
* Loads a ServiceDefinition from annotations from a class.
*
* @param string $class A class name
* @param string $type The resource type
*
* @return ServiceDefinition A ServiceDefinition instance
*
* @throws \InvalidArgumentException When route can't be parsed
*/
public function load($class, $type = null)
{
if (!class_exists($class)) {
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
}
$class = new \ReflectionClass($class);
$collection = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\ComplexType');
foreach ($class->getProperties() as $property) {
$complexType = $this->reader->getPropertyAnnotation($property, $this->complexTypeClass);
if ($complexType) {
$propertyComplexType = new ComplexType();
$propertyComplexType->setValue($complexType->getValue());
$propertyComplexType->setNillable($complexType->isNillable());
$propertyComplexType->setName($property->getName());
$collection->add($propertyComplexType);
}
}
return $collection;
}
}

View File

@ -0,0 +1,122 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Loader;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Config\Resource\FileResource;
/**
* AnnotationFileLoader loads ServiceDefinition from annotations set
* on a PHP class and its methods.
*
* Based on \Symfony\Component\Routing\Loader\AnnotationFileLoader
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class AnnotationFileLoader extends FileLoader
{
protected $loader;
/**
* Constructor.
*
* @param AnnotationClassLoader $loader An AnnotationClassLoader instance
* @param string|array $paths A path or an array of paths where to look for resources
*/
public function __construct(FileLocator $locator, AnnotationClassLoader $loader, $paths = array())
{
if (!function_exists('token_get_all')) {
throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.');
}
parent::__construct($locator, $paths);
$this->loader = $loader;
}
/**
* Loads from annotations from a file.
*
* @param string $file A PHP file path
* @param string $type The resource type
*
* @return ServiceDefinition A ServiceDefinition instance
*
* @throws \InvalidArgumentException When the file does not exist
*/
public function load($file, $type = null)
{
$path = $this->locator->locate($file);
if ($class = $this->findClass($path)) {
return $definition = $this->loader->load($class, $type);
}
return null;
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return Boolean True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null)
{
return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type);
}
/**
* Returns the full class name for the first class in the file.
*
* @param string $file A PHP file path
*
* @return string|false Full class name if found, false otherwise
*/
protected function findClass($file)
{
$class = false;
$namespace = false;
$tokens = token_get_all(file_get_contents($file));
while ($token = array_shift($tokens)) {
if (!is_array($token)) {
continue;
}
if (true === $class && T_STRING === $token[0]) {
return $namespace.'\\'.$token[1];
}
if (true === $namespace && T_STRING === $token[0]) {
$namespace = '';
do {
$namespace .= $token[1];
$token = array_shift($tokens);
} while ($tokens && is_array($token) && in_array($token[0], array(T_NS_SEPARATOR, T_STRING)));
}
if (T_CLASS === $token[0]) {
$class = true;
}
if (T_NAMESPACE === $token[0]) {
$namespace = true;
}
}
return false;
}
}

View File

@ -0,0 +1,143 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition\Loader;
use BeSimple\SoapBundle\ServiceDefinition\Argument;
use BeSimple\SoapBundle\ServiceDefinition\Header;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Type;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use Symfony\Component\Config\Loader\FileLoader;
class XmlFileLoader extends FileLoader
{
public function supports($resource, $type = null)
{
return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
}
public function load($file, $type = null)
{
$path = $this->locator->locate($file);
$xml = $this->parseFile($path);
$definition = new ServiceDefinition();
$definition->setName((string) $xml['name']);
$definition->setNamespace((string) $xml['namespace']);
foreach($xml->header as $header) {
$definition->getHeaders()->add($this->parseHeader($header));
}
foreach($xml->method as $method) {
$definition->getMethods()->add($this->parseMethod($method));
}
return $definition;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Header
*/
protected function parseHeader(\SimpleXMLElement $node)
{
return new Header((string)$node['name'], $this->parseType($node->type));
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Method
*/
protected function parseMethod(\SimpleXMLElement $node)
{
$method = new Method((string)$node['name'], (string)$node['controller']);
foreach($node->argument as $argument) {
$method->getArguments()->add($this->parseArgument($argument));
}
$method->setReturn($this->parseType($node->return->type));
return $method;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Argument
*/
protected function parseArgument(\SimpleXMLElement $node)
{
$argument = new Argument((string)$node['name'], $this->parseType($node->type));
return $argument;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Type
*/
protected function parseType(\SimpleXMLElement $node)
{
$namespaces = $node->getDocNamespaces(true);
$qname = explode(':', $node['xml-type'], 2);
$xmlType = sprintf('{%s}%s', $namespaces[$qname[0]], $qname[1]);
return new Type((string)$node['php-type'], $xmlType, (string)$node['converter']);
}
/**
* @param string $file
*
* @return \SimpleXMLElement
*/
protected function parseFile($file)
{
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
if (!$dom->load($file, LIBXML_COMPACT)) {
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors()));
}
if (!$dom->schemaValidate(__DIR__.'/schema/servicedefinition-1.0.xsd')) {
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors()));
}
$dom->validateOnParse = true;
$dom->normalizeDocument();
libxml_use_internal_errors(false);
return simplexml_import_dom($dom);
}
protected function getXmlErrors()
{
$errors = array();
foreach (libxml_get_errors() as $error) {
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
$error->code,
trim($error->message),
$error->file ? $error->file : 'n/a',
$error->line,
$error->column
);
}
libxml_clear_errors();
libxml_use_internal_errors(false);
return $errors;
}
}

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://github.com/BeSimple/BeSimpleSoapBundle/servicedefinition/1.0/" xmlns:tns="http://github.com/BeSimple/BeSimpleSoapBundle/servicedefinition/1.0/" elementFormDefault="qualified">
<element name="webservice" type="tns:WebserviceType" />
<complexType name="WebserviceType">
<sequence>
<element name="header" type="tns:HeaderType" minOccurs="0" maxOccurs="unbounded" />
<element name="method" type="tns:MethodType" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="string" use="required" />
</complexType>
<complexType name="HeaderType">
<complexContent>
<extension base="tns:TransferObjectType">
<sequence>
<element name="exception" type="tns:ExceptionType" minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="ID" use="required" />
</extension>
</complexContent>
</complexType>
<complexType name="HeaderRefType">
<attribute name="name" type="IDREF" />
<attribute name="direction">
<simpleType>
<restriction base="string">
<enumeration value="in" />
<enumeration value="out" />
<enumeration value="inout" />
</restriction>
</simpleType>
</attribute>
</complexType>
<complexType name="MethodType">
<sequence>
<element name="exception" type="tns:ExceptionType" minOccurs="0" maxOccurs="unbounded" />
<element name="header" type="tns:HeaderRefType" minOccurs="0" maxOccurs="unbounded" />
<element name="argument" type="tns:ArgumentType" minOccurs="0" maxOccurs="unbounded" />
<element name="return" type="tns:ReturnType" minOccurs="0" maxOccurs="1" />
</sequence>
<attribute name="name" type="string" use="required" />
<attribute name="controller" type="string" use="required" />
</complexType>
<complexType name="TransferObjectType" abstract="true">
<sequence>
<element name="type" type="tns:TypeConversionType" />
</sequence>
</complexType>
<complexType name="ExceptionType">
<complexContent>
<extension base="tns:TransferObjectType">
<attribute name="name" type="string" use="required" />
</extension>
</complexContent>
</complexType>
<complexType name="ArgumentType">
<complexContent>
<extension base="tns:TransferObjectType">
<attribute name="name" type="string" use="required" />
</extension>
</complexContent>
</complexType>
<complexType name="ReturnType">
<complexContent>
<extension base="tns:TransferObjectType" />
</complexContent>
</complexType>
<complexType name="TypeConversionType">
<attribute name="php-type" type="string" use="required" />
<attribute name="xml-type" type="QName" use="required" />
<attribute name="converter" type="string" use="optional" />
</complexType>
</schema>

View File

@ -0,0 +1,86 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition;
use BeSimple\SoapBundle\Util\Collection;
class Method
{
private $name;
private $controller;
private $arguments;
private $headers;
private $return;
public function __construct($name = null, $controller = null, array $headers = array(), array $arguments = array(), Type $return = null)
{
$this->setName($name);
$this->setController($controller);
$this->setHeaders($headers);
$this->setArguments($arguments);
if ($return) {
$this->setReturn($return);
}
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getController()
{
return $this->controller;
}
public function setController($controller)
{
$this->controller = $controller;
}
public function getHeaders()
{
return $this->headers;
}
public function setHeaders(array $headers)
{
$this->headers = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\Header');
$this->headers->addAll($headers);
}
public function getArguments()
{
return $this->arguments;
}
public function setArguments(array $arguments)
{
$this->arguments = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\Argument');
$this->arguments->addAll($arguments);
}
public function getReturn()
{
return $this->return;
}
public function setReturn(Type $return)
{
$this->return = $return;
}
}

View File

@ -0,0 +1,140 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition;
use BeSimple\SoapBundle\Util\Collection;
use BeSimple\SoapCommon\Classmap;
class ServiceDefinition
{
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $namespace;
/**
* @var \BeSimple\SoapBundle\Util\Collection
*/
private $methods;
/**
* @var \BeSimple\SoapCommon\Classmap
*/
private $classmap;
private $complexTypes = array();
public function __construct($name = null, $namespace = null, array $methods = array(), Classmap $classmap = null)
{
$this->setName($name);
$this->setNamespace($namespace);
$this->methods = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\Method');
$this->setMethods($methods);
$this->classmap = $classmap;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* @param string $namespace
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
}
/**
* @return \BeSimple\SoapBundle\Util\Collection
*/
public function getMethods()
{
return $this->methods;
}
/**
* @param array $methods
*/
public function setMethods(array $methods)
{
$this->methods->addAll($methods);
}
/**
* @return array
*/
public function getAllTypes()
{
$types = array();
foreach ($this->methods as $method) {
foreach ($method->getArguments() as $argument) {
$types[] = $argument->getType();
}
foreach ($method->getHeaders() as $header) {
$types[] = $header->getType();
}
$types[] = $method->getReturn();
}
return $types;
}
public function getClassmap()
{
return $this->classmap ?: array();
}
public function setClassmap(Classmap $classmap)
{
$this->classmap = $classmap;
}
public function addDefinitionComplexType($type, Collection $complexType)
{
$this->complexTypes[$type] = $complexType;
}
public function getDefinitionComplexTypes()
{
return $this->complexTypes;
}
}

View File

@ -0,0 +1,88 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\ServiceDefinition\Strategy;
use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
use Zend\Soap\Wsdl;
use Zend\Soap\Wsdl\ComplexTypeStrategy\AbstractComplexTypeStrategy;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class ComplexType extends AbstractComplexTypeStrategy
{
private $loader;
private $definition;
public function __construct(AnnotationComplexTypeLoader $loader, $definition)
{
$this->loader = $loader;
$this->definition = $definition;
}
/**
* Add a complex type by recursivly using all the class properties fetched via Reflection.
*
* @param string $type Name of the class to be specified
* @return string XSD Type for the given PHP type
*/
public function addComplexType($type)
{
$classmap = $this->definition->getClassmap();
if (null !== $soapType = $this->scanRegisteredTypes($type)) {
return $soapType;
}
if (!$this->loader->supports($type)) {
throw new \InvalidArgumentException(sprintf('Cannot add a complex type "%s" that is not an object or where class could not be found in "ComplexType" strategy.', $type));
}
$dom = $this->getContext()->toDomDocument();
$soapTypeName = $this->getContext()->translateType($type);
$soapType = 'tns:'.$soapTypeName;
if (!$classmap->has($soapTypeName)) {
$classmap->add($soapTypeName, $type);
}
// Register type here to avoid recursion
$this->getContext()->addType($type, $soapType);
$complexType = $dom->createElement('xsd:complexType');
$complexType->setAttribute('name', $soapTypeName);
$all = $dom->createElement('xsd:all');
$definitionComplexType = $this->loader->load($type);
$this->definition->addDefinitionComplexType($type, $definitionComplexType);
foreach ($definitionComplexType as $annotationComplexType) {
$element = $dom->createElement('xsd:element');
$element->setAttribute('name', $propertyName = $annotationComplexType->getName());
$element->setAttribute('type', $this->getContext()->getType(trim($annotationComplexType->getValue())));
if ($annotationComplexType->isNillable()) {
$element->setAttribute('nillable', 'true');
}
$all->appendChild($element);
}
$complexType->appendChild($all);
$this->getContext()->getSchema()->appendChild($complexType);
return $soapType;
}
}

View File

@ -0,0 +1,60 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\ServiceDefinition;
class Type
{
private $phpType;
private $xmlType;
private $converter;
public function __construct($phpType = null, $xmlType = null, $converter = null)
{
$this->setPhpType($phpType);
$this->setXmlType($xmlType);
$this->setConverter($converter);
}
public function getPhpType()
{
return $this->phpType;
}
public function setPhpType($phpType)
{
$phpType = $phpType;
if ($phpType[0] == '\\') {
$phpType = substr($phpType, 1);
}
$this->phpType = $phpType;
}
public function getXmlType()
{
return $this->xmlType;
}
public function setXmlType($xmlType)
{
$this->xmlType = $xmlType;
}
public function getConverter()
{
return $this->converter;
}
public function setConverter($converter)
{
$this->converter = $converter;
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Soap;
class SoapAttachment
{
private $id;
private $type;
private $content;
public function __construct($id, $type, $content)
{
$this->id = $id;
$this->type = $type;
$this->content = $content;
}
public function getId()
{
return $this->id;
}
public function getType()
{
return $this->type;
}
public function getContent()
{
return $this->content;
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace BeSimple\SoapBundle\Soap;
use BeSimple\SoapCommon\Classmap;
use BeSimple\SoapCommon\Converter\TypeConverterCollection;
use BeSimple\SoapClient\SoapClientBuilder as BaseSoapClientBuilder;
class SoapClientBuilder extends BaseSoapClientBuilder
{
protected $soapClient;
public function __construct($wsdl, array $options, Classmap $classmap = null, TypeConverterCollection $converters = null)
{
parent::__construct();
$this->checkOptions($options);
$this
->withWsdl($wsdl)
->withTrace($options['debug'])
;
if (isset($options['user_agent'])) {
$this->withUserAgent($options['user_agent']);
}
if (isset($options['cache_type'])) {
$this->withWsdlCache($options['cache_type']);
}
if ($classmap) {
$this->withClassmap($classmap);
}
if ($converters) {
$this->withTypeConverters($converters);
}
}
public function build()
{
if (!$this->soapClient) {
$this->soapClient = parent::build();
}
return $this->soapClient;
}
protected function checkOptions(array $options)
{
$checkOptions = array(
'debug' => false,
'cache_type' => null,
'exceptions' => true,
'user_agent' => 'BeSimpleSoap',
);
// check option names and live merge, if errors are encountered Exception will be thrown
$invalid = array();
$isInvalid = false;
foreach ($options as $key => $value) {
if (!array_key_exists($key, $checkOptions)) {
$isInvalid = true;
$invalid[] = $key;
}
}
if ($isInvalid) {
throw new \InvalidArgumentException(sprintf(
'The "%s" class does not support the following options: "%s".',
get_class($this),
implode('\', \'', $invalid)
));
}
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Soap;
class SoapHeader
{
private $namespace;
private $name;
private $data;
public function __construct($namespace, $name, $data)
{
$this->namespace = $namespace;
$this->name = $name;
$this->data = $data;
}
public function __toString()
{
return $this->data;
}
public function getNamespace()
{
return $this->namespace;
}
public function getName()
{
return $this->name;
}
public function getData()
{
return $this->data;
}
public function toNativeSoapHeader()
{
return new \SoapHeader($this->namespace, $this->name, $this->data);
}
}

View File

@ -0,0 +1,167 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Soap;
use BeSimple\SoapBundle\Util\Collection;
use Symfony\Component\HttpFoundation\Request;
use Zend\Mime\Mime;
use Zend\Mime\Message;
/**
* SoapRequest.
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class SoapRequest extends Request
{
/**
* @var string
*/
protected $soapMessage;
/**
* @var string
*/
protected $soapAction;
/**
* @var \BeSimple\SoapBundle\Util\Collection
*/
protected $soapHeaders;
/**
* @var \BeSimple\SoapBundle\Util\Collection
*/
protected $soapAttachments;
/**
* @param \Symfony\Component\HttpFoundation\Request $request
*
* @return SoapRequest
*/
public static function createFromHttpRequest(Request $request)
{
return new static($request->query->all(), $request->request->all(), $request->attributes->all(), $request->cookies->all(), $request->files->all(), $request->server->all(), $request->content);
}
public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
{
parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content);
$this->soapMessage = null;
$this->soapHeaders = new Collection('getName', 'BeSimple\SoapBundle\Soap\SoapHeader');
$this->soapAttachments = new Collection('getId', 'BeSimple\SoapBundle\Soap\SoapAttachment');
$this->setRequestFormat('soap');
}
/**
* Gets the XML string of the SOAP message.
*
* @return string
*/
public function getSoapMessage()
{
if(null === $this->soapMessage) {
$this->soapMessage = $this->initializeSoapMessage();
}
return $this->soapMessage;
}
public function getSoapHeaders()
{
return $this->soapHeaders;
}
public function getSoapAttachments()
{
return $this->soapAttachments;
}
protected function initializeSoapMessage()
{
if($this->server->has('CONTENT_TYPE')) {
$type = $this->splitContentTypeHeader($this->server->get('CONTENT_TYPE'));
switch($type['_type']) {
case 'multipart/related':
if($type['type'] == 'application/xop+xml') {
return $this->initializeMtomSoapMessage($type, $this->getContent());
} else {
//log error
}
break;
case 'application/soap+xml':
// goto fallback
break;
default:
// log error
break;
}
}
// fallback
return $this->getContent();
}
protected function initializeMtomSoapMessage(array $contentTypeHeader, $content)
{
if(!isset($contentTypeHeader['start']) || !isset($contentTypeHeader['start-info']) || !isset($contentTypeHeader['boundary'])) {
throw new \InvalidArgumentException();
}
$mimeMessage = Message::createFromMessage($content, $contentTypeHeader['boundary']);
$mimeParts = $mimeMessage->getParts();
$soapMimePartId = trim($contentTypeHeader['start'], '<>');
$soapMimePartType = $contentTypeHeader['start-info'];
$rootPart = array_shift($mimeParts);
$rootPartType = $this->splitContentTypeHeader($rootPart->type);
// TODO: add more checks to achieve full compatibility to MTOM spec
// http://www.w3.org/TR/soap12-mtom/
if($rootPart->id != $soapMimePartId || $rootPartType['_type'] != 'application/xop+xml' || $rootPartType['type'] != $soapMimePartType) {
throw new \InvalidArgumentException();
}
foreach($mimeParts as $mimePart) {
$this->soapAttachments->add(new SoapAttachment(
$mimePart->id,
$mimePart->type,
// handle content decoding / prevent encoding
$mimePart->getContent()
));
}
// handle content decoding / prevent encoding
return $rootPart->getContent();
}
protected function splitContentTypeHeader($header)
{
$result = array();
$parts = explode(';', strtolower($header));
$result['_type'] = array_shift($parts);
foreach($parts as $part) {
list($key, $value) = explode('=', trim($part), 2);
$result[$key] = trim($value, '"');
}
return $result;
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Soap;
use BeSimple\SoapBundle\Util\Collection;
use Symfony\Component\HttpFoundation\Response;
/**
* SoapResponse.
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class SoapResponse extends Response
{
/**
* @var \BeSimple\SoapBundle\Util\Collection
*/
protected $soapHeaders;
/**
* @var mixed
*/
protected $soapReturnValue;
public function __construct($returnValue = null)
{
parent::__construct();
$this->soapHeaders = new Collection('getName', 'BeSimple\SoapBundle\Soap\SoapHeader');
$this->setReturnValue($returnValue);
}
/**
* @param SoapHeader $soapHeader
*/
public function addSoapHeader(SoapHeader $soapHeader)
{
$this->soapHeaders->add($soapHeader);
}
/**
* @return \BeSimple\SoapBundle\Util\Collection
*/
public function getSoapHeaders()
{
return $this->soapHeaders;
}
public function setReturnValue($value)
{
$this->soapReturnValue = $value;
return $this;
}
public function getReturnValue()
{
return $this->soapReturnValue;
}
}

View File

@ -0,0 +1,6 @@
TODO
====
* Move RequestBinding in a future ParamConverterListener
* See SensioFrameworkExtraBundle
* Add a security layer to log an user with Symfony2 Security Component

View File

@ -0,0 +1,280 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\Tests\ServiceBinding;
use BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder;
use BeSimple\SoapBundle\ServiceDefinition as Definition;
use BeSimple\SoapBundle\Tests\fixtures\ServiceBinding as Fixtures;
use BeSimple\SoapBundle\Util\Collection;
class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider messageProvider
*/
public function testProcessMessage(Definition\Method $method, $message, $assert)
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$result = $messageBinder->processMessage($method, $message);
$this->assertSame($assert, $result);
}
public function testProcessMessageWithComplexType()
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$foo = new Fixtures\Foo('foobar', 19395);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('foo', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo')),
)),
array($foo),
$this->getDefinitionComplexTypes()
);
$this->assertEquals(array('foo' => $foo), $result);
$foo1 = new Fixtures\Foo('foobar', 29291);
$foo2 = new Fixtures\Foo('barfoo', 39392);
$foos = new \stdClass();
$foos->item = array($foo1, $foo2);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('foos', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]')),
)),
array($foos),
$this->getDefinitionComplexTypes()
);
$this->assertEquals(array('foos' => array($foo1, $foo2)), $result);
}
/**
* @expectedException SoapFault
*/
public function testProcessMessageSoapFault()
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$foo = new Fixtures\Foo('foo', null);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('foo', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo')),
)),
array($foo),
$this->getDefinitionComplexTypes()
);
}
public function testProcessMessageWithComplexTypeReference()
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$foo = new Fixtures\Foo('foo', 2499104);
$foos = new \stdClass();
$foos->item = array($foo, $foo);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('foos', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]')),
)),
array($foos),
$this->getDefinitionComplexTypes()
);
$this->assertEquals(array('foos' => array($foo, $foo)), $result);
}
public function testProcessMessageWithComplexTypeIntoComplexType()
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$foo = new Fixtures\Foo('foo', 38845);
$bar = new Fixtures\Bar('bar', null);
$fooBar = new Fixtures\FooBar($foo, $bar);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('fooBar', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooBar')),
)),
array($fooBar),
$this->getDefinitionComplexTypes()
);
$this->assertEquals(array('fooBar' => $fooBar), $result);
}
public function testProcessMessageComplexTypeWithArrays()
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$array = array(1, 2, 3, 4);
$stdClass = new \stdClass();
$stdClass->item = $array;
$simpleArrays = new Fixtures\SimpleArrays(null, new \stdClass(), $stdClass);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_with_array', null, array(), array(
new Definition\Argument('simple_arrays', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\SimpleArrays')),
)),
array($simpleArrays),
$this->getDefinitionComplexTypes()
);
$result = $result['simple_arrays'];
$this->assertEquals(null, $result->array1);
$this->assertEquals(array(), $result->getArray2());
$this->assertEquals($array, $result->getArray3());
}
public function testProcessMessageWithEmptyArrayComplexType()
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$result = $messageBinder->processMessage(
new Definition\Method('empty_array_complex_type', null, array(), array(
new Definition\Argument('foo', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]')),
)),
array(new \stdClass()),
$this->getDefinitionComplexTypes()
);
$this->assertEquals(array('foo' => array()), $result);
}
public function testProccessMessagePreventInfiniteRecursion()
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$foo = new Fixtures\FooRecursive('foo', '');
$bar = new Fixtures\BarRecursive($foo, 10394);
$foo->bar = $bar;
$result = $messageBinder->processMessage(
new Definition\Method('prevent_infinite_recursion', null, array(), array(
new Definition\Argument('foo_recursive', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive')),
)),
array($foo),
$this->getDefinitionComplexTypes()
);
$this->assertEquals(array('foo_recursive' => $foo), $result);
}
public function messageProvider()
{
$messages = array();
$messages[] = array(
new Definition\Method('no_argument'),
array(),
array(),
);
$messages[] = array(
new Definition\Method('string_argument', null, array(), array(
new Definition\Argument('foo', new Definition\Type('string')),
)),
array('bar'),
array('foo' => 'bar'),
);
$messages[] = array(
new Definition\Method('string_int_arguments', null, array(), array(
new Definition\Argument('foo', new Definition\Type('string')),
new Definition\Argument('bar', new Definition\Type('int')),
)),
array('test', 20),
array('foo' => 'test', 'bar' => 20),
);
$strings = new \stdClass();
$strings->item = array('foo', 'bar', 'barfoo');
$messages[] = array(
new Definition\Method('array_string_arguments', null, array(), array(
new Definition\Argument('foo', new Definition\Type('string[]')),
new Definition\Argument('bar', new Definition\Type('int')),
)),
array($strings, 4),
array('foo' => array('foo', 'bar', 'barfoo'), 'bar' => 4),
);
$messages[] = array(
new Definition\Method('empty_array', null, array(), array(
new Definition\Argument('foo', new Definition\Type('string[]')),
)),
array(new \stdClass()),
array('foo' => array()),
);
return $messages;
}
private function getDefinitionComplexTypes()
{
$definitionComplexTypes = array();
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo'] = $this->createComplexTypeCollection(array(
array('foo', 'string'),
array('bar', 'int'),
));
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Bar'] = $this->createComplexTypeCollection(array(
array('foo', 'string'),
array('bar', 'int', true),
));
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooBar'] = $this->createComplexTypeCollection(array(
array('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo'),
array('bar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Bar'),
));
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\SimpleArrays'] = $this->createComplexTypeCollection(array(
array('array1', 'string[]', true),
array('array2', 'string[]'),
array('array3', 'string[]'),
));
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive'] = $this->createComplexTypeCollection(array(
array('bar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\BarRecursive'),
));
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\BarRecursive'] = $this->createComplexTypeCollection(array(
array('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive'),
));
return $definitionComplexTypes;
}
private function createComplexTypeCollection(array $properties)
{
$collection = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\ComplexType');
foreach ($properties as $property) {
$complexType = new Definition\ComplexType();
$complexType->setName($property[0]);
$complexType->setValue($property[1]);
if (isset($property[2])) {
$complexType->setNillable($property[2]);
}
$collection->add($complexType);
}
return $collection;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace BeSimple\SoapBundle\Tests\ServiceBinding\fixtures;
class Attributes
{
public $foo;
public $bar;
}

View File

@ -0,0 +1,20 @@
<?php
namespace BeSimple\SoapBundle\Tests\ServiceBinding\fixtures;
class ComplexType
{
public $bar;
private $foo;
public function getFoo()
{
return $this->foo;
}
public function setFoo($foo)
{
$this->foo = $foo;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace BeSimple\SoapBundle\Tests\ServiceBinding\fixtures;
class Setters
{
private $foo;
private $bar;
public function getFoo()
{
return $this->foo;
}
public function setFoo($foo)
{
$this->foo = $foo;
}
public function getBar()
{
return $this->bar;
}
public function setBar($bar)
{
$this->bar = $bar;
}
}

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\Tests\Soap;
use BeSimple\SoapBundle\Soap\SoapRequest;
/**
* UnitTest for \BeSimple\SoapBundle\Soap\SoapRequest.
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class SoapRequestTest extends \PHPUnit_Framework_TestCase
{
public function testMtomMessage()
{
$content = $this->loadRequestContentFixture('mtom/simple.txt');
$request = new SoapRequest(array(), array(), array(), array(), array(), array(), $content);
$request->server->set('CONTENT_TYPE', 'multipart/related; type="application/xop+xml";start="<http://tempuri.org/0>";boundary="uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7";start-info="application/soap+xml"');
$message = $request->getSoapMessage();
$this->assertEquals(735, strlen(trim($message)));
$this->assertEquals(1, count($request->getSoapAttachments()));
$attachment = $request->getSoapAttachments()->get('http://tempuri.org/1/632618206527087310');
$this->assertNotNull($attachment);
$this->assertEquals('application/octet-stream', $attachment->getType());
$this->assertEquals(767, strlen(trim($attachment->getContent())));
}
private function loadRequestContentFixture($name)
{
return file_get_contents(__DIR__.'/../fixtures/Soap/'.$name);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace BeSimple\SoapBundle\Tests\fixtures\ServiceBinding;
class Bar
{
private $foo;
private $bar;
public function __construct($foo, $bar)
{
$this->foo = $foo;
$this->bar = $bar;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace BeSimple\SoapBundle\Tests\fixtures\ServiceBinding;
class BarRecursive
{
private $foo;
public function __construct(FooRecursive $foo)
{
$this->foo = $foo;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace BeSimple\SoapBundle\Tests\fixtures\ServiceBinding;
class Foo
{
public $foo;
public $bar;
public function __construct($foo, $bar)
{
$this->foo = $foo;
$this->bar = $bar;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace BeSimple\SoapBundle\Tests\fixtures\ServiceBinding;
class FooBar
{
protected $foo;
protected $bar;
public function __construct(Foo $foo, Bar $bar)
{
$this->foo = $foo;
$this->bar = $bar;
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeSimple\SoapBundle\Tests\fixtures\ServiceBinding;
class FooRecursive
{
public $bar;
}

View File

@ -0,0 +1,29 @@
<?php
namespace BeSimple\SoapBundle\Tests\fixtures\ServiceBinding;
class SimpleArrays
{
public $array1;
private $array2;
private $array3;
public function __construct($array1, $array2, $array3)
{
$this->array1 = $array1;
$this->array2 = $array2;
$this->array3 = $array3;
}
public function getArray2()
{
return $this->array2;
}
public function getArray3()
{
return $this->array3;
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<webservice name="api" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns="http://christiankerl.github.com/WebServiceBundle/servicedefinition/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://christiankerl.github.com/WebServiceBundle/servicedefinition/1.0/ ./../../ServiceDefinition/Loader/schema/servicedefinition-1.0.xsd ">
<method name="math_multiply" controller="">
<argument name="a">
<type xml-type="xs:double" php-type="float"/>
</argument>
<argument name="b">
<type xml-type="xs:double" php-type="float"/>
</argument>
<return>
<type xml-type="xs:double" php-type="float"/>
</return>
</method>
</webservice>

View File

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="MathApi" targetNamespace="http://localhost/" xmlns:tns="http://localhost/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<wsdl:types xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://localhost/">
<xsd:complexType name="AuthHeader">
<xsd:sequence>
<xsd:element name="username" type="xsd:string"/>
<xsd:element name="password" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AuthHeaderElement" type="tns:AuthHeader"/>
<xsd:complexType name="DoubleArray">
<xsd:sequence>
<xsd:element name="item" type="xsd:double" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="DoubleArrayElement" type="tns:DoubleArray"/>
<xsd:complexType name="ComplexNumber">
<xsd:sequence>
<xsd:element name="realPart" type="xsd:double"/>
<xsd:element name="imaginaryPart" type="xsd:double"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ComplexNumberElement" type="tns:ComplexNumber"/>
<xsd:complexType name="ComplexNumberArray">
<xsd:sequence>
<xsd:element name="item" type="tns:ComplexNumber" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ComplexNumberArrayElement" type="tns:ComplexNumberArray"/>
</xsd:schema>
</wsdl:types>
<wsdl:portType name="MathApiPortType">
<wsdl:operation name="math_multiply" parameterOrder="a b">
<wsdl:input message="tns:math_multiplyRequest"/>
<wsdl:output message="tns:math_multiplyResponse"/>
</wsdl:operation>
<wsdl:operation name="SimpleMultiply" parameterOrder="a b">
<wsdl:input message="tns:SimpleMultiplyRequest"/>
<wsdl:output message="tns:SimpleMultiplyResponse"/>
</wsdl:operation>
<wsdl:operation name="SimpleMultiplyWithHeader" parameterOrder="AuthHeader a b">
<wsdl:input message="tns:SimpleMultiplyWithHeaderRequest"/>
<wsdl:output message="tns:SimpleMultiplyWithHeaderResponse"/>
</wsdl:operation>
<wsdl:operation name="ArrayMultiply" parameterOrder="factors">
<wsdl:input message="tns:ArrayMultiplyRequest"/>
<wsdl:output message="tns:ArrayMultiplyResponse"/>
</wsdl:operation>
<wsdl:operation name="ComplexMultiply" parameterOrder="input">
<wsdl:input message="tns:ComplexMultiplyRequest"/>
<wsdl:output message="tns:ComplexMultiplyResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="MathApiBinding" type="tns:MathApiPortType">
<soap:binding xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="math_multiply">
<soap:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" soapAction="http://localhost/math_multiply" style="rpc"/>
<wsdl:input xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="a b" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:input>
<wsdl:output xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="result" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="SimpleMultiply">
<soap:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" soapAction="http://localhost/SimpleMultiply" style="rpc"/>
<wsdl:input xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="a b" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:input>
<wsdl:output xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="result" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="SimpleMultiplyWithHeader">
<soap:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" soapAction="http://localhost/SimpleMultiplyWithHeader" style="rpc"/>
<wsdl:input xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="a b" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<soap:header xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" message="tns:SimpleMultiplyWithHeaderRequest" part="AuthHeader" use="literal" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:input>
<wsdl:output xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="result" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<soap:header xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" message="tns:SimpleMultiplyWithHeaderResponse" part="AuthHeader" use="literal" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="ArrayMultiply">
<soap:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" soapAction="http://localhost/ArrayMultiply" style="rpc"/>
<wsdl:input xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="factors" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:input>
<wsdl:output xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="result" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="ComplexMultiply">
<soap:operation xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" soapAction="http://localhost/ComplexMultiply" style="rpc"/>
<wsdl:input xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="input" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:input>
<wsdl:output xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<soap:body xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" parts="result" use="literal" namespace="http://localhost/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:message name="math_multiplyRequest">
<wsdl:part name="a" type="xsd:double"/>
<wsdl:part name="b" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="math_multiplyResponse">
<wsdl:part name="result" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="SimpleMultiplyRequest">
<wsdl:part name="a" type="xsd:double"/>
<wsdl:part name="b" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="SimpleMultiplyResponse">
<wsdl:part name="result" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="SimpleMultiplyWithHeaderRequest">
<wsdl:part name="AuthHeader" element="tns:AuthHeaderElement"/>
<wsdl:part name="a" type="xsd:double"/>
<wsdl:part name="b" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="SimpleMultiplyWithHeaderResponse">
<wsdl:part name="AuthHeader" element="tns:AuthHeaderElement"/>
<wsdl:part name="result" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="ArrayMultiplyRequest">
<wsdl:part name="factors" type="tns:DoubleArray"/>
</wsdl:message>
<wsdl:message name="ArrayMultiplyResponse">
<wsdl:part name="result" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="ComplexMultiplyRequest">
<wsdl:part name="input" type="tns:ComplexNumberArray"/>
</wsdl:message>
<wsdl:message name="ComplexMultiplyResponse">
<wsdl:part name="result" type="tns:ComplexNumber"/>
</wsdl:message>
<wsdl:service xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="MathApiService">
<wsdl:port xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="MathApiPort" binding="tns:MathApiBinding">
<soap:address xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" location="http://localhost/MathApi.php"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@ -0,0 +1,16 @@
--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7
Content-ID: <http://tempuri.org/0>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml"
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://xmlsoap.org/echoBinaryAsString</a:Action><a:MessageID>urn:uuid:1bf061d6-d532-4b0c-930b-8b8202c38b8a</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand="1">http://131.107.72.15/Mtom/svc/service.svc/Soap12MtomUTF8</a:To></s:Header><s:Body><EchoBinaryAsString xmlns="http://xmlsoap.org/Ping"><array><xop:Include href="cid:http%3A%2F%2Ftempuri.org%2F1%2F632618206527087310" xmlns:xop="http://www.w3.org/2004/08/xop/include"/></array></EchoBinaryAsString></s:Body></s:Envelope>
--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7
Content-ID: <http://tempuri.org/1/632618206527087310>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream
H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d !
--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7--

View File

@ -0,0 +1,33 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Util;
/**
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class Assert
{
const ARGUMENT_INVALID = 'Argument "%s" is invalid.';
const ARGUMENT_NULL = 'Argument "%s" can not be null.';
public static function thatArgument($name, $condition, $message = self::ARGUMENT_INVALID)
{
if(!$condition) {
throw new \InvalidArgumentException(sprintf($message, $name));
}
}
public static function thatArgumentNotNull($name, $value)
{
self::thatArgument($name, null !== $value, self::ARGUMENT_NULL);
}
}

View File

@ -0,0 +1,65 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Util;
class Collection implements \IteratorAggregate, \Countable
{
private $elements = array();
private $getter;
private $class;
public function __construct($getter, $class = null)
{
$this->getter = $getter;
$this->class = $class;
}
public function add($element)
{
if ($this->class && !$element instanceof $this->class) {
throw new \InvalidArgumentException(sprintf('Cannot add class "%s" because it is not an instance of "%s"', get_class($element), $this->class));
}
$this->elements[$element->{$this->getter}()] = $element;
}
public function addAll($elements)
{
foreach ($elements as $element) {
$this->add($element);
}
}
public function has($key)
{
return isset($this->elements[$key]);
}
public function get($key)
{
return $this->has($key) ? $this->elements[$key] : null;
}
public function clear()
{
$this->elements = array();
}
public function count()
{
return count($this->elements);
}
public function getIterator()
{
return new \ArrayIterator($this->elements);
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Util;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class QName
{
private $namespace;
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)
{
Assert::thatArgument('qname', preg_match('/^\{(.+)\}(.+)$/', $qname, $matches));
return new self($matches[1], $matches[2]);
}
public function __construct($namespace, $name)
{
$this->namespace = $namespace;
$this->name = $name;
}
public function getNamespace()
{
return $this->namespace;
}
public function getName()
{
return $this->name;
}
public function __toString()
{
return sprintf('{%s}%s', $this->getNamespace(), $this->getName());
}
}

View File

@ -0,0 +1,49 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle\Util;
/**
* String provides utility methods for strings.
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class String
{
/**
* Checks if a string starts with a given string.
*
* @param string $str A string
* @param string $substr A string to check against
*
* @return bool True if str starts with substr
*/
public static function startsWith($str, $substr)
{
if(is_string($str) && is_string($substr) && strlen($str) >= strlen($substr)) {
return $substr == substr($str, 0, strlen($substr));
}
}
/**
* Checks if a string ends with a given string.
*
* @param string $str A string
* @param string $substr A string to check against
*
* @return bool True if str ends with substr
*/
public static function endsWith($str, $substr)
{
if(is_string($str) && is_string($substr) && strlen($str) >= strlen($substr)) {
return $substr == substr($str, strlen($str) - strlen($substr));
}
}
}

View File

@ -0,0 +1,135 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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 BeSimple\SoapBundle;
use BeSimple\SoapBundle\Converter\TypeRepository;
use BeSimple\SoapBundle\ServiceBinding\MessageBinderInterface;
use BeSimple\SoapBundle\ServiceBinding\ServiceBinder;
use BeSimple\SoapBundle\ServiceDefinition\Dumper\DumperInterface;
use BeSimple\SoapCommon\Classmap;
use BeSimple\SoapCommon\Converter\TypeConverterCollection;
use BeSimple\SoapServer\SoapServerBuilder;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Loader\LoaderInterface;
/**
* WebServiceContext.
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class WebServiceContext
{
private $classmap;
private $typeRepository;
private $converterRepository;
private $wsdlFileDumper;
private $options;
private $serviceDefinition;
private $serviceBinder;
private $serverBuilder;
public function __construct(LoaderInterface $loader, DumperInterface $dumper, Classmap $classmap, TypeRepository $typeRepository, TypeConverterCollection $converters, array $options)
{
$this->loader = $loader;
$this->wsdlFileDumper = $dumper;
$this->classmap = $classmap;
$this->typeRepository = $typeRepository;
$this->converters = $converters;
$this->options = $options;
}
public function getServiceDefinition()
{
if (null === $this->serviceDefinition) {
$cacheDefinition = new ConfigCache(sprintf('%s/%s.definition.php', $this->options['cache_dir'], $this->options['name']), $this->options['debug']);
if ($cacheDefinition->isFresh()) {
$this->serviceDefinition = include (string) $cacheDefinition;
} else {
if (!$this->loader->supports($this->options['resource'], $this->options['resource_type'])) {
throw new \LogicException(sprintf('Cannot load "%s" (%s)', $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->setNamespace($this->options['namespace']);
$this->serviceDefinition->setClassmap($this->classmap);
$this->classmap = null;
$this->typeRepository->fixTypeInformation($this->serviceDefinition);
}
}
return $this->serviceDefinition;
}
public function getWsdlFileContent($endpoint = null)
{
return file_get_contents($this->getWsdlFile($endpoint));
}
public function getWsdlFile($endpoint = null)
{
$file = sprintf('%s/%s.%s.wsdl', $this->options['cache_dir'], $this->options['name'], md5($endpoint));
$cacheWsdl = new ConfigCache($file, $this->options['debug']);
if(!$cacheWsdl->isFresh()) {
$serviceDefinition = $this->getServiceDefinition();
$cacheWsdl->write($this->wsdlFileDumper->dumpServiceDefinition($serviceDefinition, $endpoint));
$cacheDefinition = new ConfigCache(sprintf('%s/%s.definition.php', $this->options['cache_dir'], $this->options['name']), $this->options['debug']);
$cacheDefinition->write('<?php return unserialize('.var_export(serialize($serviceDefinition), true).');');
}
return (string) $cacheWsdl;
}
public function getServiceBinder()
{
if (null === $this->serviceBinder) {
$this->serviceBinder = new ServiceBinder(
$this->getServiceDefinition(),
new $this->options['binder_request_header_class'](),
new $this->options['binder_request_class'](),
new $this->options['binder_response_class']()
);
}
return $this->serviceBinder;
}
public function getServerBuilder()
{
if (null === $this->serverBuilder) {
$this->serverBuilder = SoapServerBuilder::createWithDefaults()
->withWsdl($this->getWsdlFile())
->withClassmap($this->getServiceDefinition()->getClassmap())
->withTypeConverters($this->converters)
;
if (null !== $this->options['cache_type']) {
$this->serverBuilder->withWsdlCache($this->options['cache_type']);
}
}
return $this->serverBuilder;
}
}

View File

@ -0,0 +1,41 @@
{
"name": "besimple/soap-bundle",
"type": "symfony-bundle",
"description": "Build and consume SOAP and WSDL based web services with Symfony2",
"keywords": [ "soap", "soap-bundle" ],
"homepage": "https://github.com/BeSimple/BeSimpleSoapBundle",
"license": "MIT",
"authors": [
{
"name": "Francis Besset",
"email": "francis.besset@gmail.com"
},
{
"name": "Christian Kerl",
"email": "christian-kerl@web.de"
},
{
"name": "Andreas Schamberger",
"email": "mail@andreass.net"
}
],
"require": {
"php": ">=5.3.0",
"besimple/soap-common": "dev-master",
"ass/xmlsecurity": "dev-master",
"zendframework/zend-soap": "2.1.*",
"zendframework/zend-mime": "2.1.*",
"zendframework/zend-mail": "2.1.*",
"symfony/http-foundation": ">=2.0,<2.4-dev"
},
"suggest": {
"besimple/soap-client": "dev-master",
"besimple/soap-server": "dev-master"
},
"autoload": {
"psr-0": {
"BeSimple\\SoapBundle": ""
}
},
"target-dir": "BeSimple/SoapBundle"
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="SoapBundle Test Suite">
<directory>./Tests</directory>
</testsuite>
</testsuites>
<!--
<logging>
<log type="coverage-html" target="build/coverage" />
</logging>
-->
<filter>
<whitelist>
<directory>.</directory>
<exclude>
<directory>Tests</directory>
<directory>vendor</directory>
<file>vendors.php</file>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,41 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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.
*/
/*
CAUTION: This file installs the dependencies needed to run the BeSimpleSoapBundle test suite.
https://github.com/BeSimple/BeSimpleSoapBundle
*/
if (!is_dir($vendorDir = dirname(__FILE__).'/vendor')) {
mkdir($vendorDir, 0777, true);
}
$deps = array(
array('symfony', 'http://github.com/symfony/symfony.git', 'origin/HEAD'),
array('zend-framework/library/Zend/Soap', 'http://github.com/BeSimple/zend-soap.git', 'origin/HEAD'),
array('zend-framework/library/Zend/Mime', 'http://github.com/BeSimple/zend-mime.git', 'origin/HEAD'),
);
foreach ($deps as $dep) {
list($name, $url, $rev) = $dep;
echo "> Installing/Updating $name\n";
$installDir = $vendorDir.'/'.$name;
if (!is_dir($installDir)) {
system(sprintf('git clone %s %s', escapeshellarg($url), escapeshellarg($installDir)));
}
system(sprintf('cd %s && git fetch origin && git reset --hard %s', escapeshellarg($installDir), escapeshellarg($rev)));
}