├── .gitignore ├── BeSimpleSoapBundle.php ├── CONTRIBUTORS.md ├── Cache.php ├── Controller └── SoapWebServiceController.php ├── Converter ├── TypeRepository.php └── XopIncludeTypeConverter.php ├── DependencyInjection ├── BeSimpleSoapExtension.php ├── Compiler │ ├── TypeConverterPass.php │ └── WebServiceResolverPass.php └── Configuration.php ├── EventListener ├── RequestFormatListener.php ├── SoapExceptionListener.php └── SoapResponseListener.php ├── Handler ├── ExceptionHandler.php └── wsdl │ └── exception.wsdl ├── README.md ├── Resources ├── config │ ├── client.xml │ ├── converters.xml │ ├── loaders.xml │ ├── request.xml │ ├── routing │ │ └── webservicecontroller.xml │ ├── soap.xml │ └── webservice.xml ├── doc │ ├── _static │ │ └── .gitkeep │ ├── cache.rst │ ├── conf.py │ ├── index.rst │ ├── installation.rst │ ├── requirements.rst │ ├── soapclient │ │ └── configuration.rst │ └── soapserver │ │ ├── configuration.rst │ │ ├── tutorial │ │ ├── array.rst │ │ ├── associative_array.rst │ │ ├── complex_type.rst │ │ └── header.rst │ │ ├── tutorials.rst │ │ └── types.rst └── meta │ └── LICENSE ├── ServiceBinding ├── DocumentLiteralWrappedRequestMessageBinder.php ├── DocumentLiteralWrappedResponseMessageBinder.php ├── MessageBinderInterface.php ├── RpcLiteralRequestHeaderMessageBinder.php ├── RpcLiteralRequestMessageBinder.php ├── RpcLiteralResponseMessageBinder.php └── ServiceBinder.php ├── ServiceDefinition ├── Annotation │ ├── Alias.php │ ├── ComplexType.php │ ├── Configuration.php │ ├── ConfigurationInterface.php │ ├── Header.php │ ├── Method.php │ ├── Param.php │ ├── Result.php │ └── TypedElementInterface.php ├── ComplexType.php ├── Definition.php ├── Loader │ ├── AnnotationClassLoader.php │ ├── AnnotationComplexTypeLoader.php │ ├── AnnotationFileLoader.php │ └── schema │ │ └── servicedefinition-1.0.xsd └── Method.php ├── Soap ├── SoapAttachment.php ├── SoapClientBuilder.php ├── SoapHeader.php ├── SoapRequest.php └── SoapResponse.php ├── TODO.md ├── Tests ├── ServiceBinding │ ├── RpcLiteralRequestMessageBinderTest.php │ └── fixtures │ │ ├── Attributes.php │ │ ├── ComplexType.php │ │ └── Setters.php ├── Soap │ └── SoapRequestTest.php └── fixtures │ ├── ServiceBinding │ ├── Bar.php │ ├── BarRecursive.php │ ├── Foo.php │ ├── FooBar.php │ ├── FooRecursive.php │ └── SimpleArrays.php │ └── Soap │ ├── api-servicedefinition.xml │ ├── api.wsdl │ └── mtom │ └── simple.txt ├── Util ├── Assert.php ├── Collection.php ├── QName.php └── String.php ├── WebServiceContext.php ├── composer.json └── phpunit.xml.dist /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | phpunit.xml 4 | -------------------------------------------------------------------------------- /BeSimpleSoapBundle.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle; 12 | 13 | use BeSimple\SoapBundle\DependencyInjection\Compiler\WebServiceResolverPass; 14 | use BeSimple\SoapBundle\DependencyInjection\Compiler\TypeConverterPass; 15 | 16 | use Symfony\Component\DependencyInjection\ContainerBuilder; 17 | use Symfony\Component\HttpKernel\Bundle\Bundle; 18 | 19 | /** 20 | * BeSimpleSoapBundle. 21 | * 22 | * @author Christian Kerl 23 | */ 24 | class BeSimpleSoapBundle extends Bundle 25 | { 26 | public function build(ContainerBuilder $container) 27 | { 28 | parent::build($container); 29 | 30 | $container->addCompilerPass(new WebServiceResolverPass()); 31 | $container->addCompilerPass(new TypeConverterPass()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | * [Christian Kerl](https://github.com/christiankerl) 2 | * [Francis Besset](https://github.com/francisbesset) 3 | * [Šarūnas Dubinskas](https://github.com/sarunas) 4 | * [Craig Marvelley](https://github.com/craigmarvelley) 5 | -------------------------------------------------------------------------------- /Cache.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle; 14 | 15 | use BeSimple\SoapCommon\Cache as BaseCache; 16 | 17 | /** 18 | * @author Francis Besset 19 | */ 20 | class Cache 21 | { 22 | public function __construct($cacheDisabled, $type, $directory, $lifetime = null, $limit = null) 23 | { 24 | $isEnabled = (Boolean) $cacheDisabled ? BaseCache::DISABLED : BaseCache::ENABLED; 25 | 26 | BaseCache::setEnabled($isEnabled); 27 | 28 | BaseCache::setType($type); 29 | BaseCache::setDirectory($directory); 30 | 31 | if (null !== $lifetime) { 32 | BaseCache::setLifetime($lifetime); 33 | } 34 | 35 | if (null !== $limit) { 36 | BaseCache::setLimit($limit); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Controller/SoapWebServiceController.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\Controller; 14 | 15 | use BeSimple\SoapBundle\Handler\ExceptionHandler; 16 | use BeSimple\SoapBundle\Soap\SoapRequest; 17 | use BeSimple\SoapBundle\Soap\SoapResponse; 18 | use BeSimple\SoapServer\SoapServerBuilder; 19 | use Symfony\Component\DependencyInjection\ContainerAware; 20 | use Symfony\Component\HttpFoundation\Request; 21 | use Symfony\Component\HttpFoundation\Response; 22 | use Symfony\Component\HttpKernel\Exception\FlattenException; 23 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 24 | use Symfony\Component\HttpKernel\HttpKernelInterface; 25 | use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; 26 | 27 | /** 28 | * @author Christian Kerl 29 | * @author Francis Besset 30 | */ 31 | class SoapWebServiceController extends ContainerAware 32 | { 33 | /** 34 | * @var \SoapServer 35 | */ 36 | protected $soapServer; 37 | 38 | /** 39 | * @var \BeSimple\SoapBundle\Soap\SoapRequest 40 | */ 41 | protected $soapRequest; 42 | 43 | /** 44 | * @var \BeSimple\SoapBundle\Soap\SoapResponse 45 | */ 46 | protected $soapResponse; 47 | 48 | /** 49 | * @var \BeSimple\SoapBundle\ServiceBinding\ServiceBinder 50 | */ 51 | protected $serviceBinder; 52 | 53 | /** 54 | * @var array 55 | */ 56 | private $headers = array(); 57 | 58 | /** 59 | * @return \BeSimple\SoapBundle\Soap\SoapResponse 60 | */ 61 | public function callAction($webservice) 62 | { 63 | $webServiceContext = $this->getWebServiceContext($webservice); 64 | 65 | $this->serviceBinder = $webServiceContext->getServiceBinder(); 66 | 67 | $this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request')); 68 | $this->soapServer = $webServiceContext 69 | ->getServerBuilder() 70 | ->withSoapVersion11() 71 | ->withHandler($this) 72 | ->build() 73 | ; 74 | 75 | ob_start(); 76 | $this->soapServer->handle($this->soapRequest->getSoapMessage()); 77 | 78 | $response = $this->getResponse(); 79 | $response->setContent(ob_get_clean()); 80 | 81 | // The Symfony 2.0 Response::setContent() does not return the Response instance 82 | return $response; 83 | } 84 | 85 | /** 86 | * @return Symfony\Component\HttpFoundation\Response 87 | */ 88 | public function definitionAction($webservice) 89 | { 90 | $response = new Response($this->getWebServiceContext($webservice)->getWsdlFileContent( 91 | $this->container->get('router')->generate( 92 | '_webservice_call', 93 | array('webservice' => $webservice), 94 | true 95 | ) 96 | )); 97 | 98 | $request = $this->container->get('request'); 99 | $query = $request->query; 100 | if ($query->has('wsdl') || $query->has('WSDL')) { 101 | $request->setRequestFormat('wsdl'); 102 | } 103 | 104 | return $response; 105 | } 106 | 107 | /** 108 | * Converts an Exception to a SoapFault Response. 109 | * 110 | * @param Request $request The request 111 | * @param FlattenException $exception A FlattenException instance 112 | * @param DebugLoggerInterface $logger A DebugLoggerInterface instance 113 | * 114 | * @return Response 115 | * 116 | * @throws \LogicException When the request query parameter "_besimple_soap_webservice" does not exist 117 | */ 118 | public function exceptionAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null) 119 | { 120 | if (!$webservice = $request->query->get('_besimple_soap_webservice')) { 121 | throw new \LogicException(sprintf('The parameter "%s" is required in Request::$query parameter bag to generate the SoapFault.', '_besimple_soap_webservice'), null, $e); 122 | } 123 | 124 | $view = 'TwigBundle:Exception:'.($this->container->get('kernel')->isDebug() ? 'exception' : 'error').'.txt.twig'; 125 | $code = $exception->getStatusCode(); 126 | $details = $this->container->get('templating')->render($view, array( 127 | 'status_code' => $code, 128 | 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', 129 | 'exception' => $exception, 130 | 'logger' => $logger, 131 | )); 132 | 133 | $handler = new ExceptionHandler($exception, $details); 134 | if ($soapFault = $request->query->get('_besimple_soap_fault')) { 135 | $handler->setSoapFault($soapFault); 136 | 137 | // Remove parameter from query because cannot be Serialized in Logger 138 | $request->query->remove('_besimple_soap_fault'); 139 | } 140 | 141 | $server = SoapServerBuilder::createWithDefaults() 142 | ->withWsdl(__DIR__.'/../Handler/wsdl/exception.wsdl') 143 | ->withWsdlCacheNone() 144 | ->withHandler($handler) 145 | ->build() 146 | ; 147 | 148 | ob_start(); 149 | $server->handle( 150 | ''. 151 | ''. 152 | ''. 153 | ''. 154 | ''. 155 | '' 156 | ); 157 | 158 | return new Response(ob_get_clean()); 159 | } 160 | 161 | /** 162 | * This method gets called once for every SOAP header the \SoapServer received 163 | * and afterwards once for the called SOAP operation. 164 | * 165 | * @param string $method The SOAP header or SOAP operation name 166 | * @param array $arguments 167 | * 168 | * @return mixed 169 | */ 170 | public function __call($method, $arguments) 171 | { 172 | if ($this->serviceBinder->isServiceMethod($method)) { 173 | // @TODO Add all SoapHeaders in SoapRequest 174 | foreach ($this->headers as $name => $value) { 175 | if ($this->serviceBinder->isServiceHeader($method, $name)) { 176 | $this->soapRequest->getSoapHeaders()->add($this->serviceBinder->processServiceHeader($method, $name, $value)); 177 | } 178 | } 179 | $this->headers = null; 180 | 181 | $this->soapRequest->attributes->add( 182 | $this->serviceBinder->processServiceMethodArguments($method, $arguments) 183 | ); 184 | 185 | // forward to controller 186 | $response = $this->container->get('http_kernel')->handle($this->soapRequest, HttpKernelInterface::SUB_REQUEST, false); 187 | 188 | $this->setResponse($response); 189 | 190 | // add response soap headers to soap server 191 | foreach ($response->getSoapHeaders() as $header) { 192 | $this->soapServer->addSoapHeader($header->toNativeSoapHeader()); 193 | } 194 | 195 | // return operation return value to soap server 196 | return $this->serviceBinder->processServiceMethodReturnValue( 197 | $method, 198 | $response->getReturnValue() 199 | ); 200 | } else { 201 | // collect request soap headers 202 | $this->headers[$method] = $arguments[0]; 203 | } 204 | } 205 | 206 | /** 207 | * @return \BeSimple\SoapBundle\Soap\SoapRequest 208 | */ 209 | protected function getRequest() 210 | { 211 | return $this->soapRequest; 212 | } 213 | 214 | /** 215 | * @return \BeSimple\SoapBundle\Soap\SoapResponse 216 | */ 217 | protected function getResponse() 218 | { 219 | return $this->soapResponse ?: $this->soapResponse = $this->container->get('besimple.soap.response'); 220 | } 221 | 222 | /** 223 | * Set the SoapResponse 224 | * 225 | * @param Response $response A response to check and set 226 | * 227 | * @return \BeSimple\SoapBundle\Soap\SoapResponse A valid SoapResponse 228 | * 229 | * @throws InvalidArgumentException If the given Response is not an instance of SoapResponse 230 | */ 231 | protected function setResponse(Response $response) 232 | { 233 | if (!$response instanceof SoapResponse) { 234 | throw new \InvalidArgumentException('You must return an instance of BeSimple\SoapBundle\Soap\SoapResponse'); 235 | } 236 | 237 | return $this->soapResponse = $response; 238 | } 239 | 240 | private function getWebServiceContext($webservice) 241 | { 242 | $context = sprintf('besimple.soap.context.%s', $webservice); 243 | 244 | if (!$this->container->has($context)) { 245 | throw new NotFoundHttpException(sprintf('No WebService with name "%s" found.', $webservice)); 246 | } 247 | 248 | return $this->container->get($context); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Converter/TypeRepository.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Converter; 12 | 13 | use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition; 14 | use BeSimple\SoapBundle\Util\Assert; 15 | 16 | /** 17 | * @author Christian Kerl 18 | */ 19 | class TypeRepository 20 | { 21 | const ARRAY_SUFFIX = '[]'; 22 | 23 | private $xmlNamespaces = array(); 24 | private $defaultTypeMap = array(); 25 | 26 | public function addXmlNamespace($prefix, $url) 27 | { 28 | $this->xmlNamespaces[$prefix] = $url; 29 | } 30 | 31 | public function getXmlNamespace($prefix) 32 | { 33 | return $this->xmlNamespaces[$prefix]; 34 | } 35 | 36 | public function addDefaultTypeMapping($phpType, $xmlType) 37 | { 38 | Assert::thatArgumentNotNull('phpType', $phpType); 39 | Assert::thatArgumentNotNull('xmlType', $xmlType); 40 | 41 | $this->defaultTypeMap[$phpType] = $xmlType; 42 | } 43 | 44 | public function getXmlTypeMapping($phpType) 45 | { 46 | return isset($this->defaultTypeMap[$phpType]) ? $this->defaultTypeMap[$phpType] : null; 47 | } 48 | 49 | public function fixTypeInformation(ServiceDefinition $definition) 50 | { 51 | foreach($definition->getAllTypes() as $type) { 52 | $phpType = $type->getPhpType(); 53 | $xmlType = $type->getXmlType(); 54 | 55 | if (null === $phpType) { 56 | throw new \InvalidArgumentException(); 57 | } 58 | 59 | if (null === $xmlType) { 60 | $xmlType = $this->getXmlTypeMapping($phpType); 61 | } 62 | 63 | $type->setXmlType($xmlType); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Converter/XopIncludeTypeConverter.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Converter; 12 | 13 | use BeSimple\SoapBundle\Soap\SoapRequest; 14 | use BeSimple\SoapBundle\Soap\SoapResponse; 15 | use BeSimple\SoapBundle\Util\String; 16 | use BeSimple\SoapCommon\Converter\TypeConverterInterface; 17 | 18 | /** 19 | * @author Christian Kerl 20 | */ 21 | class XopIncludeTypeConverter implements TypeConverterInterface 22 | { 23 | public function getTypeNamespace() 24 | { 25 | return 'http://www.w3.org/2001/XMLSchema'; 26 | } 27 | 28 | public function getTypeName() 29 | { 30 | return 'base64Binary'; 31 | } 32 | 33 | public function convertXmlToPhp(SoapRequest $request, $data) 34 | { 35 | $doc = new \DOMDocument(); 36 | $doc->loadXML($data); 37 | 38 | $includes = $doc->getElementsByTagNameNS('http://www.w3.org/2004/08/xop/include', 'Include'); 39 | $include = $includes->item(0); 40 | 41 | $ref = $include->getAttribute('href'); 42 | 43 | if (String::startsWith($ref, 'cid:')) { 44 | $cid = urldecode(substr($ref, 4)); 45 | 46 | return $request->getSoapAttachments()->get($cid)->getContent(); 47 | } 48 | 49 | return $data; 50 | } 51 | 52 | public function convertPhpToXml(SoapResponse $response, $data) 53 | { 54 | return $data; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /DependencyInjection/BeSimpleSoapExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\DependencyInjection; 14 | 15 | use BeSimple\SoapCommon\Cache; 16 | 17 | use Symfony\Component\Config\Definition\Processor; 18 | use Symfony\Component\Config\FileLocator; 19 | use Symfony\Component\DependencyInjection\ContainerBuilder; 20 | use Symfony\Component\DependencyInjection\DefinitionDecorator; 21 | use Symfony\Component\DependencyInjection\Reference; 22 | use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; 23 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; 24 | 25 | /** 26 | * BeSimpleSoapExtension. 27 | * 28 | * @author Christian Kerl 29 | * @author Francis Besset 30 | */ 31 | class BeSimpleSoapExtension extends Extension 32 | { 33 | // maps config options to service suffix 34 | private $bindingConfigToServiceSuffixMap = array( 35 | 'rpc-literal' => 'rpcliteral', 36 | 'document-wrapped' => 'documentwrapped', 37 | ); 38 | 39 | public function load(array $configs, ContainerBuilder $container) 40 | { 41 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 42 | 43 | $loader->load('request.xml'); 44 | 45 | $loader->load('loaders.xml'); 46 | $loader->load('converters.xml'); 47 | $loader->load('webservice.xml'); 48 | 49 | $processor = new Processor(); 50 | $configuration = new Configuration(); 51 | 52 | $config = $processor->process($configuration->getConfigTree(), $configs); 53 | 54 | $this->registerCacheConfiguration($config['cache'], $container, $loader); 55 | 56 | if (!empty($config['clients'])) { 57 | $this->registerClientConfiguration($config['clients'], $container, $loader); 58 | } 59 | 60 | $container->setParameter('besimple.soap.definition.dumper.options.stylesheet', $config['wsdl_dumper']['stylesheet']); 61 | 62 | foreach($config['services'] as $name => $serviceConfig) { 63 | $serviceConfig['name'] = $name; 64 | $this->createWebServiceContext($serviceConfig, $container); 65 | } 66 | 67 | $container->setParameter('besimple.soap.exception_listener.controller', $config['exception_controller']); 68 | } 69 | 70 | private function registerCacheConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) 71 | { 72 | $loader->load('soap.xml'); 73 | 74 | $config['type'] = $this->getCacheType($config['type']); 75 | 76 | foreach (array('type', 'lifetime', 'limit') as $key) { 77 | $container->setParameter('besimple.soap.cache.'.$key, $config[$key]); 78 | } 79 | } 80 | 81 | private function registerClientConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) 82 | { 83 | $loader->load('client.xml'); 84 | 85 | foreach ($config as $client => $options) { 86 | $definition = new DefinitionDecorator('besimple.soap.client.builder'); 87 | $container->setDefinition(sprintf('besimple.soap.client.builder.%s', $client), $definition); 88 | 89 | $definition->replaceArgument(0, $options['wsdl']); 90 | 91 | $defOptions = $container 92 | ->getDefinition('besimple.soap.client.builder') 93 | ->getArgument(1); 94 | 95 | foreach (array('cache_type', 'user_agent') as $key) { 96 | if (isset($options[$key])) { 97 | $defOptions[$key] = $options[$key]; 98 | } 99 | } 100 | 101 | $proxy = $options['proxy']; 102 | if (false !== $proxy['host']) { 103 | if (null !== $proxy['auth']) { 104 | if ('basic' === $proxy['auth']) { 105 | $proxy['auth'] = \CURLAUTH_BASIC; 106 | } elseif ('ntlm' === $proxy['auth']) { 107 | $proxy['auth'] = \CURLAUTH_NTLM; 108 | } 109 | } 110 | 111 | $definition->addMethodCall('withProxy', array( 112 | $proxy['host'], $proxy['port'], 113 | $proxy['login'], $proxy['password'], 114 | $proxy['auth'] 115 | )); 116 | } 117 | 118 | if (isset($defOptions['cache_type'])) { 119 | $defOptions['cache_type'] = $this->getCacheType($defOptions['cache_type']); 120 | } 121 | 122 | $definition->replaceArgument(1, $defOptions); 123 | 124 | $classmap = $this->createClientClassmap($client, $options['classmap'], $container); 125 | $definition->replaceArgument(2, new Reference($classmap)); 126 | 127 | $this->createClient($client, $container); 128 | } 129 | } 130 | 131 | private function createClientClassmap($client, array $classmap, ContainerBuilder $container) 132 | { 133 | $definition = new DefinitionDecorator('besimple.soap.classmap'); 134 | $container->setDefinition(sprintf('besimple.soap.classmap.%s', $client), $definition); 135 | 136 | if (!empty($classmap)) { 137 | $definition->setMethodCalls(array( 138 | array('set', array($classmap)), 139 | )); 140 | } 141 | 142 | return sprintf('besimple.soap.classmap.%s', $client); 143 | } 144 | 145 | private function createClient($client, ContainerBuilder $container) 146 | { 147 | $definition = new DefinitionDecorator('besimple.soap.client'); 148 | $container->setDefinition(sprintf('besimple.soap.client.%s', $client), $definition); 149 | 150 | $definition->setFactory(array( 151 | new Reference(sprintf('besimple.soap.client.builder.%s', $client)), 152 | 'build' 153 | )); 154 | } 155 | 156 | private function createWebServiceContext(array $config, ContainerBuilder $container) 157 | { 158 | $bindingSuffix = $this->bindingConfigToServiceSuffixMap[$config['binding']]; 159 | unset($config['binding']); 160 | 161 | $contextId = 'besimple.soap.context.'.$config['name']; 162 | $definition = new DefinitionDecorator('besimple.soap.context.'.$bindingSuffix); 163 | $container->setDefinition($contextId, $definition); 164 | 165 | if (isset($config['cache_type'])) { 166 | $config['cache_type'] = $this->getCacheType($config['cache_type']); 167 | } 168 | 169 | $options = $container 170 | ->getDefinition('besimple.soap.context.'.$bindingSuffix) 171 | ->getArgument(2); 172 | 173 | $definition->replaceArgument(2, array_merge($options, $config)); 174 | } 175 | 176 | private function getCacheType($type) 177 | { 178 | switch ($type) { 179 | case 'none': 180 | return Cache::TYPE_NONE; 181 | 182 | case 'disk': 183 | return Cache::TYPE_DISK; 184 | 185 | case 'memory': 186 | return Cache::TYPE_MEMORY; 187 | 188 | case 'disk_memory': 189 | return Cache::TYPE_DISK_MEMORY; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /DependencyInjection/Compiler/TypeConverterPass.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\DependencyInjection\Compiler; 12 | 13 | use Symfony\Component\DependencyInjection\Reference; 14 | use Symfony\Component\DependencyInjection\ContainerBuilder; 15 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 16 | 17 | /** 18 | * Adds tagged besimple.soap.converter services to besimple.soap.converter.repository service 19 | * 20 | * @author Christian Kerl 21 | */ 22 | class TypeConverterPass implements CompilerPassInterface 23 | { 24 | public function process(ContainerBuilder $container) 25 | { 26 | if (false === $container->hasDefinition('besimple.soap.converter.collection')) { 27 | return; 28 | } 29 | 30 | $definition = $container->getDefinition('besimple.soap.converter.collection'); 31 | 32 | foreach ($container->findTaggedServiceIds('besimple.soap.converter') as $id => $attributes) { 33 | $definition->addMethodCall('add', array(new Reference($id))); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DependencyInjection/Compiler/WebServiceResolverPass.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\DependencyInjection\Compiler; 12 | 13 | use Symfony\Component\DependencyInjection\Reference; 14 | use Symfony\Component\DependencyInjection\ContainerBuilder; 15 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 16 | 17 | /** 18 | * Adds tagged besimple.soap.definition.loader services to ebservice.definition.resolver service 19 | * 20 | * @author Francis Besset 21 | */ 22 | class WebServiceResolverPass implements CompilerPassInterface 23 | { 24 | public function process(ContainerBuilder $container) 25 | { 26 | if (false === $container->hasDefinition('besimple.soap.definition.loader.resolver')) { 27 | return; 28 | } 29 | 30 | $definition = $container->getDefinition('besimple.soap.definition.loader.resolver'); 31 | 32 | foreach ($container->findTaggedServiceIds('besimple.soap.definition.loader') as $id => $attributes) { 33 | $definition->addMethodCall('addLoader', array(new Reference($id))); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\DependencyInjection; 14 | 15 | use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; 16 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; 17 | 18 | /** 19 | * WebServiceExtension configuration structure. 20 | * 21 | * @author Christian Kerl 22 | * @author Francis Besset 23 | */ 24 | class Configuration 25 | { 26 | private $cacheTypes = array('none', 'disk', 'memory', 'disk_memory'); 27 | private $proxyAuth = array('basic', 'ntlm'); 28 | 29 | /** 30 | * Generates the configuration tree. 31 | * 32 | * @return \Symfony\Component\Config\Definition\ArrayNode The config tree 33 | */ 34 | public function getConfigTree() 35 | { 36 | $treeBuilder = new TreeBuilder(); 37 | $rootNode = $treeBuilder->root('be_simple_soap'); 38 | 39 | $this->addCacheSection($rootNode); 40 | $this->addClientSection($rootNode); 41 | $this->addServicesSection($rootNode); 42 | $this->addWsdlDumperSection($rootNode); 43 | 44 | $rootNode 45 | ->children() 46 | ->scalarNode('exception_controller')->defaultValue('BeSimpleSoapBundle:SoapWebService:exception')->end() 47 | ->end() 48 | ; 49 | 50 | return $treeBuilder->buildTree(); 51 | } 52 | 53 | private function addCacheSection(ArrayNodeDefinition $rootNode) 54 | { 55 | $rootNode 56 | ->children() 57 | ->arrayNode('cache') 58 | ->addDefaultsIfNotSet() 59 | ->children() 60 | ->scalarNode('type') 61 | ->defaultValue('disk') 62 | ->validate() 63 | ->ifNotInArray($this->cacheTypes) 64 | ->thenInvalid(sprintf('The cache type has to be either %s', implode(', ', $this->cacheTypes))) 65 | ->end() 66 | ->end() 67 | ->scalarNode('lifetime')->defaultNull()->end() 68 | ->scalarNode('limit')->defaultNull()->end() 69 | ->end() 70 | ->end() 71 | ->end() 72 | ->end() 73 | ; 74 | } 75 | 76 | private function addClientSection(ArrayNodeDefinition $rootNode) 77 | { 78 | $rootNode 79 | ->children() 80 | ->arrayNode('clients') 81 | ->useAttributeAsKey('name') 82 | ->prototype('array') 83 | ->children() 84 | ->scalarNode('wsdl')->isRequired()->end() 85 | ->scalarNode('user_agent')->end() 86 | ->scalarNode('cache_type') 87 | ->validate() 88 | ->ifNotInArray($this->cacheTypes) 89 | ->thenInvalid(sprintf('The cache type has to be either: %s', implode(', ', $this->cacheTypes))) 90 | ->end() 91 | ->end() 92 | ->arrayNode('classmap') 93 | ->useAttributeAsKey('name')->prototype('scalar')->end() 94 | ->end() 95 | ->arrayNode('proxy') 96 | ->info('proxy configuration') 97 | ->addDefaultsIfNotSet() 98 | ->beforeNormalization() 99 | ->ifTrue(function ($v) { return !is_array($v); }) 100 | ->then(function ($v) { return array('host' => null === $v ? false : $v); }) 101 | ->end() 102 | ->children() 103 | ->scalarNode('host')->defaultFalse()->end() 104 | ->scalarNode('port')->defaultValue(3128)->end() 105 | ->scalarNode('login')->defaultNull()->end() 106 | ->scalarNode('password')->defaultNull()->end() 107 | ->scalarNode('auth') 108 | ->defaultNull() 109 | ->validate() 110 | ->ifNotInArray($this->proxyAuth) 111 | ->thenInvalid(sprintf('The proxy auth has to be either: %s', implode(', ', $this->proxyAuth))) 112 | ->end() 113 | ->end() 114 | ->end() 115 | ->end() 116 | ->end() 117 | ->end() 118 | ->end() 119 | ->end() 120 | ->end() 121 | ; 122 | } 123 | 124 | private function addServicesSection(ArrayNodeDefinition $rootNode) 125 | { 126 | $rootNode 127 | ->children() 128 | ->arrayNode('services') 129 | ->useAttributeAsKey('name') 130 | ->prototype('array') 131 | ->children() 132 | ->scalarNode('namespace')->isRequired()->end() 133 | ->scalarNode('resource')->defaultValue('*')->end() 134 | ->scalarNode('resource_type')->defaultValue('annotation')->end() 135 | ->scalarNode('binding') 136 | ->defaultValue('document-wrapped') 137 | ->validate() 138 | ->ifNotInArray(array('rpc-literal', 'document-wrapped')) 139 | ->thenInvalid("Service binding style has to be either 'rpc-literal' or 'document-wrapped'") 140 | ->end() 141 | ->end() 142 | ->scalarNode('cache_type') 143 | ->validate() 144 | ->ifNotInArray($this->cacheTypes) 145 | ->thenInvalid(sprintf('The cache type has to be either %s', implode(', ', $this->cacheTypes))) 146 | ->end() 147 | ->end() 148 | ->end() 149 | ->end() 150 | ->end() 151 | ->end() 152 | ; 153 | } 154 | 155 | private function addWsdlDumperSection(ArrayNodeDefinition $rootNode) 156 | { 157 | $rootNode 158 | ->children() 159 | ->arrayNode('wsdl_dumper') 160 | ->addDefaultsIfNotSet() 161 | ->children() 162 | ->scalarNode('stylesheet')->defaultNull()->end() 163 | ->end() 164 | ->end() 165 | ->end() 166 | ->end() 167 | ; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /EventListener/RequestFormatListener.php: -------------------------------------------------------------------------------- 1 | getRequest()->setFormat('wsdl', 'application/wsdl+xml'); 12 | $event->getRequest()->setFormat('soap', 'application/soap+xml'); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /EventListener/SoapExceptionListener.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\EventListener; 14 | 15 | use Symfony\Component\DependencyInjection\ContainerInterface; 16 | use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; 17 | use Symfony\Component\HttpKernel\EventListener\ExceptionListener; 18 | use Symfony\Component\HttpKernel\HttpKernelInterface; 19 | use Symfony\Component\HttpKernel\KernelEvents; 20 | 21 | /** 22 | * @author Francis Besset 23 | */ 24 | class SoapExceptionListener extends ExceptionListener 25 | { 26 | /** 27 | * @var ContainerInterface 28 | */ 29 | protected $container; 30 | 31 | /** 32 | * To avoid conflict between , the logger param is not typed: 33 | * The parent class needs and instance of `Psr\Log\LoggerInterface` from Symfony 2.2, 34 | * before logger is an instance of `Symfony\Component\HttpKernel\Log\LoggerInterface`. 35 | * 36 | * @param ContainerInterface $container A ContainerInterface instance 37 | * @param string $controller The controller name to call 38 | * @param LoggerInterface $logger A logger instance 39 | */ 40 | public function __construct(ContainerInterface $container, $controller, $logger) 41 | { 42 | parent::__construct($controller, $logger); 43 | 44 | $this->container = $container; 45 | } 46 | 47 | public function onKernelException(GetResponseForExceptionEvent $event) 48 | { 49 | if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { 50 | return; 51 | } 52 | 53 | $request = $event->getRequest(); 54 | if (!in_array($request->getRequestFormat(), array('soap', 'xml'))) { 55 | return; 56 | } elseif ('xml' === $request->getRequestFormat() && '_webservice_call' !== $request->attributes->get('_route')) { 57 | return; 58 | } 59 | 60 | $attributes = $request->attributes; 61 | if (!$webservice = $attributes->get('webservice')) { 62 | return; 63 | } 64 | 65 | if (!$this->container->has(sprintf('besimple.soap.context.%s', $webservice))) { 66 | return; 67 | } 68 | 69 | // hack to retrieve the current WebService name in the controller 70 | $request->query->set('_besimple_soap_webservice', $webservice); 71 | 72 | $exception = $event->getException(); 73 | if ($exception instanceof \SoapFault) { 74 | $request->query->set('_besimple_soap_fault', $exception); 75 | } 76 | 77 | parent::onKernelException($event); 78 | } 79 | 80 | public static function getSubscribedEvents() 81 | { 82 | return array( 83 | // Must be called before ExceptionListener of HttpKernel component 84 | KernelEvents::EXCEPTION => array('onKernelException', -64), 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /EventListener/SoapResponseListener.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\EventListener; 14 | 15 | use BeSimple\SoapBundle\Soap\SoapRequest; 16 | use BeSimple\SoapBundle\Soap\SoapResponse; 17 | use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; 18 | 19 | /** 20 | * SoapResponseListener. 21 | * 22 | * @author Francis Besset 23 | */ 24 | class SoapResponseListener 25 | { 26 | /** 27 | * @var SoapResponse 28 | */ 29 | protected $response; 30 | 31 | /** 32 | * Constructor. 33 | * 34 | * @param SoapResponse $response The SoapResponse instance 35 | */ 36 | public function __construct(SoapResponse $response) 37 | { 38 | $this->response = $response; 39 | } 40 | 41 | /** 42 | * Set the controller result in SoapResponse. 43 | * 44 | * @param GetResponseForControllerResultEvent $event A GetResponseForControllerResultEvent instance 45 | */ 46 | public function onKernelView(GetResponseForControllerResultEvent $event) 47 | { 48 | $request = $event->getRequest(); 49 | if (!$request instanceof SoapRequest) { 50 | return; 51 | } 52 | 53 | $this->response->setReturnValue($event->getControllerResult()); 54 | $event->setResponse($this->response); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Handler/ExceptionHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\Handler; 14 | 15 | use BeSimple\SoapServer\Exception\ReceiverSoapFault; 16 | use Symfony\Component\HttpFoundation\Response; 17 | use Symfony\Component\HttpKernel\Exception\FlattenException; 18 | 19 | /** 20 | * @author Francis Besset 21 | */ 22 | class ExceptionHandler 23 | { 24 | protected $exception; 25 | protected $details; 26 | protected $soapFault; 27 | 28 | public function __construct(FlattenException $exception, $details = null) 29 | { 30 | $this->exception = $exception; 31 | $this->details = $details; 32 | } 33 | 34 | public function setSoapFault(\SoapFault $soapFault) 35 | { 36 | $this->soapFault = $soapFault; 37 | } 38 | 39 | public function __call($method, $arguments) 40 | { 41 | if (isset($this->soapFault)) { 42 | throw $this->soapFault; 43 | } 44 | 45 | $code = $this->exception->getStatusCode(); 46 | 47 | throw new ReceiverSoapFault( 48 | isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', 49 | null, 50 | $this->details 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Handler/wsdl/exception.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BeSimpleSoapBundle 2 | 3 | The BeSimpleSoapBundle is a Symfony2 bundle to build WSDL and SOAP based web services. 4 | It is based on the [ckWebServicePlugin](http://www.symfony-project.org/plugins/ckWebServicePlugin) for symfony. 5 | 6 | Read about it on its [official homepage](http://besim.pl/SoapBundle/). 7 | -------------------------------------------------------------------------------- /Resources/config/client.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | BeSimple\SoapBundle\Soap\SoapClientBuilder 8 | BeSimple\SoapCommon\Classmap 9 | 10 | 11 | 12 | 13 | 14 | 15 | %kernel.debug% 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Resources/config/converters.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | BeSimple\SoapCommon\Converter\TypeConverterCollection 9 | BeSimple\SoapCommon\Converter\DateTimeTypeConverter 10 | BeSimple\SoapCommon\Converter\DateTypeConverter 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Resources/config/loaders.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Symfony\Component\Config\Loader\LoaderResolver 9 | Symfony\Component\Config\Loader\DelegatingLoader 10 | BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationDirectoryLoader 11 | BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationFileLoader 12 | BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationClassLoader 13 | BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Resources/config/request.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | BeSimple\SoapBundle\EventListener\RequestFormatListener 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Resources/config/routing/webservicecontroller.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | BeSimpleSoapBundle:SoapWebService:Call 9 | xml 10 | POST 11 | 12 | 13 | 14 | BeSimpleSoapBundle:SoapWebService:Definition 15 | xml 16 | GET 17 | 18 | 19 | -------------------------------------------------------------------------------- /Resources/config/soap.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | BeSimple\SoapBundle\Cache 8 | %kernel.cache_dir%/besimple/soap 9 | 10 | 11 | 12 | 13 | %kernel.debug% 14 | %besimple.soap.cache.type% 15 | %besimple.soap.cache.dir%/cache 16 | %besimple.soap.cache.lifetime% 17 | %besimple.soap.cache.limit% 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Resources/config/webservice.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | BeSimple\SoapBundle\Soap\SoapResponse 8 | BeSimple\SoapBundle\EventListener\SoapResponseListener 9 | BeSimple\SoapBundle\EventListener\SoapExceptionListener 10 | BeSimple\SoapBundle\WebServiceContext 11 | BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestHeaderMessageBinder 12 | BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder 13 | BeSimple\SoapBundle\ServiceBinding\RpcLiteralResponseMessageBinder 14 | BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedRequestMessageBinder 15 | BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedRequestHeaderMessageBinder 16 | BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedResponseMessageBinder 17 | BeSimple\SoapCommon\Definition\Type\TypeRepository 18 | BeSimple\SoapServer\Classmap 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | %besimple.soap.exception_listener.controller% 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | %besimple.soap.cache.dir% 42 | %kernel.debug% 43 | null 44 | %besimple.soap.binder.request_header.rpcliteral.class% 45 | %besimple.soap.binder.request.rpcliteral.class% 46 | %besimple.soap.binder.response.rpcliteral.class% 47 | %besimple.soap.definition.dumper.options.stylesheet% 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | %besimple.soap.cache.dir% 57 | %kernel.debug% 58 | null 59 | %besimple.soap.binder.request_header.documentwrapped.class% 60 | %besimple.soap.binder.request.documentwrapped.class% 61 | %besimple.soap.binder.response.documentwrapped.class% 62 | %besimple.soap.definition.dumper.options.stylesheet% 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | xsd 73 | http://www.w3.org/2001/XMLSchema 74 | 75 | 76 | string 77 | xsd:string 78 | 79 | 80 | boolean 81 | xsd:boolean 82 | 83 | 84 | int 85 | xsd:int 86 | 87 | 88 | float 89 | xsd:float 90 | 91 | 92 | date 93 | xsd:date 94 | 95 | 96 | dateTime 97 | xsd:dateTime 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Resources/doc/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeSimple/BeSimpleSoapBundle/5855f875cc682475aba2b723544bd3b3a21baa76/Resources/doc/_static/.gitkeep -------------------------------------------------------------------------------- /Resources/doc/cache.rst: -------------------------------------------------------------------------------- 1 | Cache 2 | ===== 3 | 4 | Configuration 5 | ------------- 6 | 7 | Configure the cache of PHP Soap WSDL in your config file: 8 | 9 | .. code-block:: yaml 10 | 11 | # app/config/config.yml 12 | be_simple_soap: 13 | cache: 14 | type: disk 15 | lifetime: 86400 16 | limit: 5 17 | 18 | The cache type can be: **none**, **disk** (default value), **memory**, **disk_memory**. 19 | 20 | The lifetime in seconds of a WSDL file in the cache (**86400 is the default value by PHP**). 21 | 22 | The limit is the maximum number of in-memory cached WSDL files (**5 is the default value by PHP**). 23 | 24 | The WSDL files cached are written in cache folder of your Symfony2 application. 25 | 26 | If you want more information you can visit the following page `PHP Soap runtime configuration `_. 27 | -------------------------------------------------------------------------------- /Resources/doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BeSimpleSoapBundle documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Aug 1 22:24:10 2011. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | from sphinx.highlighting import lexers 15 | from pygments.lexers.web import PhpLexer 16 | lexers['php'] = PhpLexer(startinline=True) 17 | 18 | import sys, os 19 | 20 | # If extensions (or modules to document with autodoc) are in another directory, 21 | # add these directories to sys.path here. If the directory is relative to the 22 | # documentation root, use os.path.abspath to make it absolute, like shown here. 23 | #sys.path.insert(0, os.path.abspath('.')) 24 | sys.path.append(os.path.abspath('_exts')) 25 | 26 | # -- General configuration ----------------------------------------------------- 27 | 28 | # If your documentation needs a minimal Sphinx version, state it here. 29 | #needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be extensions 32 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 33 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode'] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = ['_templates'] 37 | 38 | # The suffix of source filenames. 39 | source_suffix = '.rst' 40 | 41 | # The encoding of source files. 42 | #source_encoding = 'utf-8-sig' 43 | 44 | # The master toctree document. 45 | master_doc = 'index' 46 | 47 | # General information about the project. 48 | project = u'BeSimpleSoapBundle' 49 | copyright = u'2011, Christian Kerl, Francis Besset' 50 | 51 | # The version info for the project you're documenting, acts as replacement for 52 | # |version| and |release|, also used in various other places throughout the 53 | # built documents. 54 | # 55 | # The short X.Y version. 56 | version = '1.0.0' 57 | # The full version, including alpha/beta/rc tags. 58 | release = '1.0.0-DEV' 59 | 60 | # The language for content autogenerated by Sphinx. Refer to documentation 61 | # for a list of supported languages. 62 | #language = None 63 | 64 | # There are two options for replacing |today|: either, you set today to some 65 | # non-false value, then it is used: 66 | #today = '' 67 | # Else, today_fmt is used as the format for a strftime call. 68 | #today_fmt = '%B %d, %Y' 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | exclude_patterns = [] 73 | 74 | # The reST default role (used for this markup: `text`) to use for all documents. 75 | #default_role = None 76 | 77 | # If true, '()' will be appended to :func: etc. cross-reference text. 78 | #add_function_parentheses = True 79 | 80 | # If true, the current module name will be prepended to all description 81 | # unit titles (such as .. function::). 82 | #add_module_names = True 83 | 84 | # If true, sectionauthor and moduleauthor directives will be shown in the 85 | # output. They are ignored by default. 86 | #show_authors = False 87 | 88 | # The name of the Pygments (syntax highlighting) style to use. 89 | #pygments_style = 'sphinx' 90 | pygments_style = 'perldoc' 91 | 92 | # A list of ignored prefixes for module index sorting. 93 | #modindex_common_prefix = [] 94 | 95 | 96 | # -- Options for HTML output --------------------------------------------------- 97 | 98 | # The theme to use for HTML and HTML Help pages. See the documentation for 99 | # a list of builtin themes. 100 | html_theme = 'default' 101 | 102 | # Theme options are theme-specific and customize the look and feel of a theme 103 | # further. For a list of options available for each theme, see the 104 | # documentation. 105 | #html_theme_options = {} 106 | 107 | # Add any paths that contain custom themes here, relative to this directory. 108 | #html_theme_path = [] 109 | 110 | # The name for this set of Sphinx documents. If None, it defaults to 111 | # " v documentation". 112 | #html_title = None 113 | 114 | # A shorter title for the navigation bar. Default is the same as html_title. 115 | #html_short_title = None 116 | 117 | # The name of an image file (relative to this directory) to place at the top 118 | # of the sidebar. 119 | #html_logo = None 120 | 121 | # The name of an image file (within the static path) to use as favicon of the 122 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 123 | # pixels large. 124 | #html_favicon = None 125 | 126 | # Add any paths that contain custom static files (such as style sheets) here, 127 | # relative to this directory. They are copied after the builtin static files, 128 | # so a file named "default.css" will overwrite the builtin "default.css". 129 | html_static_path = ['_static'] 130 | 131 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 132 | # using the given strftime format. 133 | #html_last_updated_fmt = '%b %d, %Y' 134 | 135 | # If true, SmartyPants will be used to convert quotes and dashes to 136 | # typographically correct entities. 137 | #html_use_smartypants = True 138 | 139 | # Custom sidebar templates, maps document names to template names. 140 | #html_sidebars = {} 141 | 142 | # Additional templates that should be rendered to pages, maps page names to 143 | # template names. 144 | #html_additional_pages = {} 145 | 146 | # If false, no module index is generated. 147 | #html_domain_indices = True 148 | 149 | # If false, no index is generated. 150 | #html_use_index = True 151 | 152 | # If true, the index is split into individual pages for each letter. 153 | #html_split_index = False 154 | 155 | # If true, links to the reST sources are added to the pages. 156 | #html_show_sourcelink = True 157 | 158 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 159 | #html_show_sphinx = True 160 | 161 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 162 | #html_show_copyright = True 163 | 164 | # If true, an OpenSearch description file will be output, and all pages will 165 | # contain a tag referring to it. The value of this option must be the 166 | # base URL from which the finished HTML is served. 167 | #html_use_opensearch = '' 168 | 169 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 170 | #html_file_suffix = None 171 | 172 | # Output file base name for HTML help builder. 173 | htmlhelp_basename = 'BeSimpleSoapBundledoc' 174 | 175 | 176 | # -- Options for LaTeX output -------------------------------------------------- 177 | 178 | # The paper size ('letter' or 'a4'). 179 | #latex_paper_size = 'letter' 180 | 181 | # The font size ('10pt', '11pt' or '12pt'). 182 | #latex_font_size = '10pt' 183 | 184 | # Grouping the document tree into LaTeX files. List of tuples 185 | # (source start file, target name, title, author, documentclass [howto/manual]). 186 | latex_documents = [ 187 | ('index', 'BeSimpleSoapBundle.tex', u'BeSimpleSoapBundle Documentation', 188 | u'Christian Kerl, Francis Besset', 'manual'), 189 | ] 190 | 191 | # The name of an image file (relative to this directory) to place at the top of 192 | # the title page. 193 | #latex_logo = None 194 | 195 | # For "manual" documents, if this is true, then toplevel headings are parts, 196 | # not chapters. 197 | #latex_use_parts = False 198 | 199 | # If true, show page references after internal links. 200 | #latex_show_pagerefs = False 201 | 202 | # If true, show URL addresses after external links. 203 | #latex_show_urls = False 204 | 205 | # Additional stuff for the LaTeX preamble. 206 | #latex_preamble = '' 207 | 208 | # Documents to append as an appendix to all manuals. 209 | #latex_appendices = [] 210 | 211 | # If false, no module index is generated. 212 | #latex_domain_indices = True 213 | 214 | 215 | # -- Options for manual page output -------------------------------------------- 216 | 217 | # One entry per manual page. List of tuples 218 | # (source start file, name, description, authors, manual section). 219 | man_pages = [ 220 | ('index', 'besimplesoapbundle', u'BeSimpleSoapBundle Documentation', 221 | [u'Christian Kerl, Francis Besset'], 1) 222 | ] 223 | 224 | 225 | # Example configuration for intersphinx: refer to the Python standard library. 226 | intersphinx_mapping = {'http://docs.python.org/': None} 227 | -------------------------------------------------------------------------------- /Resources/doc/index.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | BeSimpleSoapBundle 3 | ================== 4 | 5 | The BeSimpleSoapBundle is a Symfony2 bundle to build WSDL and SOAP based web services. 6 | It is based on the `ckWebServicePlugin `_ for symfony. 7 | 8 | --------------- 9 | Reference Guide 10 | --------------- 11 | 12 | .. toctree:: 13 | :maxdepth: 1 14 | :numbered: 15 | 16 | requirements 17 | installation 18 | cache 19 | 20 | ---------- 21 | SoapServer 22 | ---------- 23 | 24 | .. toctree:: 25 | :maxdepth: 1 26 | :numbered: 27 | 28 | soapserver/configuration 29 | soapserver/types 30 | soapserver/tutorials 31 | 32 | ---------- 33 | SoapClient 34 | ---------- 35 | 36 | .. toctree:: 37 | :maxdepth: 1 38 | :numbered: 39 | 40 | soapclient/configuration 41 | -------------------------------------------------------------------------------- /Resources/doc/installation.rst: -------------------------------------------------------------------------------- 1 | Installation with Composer 2 | ========================== 3 | 4 | Add `besimple/soap-bundle `_ (with vendors) in your composer.json: 5 | 6 | .. code-block:: json 7 | 8 | { 9 | "require": { 10 | "besimple/soap": "0.1.*@dev, 11 | "ass/xmlsecurity": "dev-master" 12 | } 13 | } 14 | 15 | Update vendors: 16 | 17 | .. code-block:: bash 18 | 19 | $ php composer.phar self-update 20 | $ php composer.phar update 21 | 22 | Enable the BeSimpleSoapBundle your application Kernel class: 23 | 24 | .. code-block:: php 25 | 26 | // app/AppKernel.php 27 | public function registerBundles() 28 | { 29 | return array( 30 | // ... 31 | new BeSimple\SoapBundle\BeSimpleSoapBundle(), 32 | // ... 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /Resources/doc/requirements.rst: -------------------------------------------------------------------------------- 1 | Requirements 2 | ============ 3 | 4 | Install and enable PHP's `SOAP extension ` and `cURL extension `. 5 | -------------------------------------------------------------------------------- /Resources/doc/soapclient/configuration.rst: -------------------------------------------------------------------------------- 1 | Configuration 2 | ============= 3 | 4 | Client configuration 5 | -------------------- 6 | 7 | Configure your first client in your config file: 8 | 9 | .. code-block:: yaml 10 | 11 | # app/config/config.yml 12 | be_simple_soap: 13 | clients: 14 | DemoApi: 15 | # required 16 | wsdl: http://localhost/app_dev.php/ws/DemoApi?wsdl 17 | 18 | # classmap (optional) 19 | classmap: 20 | type_name: "Full\Class\Name" 21 | 22 | # proxy (optional) 23 | proxy: 24 | host: proxy.domain.name # required to enable proxy configuration 25 | port: 3128 26 | login: ~ 27 | password: ~ 28 | auth: ~ # can be 'basic' or 'ntlm' 29 | 30 | Using client 31 | ------------ 32 | 33 | .. code-block:: php 34 | 35 | namespace Acme\DemoBundle\Controller; 36 | 37 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; 38 | 39 | class DemoController extends Controller 40 | { 41 | public function helloAction($name) 42 | { 43 | // The client service name is `besimple.soap.client.demoapi`: 44 | // `besimple.soap.client.`: is the base name of your client 45 | // `demoapi`: is the name specified in your config file converted to lowercase 46 | $client = $this->container->get('besimple.soap.client.demoapi'); 47 | 48 | // call `hello` method on WebService with the string parameter `$name` 49 | $helloResult = $client->hello($name); 50 | 51 | return $this->render('AcmeDemoBundle:Demo:hello.html.twig', array( 52 | 'hello' => $helloResult, 53 | )); 54 | } 55 | } 56 | 57 | Classmap 58 | -------- 59 | 60 | Configuration 61 | ~~~~~~~~~~~~~ 62 | 63 | .. code-block:: yaml 64 | 65 | # app/config/config.yml 66 | be_simple_soap: 67 | clients: 68 | DemoApi: 69 | # ... 70 | classmap: 71 | User: Acme\DemoBundle\Api\UserApi 72 | # add other type_name: classname 73 | 74 | UserApi class 75 | ~~~~~~~~~~~~~ 76 | 77 | .. code-block:: php 78 | 79 | namespace Acme\DemoBundle\Api; 80 | 81 | class UserApi 82 | { 83 | private $username; 84 | 85 | private $firstname; 86 | 87 | private $lastname; 88 | 89 | public function __construct($username) 90 | { 91 | $this->username = $username; 92 | } 93 | 94 | public function getFirstname() 95 | { 96 | return $this->firstname; 97 | } 98 | 99 | public function getLastname() 100 | { 101 | return $this->lastname; 102 | } 103 | } 104 | 105 | Usage 106 | ~~~~~ 107 | 108 | .. code-block:: php 109 | 110 | namespace Acme\DemoBundle\Controller; 111 | 112 | use Acme\DemoBundle\Api\UserApi; 113 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; 114 | 115 | class DemoController extends Controller 116 | { 117 | public function userAction($username) 118 | { 119 | // The client service name is `besimple.soap.client.demoapi`: 120 | // `besimple.soap.client.`: is the base name of your client 121 | // `demoapi`: is the name specified in your config file converted to lowercase 122 | $client = $this->container->get('besimple.soap.client.demoapi'); 123 | 124 | // call `getUser` method on WebService with an instance of UserApi 125 | // if the `getUserByUsername` method return a `User` type then `$userResult` is an instance of UserApi 126 | $userResult = $client->getUserByUsername($username); 127 | 128 | return $this->render('AcmeDemoBundle:Demo:user.html.twig', array( 129 | 'user' => $userResult, 130 | )); 131 | } 132 | } 133 | 134 | Without classmap configuration the `$userResult` is an instance of `stdClass`: 135 | 136 | .. code-block:: text 137 | 138 | object(stdClass)#5561 (3) { 139 | ["username"]=> 140 | string(6) "FooBar" 141 | ["firstname"]=> 142 | string(3) "Foo" 143 | ["lastname"]=> 144 | string(3) "Bar" 145 | } 146 | 147 | With classmap configuration the `$userResult` is an instance of `Acme\DemoBundle\Api\UserApi`: 148 | 149 | .. code-block:: text 150 | 151 | object(Acme\DemoBundle\Api\UserApi)#208 (3) { 152 | ["username":"Acme\DemoBundle\Api\UserApi":private]=> 153 | string(6) "FooBar" 154 | ["firstname":"Acme\DemoBundle\Api\UserApi":private]=> 155 | string(3) "Foo" 156 | ["lastname":"Acme\DemoBundle\Api\UserApi":private]=> 157 | string(3) "Bar" 158 | } 159 | -------------------------------------------------------------------------------- /Resources/doc/soapserver/configuration.rst: -------------------------------------------------------------------------------- 1 | Configuration 2 | ============= 3 | 4 | Routing 5 | ------- 6 | 7 | Include the `BeSimpleSoapBundle`'s routing configuration in your routing file (you can choose the prefix arbitrarily): 8 | 9 | .. code-block:: yaml 10 | 11 | # app/config/routing.yml 12 | _besimple_soap: 13 | resource: "@BeSimpleSoapBundle/Resources/config/routing/webservicecontroller.xml" 14 | prefix: /ws 15 | 16 | Config 17 | ------ 18 | 19 | Configure your first web service in your config file: 20 | 21 | .. code-block:: yaml 22 | 23 | # app/config/config.yml 24 | be_simple_soap: 25 | services: 26 | DemoApi: 27 | namespace: http://mysymfonyapp.com/ws/DemoApi/1.0/ 28 | binding: rpc-literal 29 | resource: "@AcmeDemoBundle/Controller/DemoController.php" 30 | resource_type: annotation 31 | 32 | Annotations for Controllers 33 | --------------------------- 34 | 35 | .. code-block:: php 36 | 37 | namespace Acme\DemoBundle\Controller; 38 | 39 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 40 | use Symfony\Component\DependencyInjection\ContainerAware; 41 | 42 | class DemoController extends ContainerAware 43 | { 44 | /** 45 | * @Soap\Method("hello") 46 | * @Soap\Param("name", phpType = "string") 47 | * @Soap\Result(phpType = "string") 48 | */ 49 | public function helloAction($name) 50 | { 51 | return sprintf('Hello %s!', $name); 52 | } 53 | 54 | /** 55 | * @Soap\Method("goodbye") 56 | * @Soap\Param("name", phpType = "string") 57 | * @Soap\Result(phpType = "string") 58 | */ 59 | public function goodbyeAction($name) 60 | { 61 | return $this->container->get('besimple.soap.response')->setReturnValue(sprintf('Goodbye %s!', $name)); 62 | } 63 | } 64 | 65 | Get your WSDL 66 | ------------- 67 | 68 | To access your WSDL go to the following address: http://localhost/app_dev.php/ws/DemoApi?wsdl 69 | 70 | To read the WSDL in your browser you can call this address (without `wsdl` parameter): http://localhost/app_dev.php/ws/DemoApi 71 | -------------------------------------------------------------------------------- /Resources/doc/soapserver/tutorial/array.rst: -------------------------------------------------------------------------------- 1 | Array 2 | ===== 3 | 4 | Controller 5 | ---------- 6 | 7 | .. code-block:: php 8 | 9 | namespace Acme\DemoBundle\Controller; 10 | 11 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 12 | use Symfony\Component\DependencyInjection\ContainerAware; 13 | 14 | class DemoController extends ContainerAware 15 | { 16 | /** 17 | * @Soap\Method("hello") 18 | * @Soap\Param("names", phpType = "string[]") 19 | * @Soap\Result(phpType = "string") 20 | */ 21 | public function helloAction(array $names) 22 | { 23 | return "Hello ".implode(', ', $names); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Resources/doc/soapserver/tutorial/associative_array.rst: -------------------------------------------------------------------------------- 1 | Associative Array 2 | ================= 3 | 4 | Pre-existent Type 5 | ----------------- 6 | 7 | +------------------------------------------------+-----------------+ 8 | | Php Type | Value Type | 9 | +================================================+=================+ 10 | | BeSimple\\SoapCommon\\Type\\KeyValue\\String | String | 11 | +------------------------------------------------+-----------------+ 12 | | BeSimple\\SoapCommon\\Type\\KeyValue\\Boolean | Boolean | 13 | +------------------------------------------------+-----------------+ 14 | | BeSimple\\SoapCommon\\Type\\KeyValue\\Int | Int | 15 | +------------------------------------------------+-----------------+ 16 | | BeSimple\\SoapCommon\\Type\\KeyValue\\Float | Float | 17 | +------------------------------------------------+-----------------+ 18 | | BeSimple\\SoapCommon\\Type\\KeyValue\\Date | DateTime object | 19 | +------------------------------------------------+-----------------+ 20 | | BeSimple\\SoapCommon\\Type\\KeyValue\\DateTime | DateTime object | 21 | +------------------------------------------------+-----------------+ 22 | 23 | Controller 24 | ---------- 25 | 26 | .. code-block:: php 27 | 28 | namespace Acme\DemoBundle\Controller; 29 | 30 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 31 | use Symfony\Component\DependencyInjection\ContainerAware; 32 | 33 | class DemoController extends ContainerAware 34 | { 35 | /** 36 | * @Soap\Method("returnAssocArray") 37 | * @Soap\Result(phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]") 38 | */ 39 | public function assocArrayOfStringAction() 40 | { 41 | return array( 42 | 'foo' => 'bar', 43 | 'bar' => 'foo', 44 | ); 45 | } 46 | 47 | /** 48 | * @Soap\Method("sendAssocArray") 49 | * @Soap\Param("assocArray", phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]") 50 | * @Soap\Result(phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]") 51 | */ 52 | public function sendAssocArrayOfStringAction(array $assocArray) 53 | { 54 | // The $assocArray it's a real associative array 55 | // var_dump($assocArray);die; 56 | 57 | return $assocArray; 58 | } 59 | } 60 | 61 | How to create my Associative Array? 62 | ----------------------------------- 63 | 64 | .. code-block:: php 65 | 66 | namespace Acme\DemoBundle\Soap\Type; 67 | 68 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 69 | use BeSimple\SoapCommon\Type\AbstractKeyValue; 70 | 71 | class User extends AbstractKeyValue 72 | { 73 | /** 74 | * @Soap\ComplexType("Acme\DemoBundle\Entity\User") 75 | */ 76 | protected $value; 77 | } 78 | 79 | .. code-block:: php 80 | 81 | namespace Acme\DemoBundle\Controller; 82 | 83 | use Acme\DemoBundle\Entity\User; 84 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 85 | use Symfony\Component\DependencyInjection\ContainerAware; 86 | 87 | class DemoController extends ContainerAware 88 | { 89 | /** 90 | * @Soap\Method("getUsers") 91 | * @Soap\Result(phpType = "Acme\DemoBundle\Soap\Type\User[]") 92 | */ 93 | public function getUsers() 94 | { 95 | return array( 96 | 'user1' => new User('user1', 'user1@user.com'), 97 | 'user2' => new User('user2', 'user2@user.com'), 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Resources/doc/soapserver/tutorial/complex_type.rst: -------------------------------------------------------------------------------- 1 | Complex Type 2 | ============ 3 | 4 | This tutorial explains how to do to return a complex type. 5 | 6 | If your SOAP function takes a complex type as input, this tutorial is 7 | valid. You'll just have to adapt the input parameters of your method. 8 | 9 | 10 | Controller 11 | ---------- 12 | 13 | .. code-block:: php 14 | 15 | namespace Acme\DemoBundle\Controller; 16 | 17 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 18 | use Symfony\Component\DependencyInjection\ContainerAware; 19 | 20 | class DemoController extends ContainerAware 21 | { 22 | /** 23 | * @Soap\Method("getUser") 24 | * @Soap\Param("name", phpType = "string") 25 | * @Soap\Result(phpType = "Acme\DemoBundle\Entity\User") 26 | */ 27 | public function getUserAction($name) 28 | { 29 | $user = $this->container->getDoctrine()->getRepository('MyApp:User')->findOneBy(array( 30 | 'name' => $name, 31 | )); 32 | 33 | if (!$user) { 34 | throw new \SoapFault('USER_NOT_FOUND', sprintf('The user with the name "%s" can not be found', $name)); 35 | } 36 | 37 | return $user; 38 | } 39 | } 40 | 41 | User class 42 | ---------- 43 | 44 | You can expose only the properties (public, protected or private) of a complex type. 45 | 46 | **For performance reasons, we advise to create getter and setter for each property.** 47 | 48 | .. code-block:: php 49 | 50 | namespace Acme\DemoBundle\Entity; 51 | 52 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 53 | 54 | /** 55 | * @Soap\Alias("User") 56 | */ 57 | class User 58 | { 59 | /** 60 | * @Soap\ComplexType("int", nillable=true) 61 | */ 62 | private $id; 63 | 64 | /** 65 | * @Soap\ComplexType("string") 66 | */ 67 | public $firstname; 68 | 69 | /** 70 | * @Soap\ComplexType("string") 71 | */ 72 | public $lastname; 73 | 74 | /** 75 | * @Soap\ComplexType("string") 76 | */ 77 | private $username; 78 | 79 | /** 80 | * @Soap\ComplexType("string") 81 | */ 82 | private $email; 83 | 84 | /** 85 | * @Soap\ComplexType("boolean") 86 | */ 87 | private $newsletter; 88 | 89 | /** 90 | * @Soap\ComplexType("date") 91 | */ 92 | private $createdAt: 93 | 94 | /** 95 | * @Soap\ComplexType("datetime") 96 | */ 97 | private $updatedAt; 98 | 99 | public function getId() 100 | { 101 | return $this->id; 102 | } 103 | 104 | public function getUsername() 105 | { 106 | return $this->username; 107 | } 108 | 109 | public function getEmail() 110 | { 111 | return $this->email; 112 | } 113 | 114 | public function getFirstname() 115 | { 116 | return $this->firstname; 117 | } 118 | 119 | public function setFirstname($firstname) 120 | { 121 | $this->firstname = $firstname; 122 | } 123 | 124 | public function getLastname() 125 | { 126 | return $this->lastname; 127 | } 128 | 129 | public function setLastname($lastname) 130 | { 131 | $this->lastname = $lastname; 132 | } 133 | 134 | public function hasNewsletter() 135 | { 136 | return $this->newsletter; 137 | } 138 | 139 | public function setNewsletter($newsletter) 140 | { 141 | $this->newletter = (Boolean) $newsletter; 142 | } 143 | 144 | public function getCreatedAt() 145 | { 146 | return $this->createdAt; 147 | } 148 | 149 | public function setCreatedAt(\DateTime $createdAt) 150 | { 151 | $this->createdAt = $createdAt; 152 | } 153 | 154 | public function getUpdatedAt() 155 | { 156 | return this->updatedAt; 157 | } 158 | 159 | public function setUpdatedAt(\DateTime $updatedAt) 160 | { 161 | $this->updatedAt = $updatedAt; 162 | } 163 | } 164 | 165 | ComplexType 166 | ----------- 167 | 168 | `ComplexType` accepts the following options: 169 | 170 | * nillable: To specify that the value can be null 171 | 172 | Alias 173 | ----- 174 | 175 | If you can Alias annotation, the name of your entity will be renamed in the WSDL generated. 176 | With alias the name in WSDL will `User` instead of `Acme.DemoBundle.Entity.User` (name without Alias annotation). 177 | -------------------------------------------------------------------------------- /Resources/doc/soapserver/tutorial/header.rst: -------------------------------------------------------------------------------- 1 | Header 2 | ====== 3 | 4 | Controller 5 | ---------- 6 | 7 | .. code-block:: php 8 | 9 | namespace Acme\DemoBundle\Controller; 10 | 11 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 12 | use Symfony\Component\DependencyInjection\ContainerAware; 13 | 14 | class DemoController extends ContainerAware 15 | { 16 | /** 17 | * @Soap\Method("hello") 18 | * @Soap\Header("api_key", phpType = "string") 19 | * @Soap\Param("names", phpType = "string[]") 20 | * @Soap\Result(phpType = "string") 21 | */ 22 | public function helloAction(array $names) 23 | { 24 | $soapHeaders = $this->container->get('request')->getSoapHeaders(); 25 | 26 | // You can use '1234' !== (string) $soapHeaders->get('api_key') 27 | if (!$soapHeaders->has('api_key') || '1234' !== $soapHeaders->get('api_key')->getData()) { 28 | throw new \SoapFault("INVALID_API_KEY", "The api_key is invalid."); 29 | } 30 | 31 | return "Hello ".implode(', ', $names); 32 | } 33 | } 34 | 35 | Global header 36 | ------------- 37 | 38 | If you want use a header for all actions of your controller you can declare the header like this: 39 | 40 | .. code-block:: php 41 | 42 | namespace Acme\DemoBundle\Controller; 43 | 44 | use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap; 45 | use Symfony\Component\DependencyInjection\ContainerAware; 46 | use Symfony\Component\DependencyInjection\ContainerInterface; 47 | 48 | /** 49 | * @Soap\Header("api_key", phpType = "string") 50 | */ 51 | class DemoController extends ContainerAware 52 | { 53 | /** 54 | * @Soap\Method("hello") 55 | * @Soap\Param("names", phpType = "string[]") 56 | * @Soap\Result(phpType = "string") 57 | */ 58 | public function helloAction(array $names) 59 | { 60 | return "Hello ".implode(', ', $names); 61 | } 62 | 63 | /** 64 | * @Soap\Method("welcome") 65 | * @Soap\Param("names", phpType = "string[]") 66 | * @Soap\Result(phpType = "string") 67 | */ 68 | public function welcomeAction($names) 69 | { 70 | return "Welcome ".implode(', ', $names); 71 | } 72 | 73 | public function setContainer(ContainerInterface $container = null) 74 | { 75 | parent::setContainer($container); 76 | 77 | $this->checkApiKeyHeader(); 78 | } 79 | 80 | private function checkApiKeyHeader() 81 | { 82 | $soapHeaders = $this->container->get('request')->getSoapHeaders(); 83 | 84 | // You can use '1234' !== (string) $soapHeaders->get('api_key') 85 | if (!$soapHeaders->has('api_key') || '1234' !== $soapHeaders->get('api_key')->getData()) { 86 | throw new \SoapFault("INVALID_API_KEY", "The api_key is invalid."); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Resources/doc/soapserver/tutorials.rst: -------------------------------------------------------------------------------- 1 | Tutorials 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | tutorial/array 8 | tutorial/associative_array 9 | tutorial/complex_type 10 | tutorial/header 11 | -------------------------------------------------------------------------------- /Resources/doc/soapserver/types.rst: -------------------------------------------------------------------------------- 1 | Types available 2 | =============== 3 | 4 | +----------+-----------------+ 5 | | Php Type | XML Type | 6 | +==========+=================+ 7 | | string | `xsd:string`_ | 8 | +----------+-----------------+ 9 | | boolean | `xsd:boolean`_ | 10 | +----------+-----------------+ 11 | | int | `xsd:int`_ | 12 | +----------+-----------------+ 13 | | float | `xsd:float`_ | 14 | +----------+-----------------+ 15 | | date | `xsd:date`_ | 16 | +----------+-----------------+ 17 | | dateTime | `xsd:dateTime`_ | 18 | +----------+-----------------+ 19 | 20 | .. _`xsd:string`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#string 21 | .. _`xsd:boolean`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#boolean 22 | .. _`xsd:int`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#int 23 | .. _`xsd:float`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#float 24 | .. _`xsd:date`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#date 25 | .. _`xsd:dateTime`: http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#dateTime -------------------------------------------------------------------------------- /Resources/meta/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2013 Christian Kerl, Francis Besset 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceBinding; 12 | 13 | use BeSimple\SoapBundle\ServiceDefinition\Method; 14 | 15 | /** 16 | * @author Christian Kerl 17 | */ 18 | class DocumentLiteralWrappedRequestMessageBinder implements MessageBinderInterface 19 | { 20 | public function processMessage(Method $messageDefinition, $message) 21 | { 22 | if(count($message) > 1) { 23 | throw new \InvalidArgumentException(); 24 | } 25 | 26 | $result = array(); 27 | $message = $message[0]; 28 | 29 | foreach($messageDefinition->getArguments() as $argument) { 30 | $result[$argument->getName()] = $message->{$argument->getName()}; 31 | } 32 | 33 | return $result; 34 | } 35 | } -------------------------------------------------------------------------------- /ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceBinding; 12 | 13 | use BeSimple\SoapBundle\ServiceDefinition\Method; 14 | 15 | /** 16 | * @author Christian Kerl 17 | */ 18 | class DocumentLiteralWrappedResponseMessageBinder implements MessageBinderInterface 19 | { 20 | public function processMessage(Method $messageDefinition, $message) 21 | { 22 | $result = new \stdClass(); 23 | $result->{$messageDefinition->getName().'Result'} = $message; 24 | 25 | return $result; 26 | } 27 | } -------------------------------------------------------------------------------- /ServiceBinding/MessageBinderInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceBinding; 12 | 13 | use BeSimple\SoapBundle\ServiceDefinition\Method; 14 | use BeSimple\SoapCommon\Definition\Type\TypeRepository; 15 | 16 | /** 17 | * @author Christian Kerl 18 | */ 19 | interface MessageBinderInterface 20 | { 21 | /** 22 | * @param Method $messageDefinition 23 | * @param mixed $message 24 | * 25 | * @return mixed 26 | */ 27 | function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository); 28 | } 29 | -------------------------------------------------------------------------------- /ServiceBinding/RpcLiteralRequestHeaderMessageBinder.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceBinding; 12 | 13 | use BeSimple\SoapBundle\ServiceDefinition\Method; 14 | use BeSimple\SoapCommon\Definition\Type\TypeRepository; 15 | 16 | /** 17 | * @author Francis Besset 18 | */ 19 | class RpcLiteralRequestHeaderMessageBinder extends RpcLiteralRequestMessageBinder 20 | { 21 | private $header; 22 | 23 | public function setHeader($header) 24 | { 25 | $this->header = $header; 26 | } 27 | 28 | public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository) 29 | { 30 | $this->typeRepository = $typeRepository; 31 | $headerDefinition = $messageDefinition->getHeaders()->get($this->header); 32 | 33 | return $this->processType($headerDefinition->getType(), $message); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ServiceBinding/RpcLiteralRequestMessageBinder.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceBinding; 14 | 15 | use BeSimple\SoapBundle\ServiceDefinition\Method; 16 | use BeSimple\SoapCommon\Definition\Type\ArrayOfType; 17 | use BeSimple\SoapCommon\Definition\Type\ComplexType; 18 | use BeSimple\SoapCommon\Definition\Type\TypeRepository; 19 | use BeSimple\SoapCommon\Util\MessageBinder; 20 | 21 | /** 22 | * @author Christian Kerl 23 | * @author Francis Besset 24 | */ 25 | class RpcLiteralRequestMessageBinder implements MessageBinderInterface 26 | { 27 | protected $typeRepository; 28 | 29 | private $messageRefs = array(); 30 | 31 | public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository) 32 | { 33 | $this->typeRepository = $typeRepository; 34 | 35 | $result = array(); 36 | $i = 0; 37 | 38 | foreach ($messageDefinition->getInput()->all() as $argument) { 39 | if (isset($message[$i])) { 40 | $result[$argument->getName()] = $this->processType($argument->getType(), $message[$i]); 41 | } 42 | 43 | $i++; 44 | } 45 | 46 | return $result; 47 | } 48 | 49 | protected function processType($phpType, $message) 50 | { 51 | $isArray = false; 52 | 53 | $type = $this->typeRepository->getType($phpType); 54 | if ($type instanceof ArrayOfType) { 55 | $isArray = true; 56 | $array = array(); 57 | 58 | $type = $this->typeRepository->getType($type->get('item')->getType()); 59 | } 60 | 61 | // @TODO Fix array reference 62 | if ($type instanceof ComplexType) { 63 | $phpType = $type->getPhpType(); 64 | 65 | if ($isArray) { 66 | if (isset($message->item)) { 67 | foreach ($message->item as $complexType) { 68 | $array[] = $this->checkComplexType($phpType, $complexType); 69 | } 70 | 71 | // See https://github.com/BeSimple/BeSimpleSoapBundle/issues/29 72 | if (in_array('BeSimple\SoapCommon\Type\AbstractKeyValue', class_parents($phpType))) { 73 | $assocArray = array(); 74 | foreach ($array as $keyValue) { 75 | $assocArray[$keyValue->getKey()] = $keyValue->getValue(); 76 | } 77 | 78 | $array = $assocArray; 79 | } 80 | } 81 | 82 | $message = $array; 83 | } else { 84 | $message = $this->checkComplexType($phpType, $message); 85 | } 86 | } elseif ($isArray) { 87 | if (isset($message->item)) { 88 | $message = $message->item; 89 | } else { 90 | $message = $array; 91 | } 92 | } 93 | 94 | return $message; 95 | } 96 | 97 | protected function checkComplexType($phpType, $message) 98 | { 99 | $hash = spl_object_hash($message); 100 | if (isset($this->messageRefs[$hash])) { 101 | return $this->messageRefs[$hash]; 102 | } 103 | 104 | $this->messageRefs[$hash] = $message; 105 | 106 | $messageBinder = new MessageBinder($message); 107 | foreach ($this->typeRepository->getType($phpType)->all() as $type) { 108 | $property = $type->getName(); 109 | $value = $messageBinder->readProperty($property); 110 | 111 | if (null !== $value) { 112 | $value = $this->processType($type->getType(), $value); 113 | 114 | $messageBinder->writeProperty($property, $value); 115 | } elseif (!$type->isNillable()) { 116 | // @TODO use xmlType instead of phpType 117 | throw new \SoapFault('SOAP_ERROR_COMPLEX_TYPE', sprintf('"%s:%s" cannot be null.', ucfirst($phpType), $type->getName())); 118 | } 119 | } 120 | 121 | return $message; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ServiceBinding/RpcLiteralResponseMessageBinder.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceBinding; 14 | 15 | use BeSimple\SoapBundle\ServiceDefinition\Method; 16 | use BeSimple\SoapCommon\Definition\Type\ArrayOfType; 17 | use BeSimple\SoapCommon\Definition\Type\ComplexType; 18 | use BeSimple\SoapCommon\Definition\Type\TypeRepository; 19 | use BeSimple\SoapCommon\Util\MessageBinder; 20 | 21 | /** 22 | * @author Christian Kerl 23 | * @author Francis Besset 24 | */ 25 | class RpcLiteralResponseMessageBinder implements MessageBinderInterface 26 | { 27 | protected $typeRepository; 28 | 29 | private $messageRefs = array(); 30 | 31 | public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository) 32 | { 33 | $this->typeRepository = $typeRepository; 34 | 35 | return $this->processType($messageDefinition->getOutput()->get('return')->getType(), $message); 36 | } 37 | 38 | private function processType($phpType, $message) 39 | { 40 | $isArray = false; 41 | 42 | $type = $this->typeRepository->getType($phpType); 43 | if ($type instanceof ArrayOfType) { 44 | $isArray = true; 45 | 46 | $type = $this->typeRepository->getType($type->get('item')->getType()); 47 | } 48 | 49 | if ($type instanceof ComplexType) { 50 | $phpType = $type->getPhpType(); 51 | 52 | if ($isArray) { 53 | $array = array(); 54 | 55 | // See https://github.com/BeSimple/BeSimpleSoapBundle/issues/29 56 | if (is_array($message) && in_array('BeSimple\SoapCommon\Type\AbstractKeyValue', class_parents($phpType))) { 57 | $keyValue = array(); 58 | foreach ($message as $key => $value) { 59 | $keyValue[] = new $phpType($key, $value); 60 | } 61 | 62 | $message = $keyValue; 63 | } 64 | 65 | foreach ($message as $complexType) { 66 | $array[] = $this->checkComplexType($phpType, $complexType); 67 | } 68 | 69 | $message = $array; 70 | } else { 71 | $message = $this->checkComplexType($phpType, $message); 72 | } 73 | } 74 | 75 | return $message; 76 | } 77 | 78 | private function checkComplexType($phpType, $message) 79 | { 80 | $hash = spl_object_hash($message); 81 | if (isset($this->messageRefs[$hash])) { 82 | return $this->messageRefs[$hash]; 83 | } 84 | 85 | $this->messageRefs[$hash] = $message; 86 | 87 | if (!$message instanceof $phpType) { 88 | throw new \InvalidArgumentException(sprintf('The instance class must be "%s", "%s" given.', $phpType, get_class($message))); 89 | } 90 | 91 | $messageBinder = new MessageBinder($message); 92 | foreach ($this->typeRepository->getType($phpType)->all() as $type) { 93 | $property = $type->getName(); 94 | $value = $messageBinder->readProperty($property); 95 | 96 | if (null !== $value) { 97 | $value = $this->processType($type->getType(), $value); 98 | 99 | $messageBinder->writeProperty($property, $value); 100 | } 101 | 102 | if (!$type->isNillable() && null === $value) { 103 | throw new \InvalidArgumentException(sprintf('"%s::%s" cannot be null.', $phpType, $type->getName())); 104 | } 105 | } 106 | 107 | return $message; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ServiceBinding/ServiceBinder.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceBinding; 12 | 13 | use BeSimple\SoapBundle\ServiceDefinition\Definition; 14 | use BeSimple\SoapBundle\Soap\SoapHeader; 15 | 16 | /** 17 | * @author Christian Kerl 18 | */ 19 | class ServiceBinder 20 | { 21 | /** 22 | * @var \BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition 23 | */ 24 | private $definition; 25 | 26 | /** 27 | * @var \BeSimple\SoapBundle\ServiceBinding\MessageBinderInterface 28 | */ 29 | private $requestHeaderMessageBinder; 30 | 31 | /** 32 | * @var \BeSimple\SoapBundle\ServiceBinding\MessageBinderInterface 33 | */ 34 | private $requestMessageBinder; 35 | 36 | /** 37 | * @var \BeSimple\SoapBundle\ServiceBinding\MessageBinderInterface 38 | */ 39 | private $responseMessageBinder; 40 | 41 | /** 42 | * @param Definition $definition 43 | * @param MessageBinderInterface $requestHeaderMessageBinder 44 | * @param MessageBinderInterface $requestMessageBinder 45 | * @param MessageBinderInterface $responseMessageBinder 46 | */ 47 | public function __construct(Definition $definition, MessageBinderInterface $requestHeaderMessageBinder, MessageBinderInterface $requestMessageBinder, MessageBinderInterface $responseMessageBinder) { 48 | $this->definition = $definition; 49 | 50 | $this->requestHeaderMessageBinder = $requestHeaderMessageBinder; 51 | $this->requestMessageBinder = $requestMessageBinder; 52 | 53 | $this->responseMessageBinder = $responseMessageBinder; 54 | } 55 | 56 | /** 57 | * @param string $method 58 | * @param string $header 59 | * 60 | * @return boolean 61 | */ 62 | public function isServiceHeader($method, $header) 63 | { 64 | return $this->definition->getMethod($method)->getHeader($header); 65 | } 66 | 67 | /** 68 | * @param $string 69 | * 70 | * @return boolean 71 | */ 72 | public function isServiceMethod($method) 73 | { 74 | return null !== $this->definition->getMethod($method); 75 | } 76 | 77 | /** 78 | * @param string $method 79 | * @param string $header 80 | * @param mixed $data 81 | * 82 | * @return SoapHeader 83 | */ 84 | public function processServiceHeader($method, $header, $data) 85 | { 86 | $methodDefinition = $this->definition->getMethod($method); 87 | $headerDefinition = $methodDefinition->getHeader($header); 88 | 89 | $this->requestHeaderMessageBinder->setHeader($header); 90 | $data = $this->requestHeaderMessageBinder->processMessage($methodDefinition, $data, $this->definition->getTypeRepository()); 91 | 92 | return new SoapHeader($this->definition->getNamespace(), $headerDefinition->getName(), $data); 93 | } 94 | 95 | /** 96 | * @param string $method 97 | * @param array $arguments 98 | * 99 | * @return array 100 | */ 101 | public function processServiceMethodArguments($method, $arguments) 102 | { 103 | $methodDefinition = $this->definition->getMethod($method); 104 | 105 | return array_merge( 106 | array('_controller' => $methodDefinition->getController()), 107 | $this->requestMessageBinder->processMessage($methodDefinition, $arguments, $this->definition->getTypeRepository()) 108 | ); 109 | } 110 | 111 | /** 112 | * @param string $name 113 | * @param mixed 114 | * 115 | * @return mixed 116 | */ 117 | public function processServiceMethodReturnValue($name, $return) 118 | { 119 | $methodDefinition = $this->definition->getMethod($name); 120 | 121 | return $this->responseMessageBinder->processMessage($methodDefinition, $return, $this->definition->getTypeRepository()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/Alias.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 14 | 15 | /** 16 | * @Annotation 17 | */ 18 | class Alias extends Configuration 19 | { 20 | private $value; 21 | 22 | public function getValue() 23 | { 24 | return $this->value; 25 | } 26 | 27 | public function setValue($value) 28 | { 29 | $this->value = $value; 30 | } 31 | 32 | public function getAliasName() 33 | { 34 | return 'alias'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/ComplexType.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 12 | 13 | /** 14 | * @Annotation 15 | */ 16 | class ComplexType extends Configuration 17 | { 18 | private $name; 19 | private $value; 20 | private $isNillable = false; 21 | 22 | public function getName() 23 | { 24 | return $this->name; 25 | } 26 | 27 | public function getValue() 28 | { 29 | return $this->value; 30 | } 31 | 32 | public function isNillable() 33 | { 34 | return $this->isNillable; 35 | } 36 | 37 | public function setName($name) 38 | { 39 | $this->name = $name; 40 | } 41 | 42 | public function setValue($value) 43 | { 44 | $this->value = $value; 45 | } 46 | 47 | public function setNillable($isNillable) 48 | { 49 | $this->isNillable = (bool) $isNillable; 50 | } 51 | 52 | public function getAliasName() 53 | { 54 | return 'complextype'; 55 | } 56 | } -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/Configuration.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 12 | 13 | /** 14 | * Based on \Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationAnnotation 15 | * 16 | * @author Francis Besset 17 | */ 18 | abstract class Configuration implements ConfigurationInterface 19 | { 20 | public function __construct(array $values) 21 | { 22 | foreach ($values as $k => $v) { 23 | if (!method_exists($this, $name = 'set'.$k)) { 24 | throw new \RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, __CLASS__)); 25 | } 26 | 27 | $this->$name($v); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/ConfigurationInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 12 | 13 | /** 14 | * Based on \Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface 15 | * 16 | * @author Francis Besset 17 | */ 18 | interface ConfigurationInterface 19 | { 20 | /** 21 | * Returns the alias name for an annotated configuration. 22 | * 23 | * @return string 24 | */ 25 | function getAliasName(); 26 | } -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/Header.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 12 | 13 | /** 14 | * @Annotation 15 | */ 16 | class Header extends Param 17 | { 18 | public function getAliasName() 19 | { 20 | return 'header'; 21 | } 22 | } -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/Method.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 12 | 13 | /** 14 | * @Annotation 15 | */ 16 | class Method extends Configuration 17 | { 18 | private $value; 19 | private $service; 20 | 21 | public function getValue() 22 | { 23 | return $this->value; 24 | } 25 | 26 | public function getService() 27 | { 28 | return $this->service; 29 | } 30 | 31 | public function setValue($value) 32 | { 33 | $this->value = $value; 34 | } 35 | 36 | public function setService($service) 37 | { 38 | $this->service = $service; 39 | } 40 | 41 | public function getAliasName() 42 | { 43 | return 'method'; 44 | } 45 | } -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/Param.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 12 | 13 | /** 14 | * @Annotation 15 | */ 16 | class Param extends Configuration implements TypedElementInterface 17 | { 18 | private $value; 19 | private $phpType; 20 | private $xmlType; 21 | 22 | public function getValue() 23 | { 24 | return $this->value; 25 | } 26 | 27 | public function getPhpType() 28 | { 29 | return $this->phpType; 30 | } 31 | 32 | public function getXmlType() 33 | { 34 | return $this->xmlType; 35 | } 36 | 37 | public function setValue($value) 38 | { 39 | $this->value = $value; 40 | } 41 | 42 | public function setPhpType($phpType) 43 | { 44 | $this->phpType = $phpType; 45 | } 46 | 47 | public function setXmlType($xmlType) 48 | { 49 | $this->xmlType = $xmlType; 50 | } 51 | 52 | public function getAliasName() 53 | { 54 | return 'param'; 55 | } 56 | } -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/Result.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 12 | 13 | /** 14 | * @Annotation 15 | */ 16 | class Result extends Configuration implements TypedElementInterface 17 | { 18 | private $phpType; 19 | private $xmlType; 20 | 21 | public function getPhpType() 22 | { 23 | return $this->phpType; 24 | } 25 | 26 | public function getXmlType() 27 | { 28 | return $this->xmlType; 29 | } 30 | 31 | public function setPhpType($phpType) 32 | { 33 | $this->phpType = $phpType; 34 | } 35 | 36 | public function setXmlType($xmlType) 37 | { 38 | $this->xmlType = $xmlType; 39 | } 40 | 41 | public function getAliasName() 42 | { 43 | return 'result'; 44 | } 45 | } -------------------------------------------------------------------------------- /ServiceDefinition/Annotation/TypedElementInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\ServiceDefinition\Annotation; 12 | 13 | interface TypedElementInterface 14 | { 15 | function getPhpType(); 16 | function getXmlType(); 17 | function setPhpType($phpType); 18 | function setXmlType($xmlType); 19 | } -------------------------------------------------------------------------------- /ServiceDefinition/ComplexType.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceDefinition; 14 | 15 | /** 16 | * @author Francis Besset 17 | */ 18 | class ComplexType 19 | { 20 | private $name; 21 | private $value; 22 | private $isNillable = false; 23 | 24 | public function getName() 25 | { 26 | return $this->name; 27 | } 28 | 29 | public function getValue() 30 | { 31 | return $this->value; 32 | } 33 | 34 | public function isNillable() 35 | { 36 | return $this->isNillable; 37 | } 38 | 39 | public function setName($name) 40 | { 41 | $this->name = $name; 42 | } 43 | 44 | public function setValue($value) 45 | { 46 | $this->value = $value; 47 | } 48 | 49 | public function setNillable($isNillable) 50 | { 51 | $this->isNillable = (bool) $isNillable; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ServiceDefinition/Definition.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceDefinition; 14 | 15 | use BeSimple\SoapCommon\Definition\Definition as BaseDefinition; 16 | use BeSimple\SoapCommon\Definition\Type\TypeRepository; 17 | 18 | /** 19 | * @author Christian Kerl 20 | * @author Francis Besset 21 | */ 22 | class Definition extends BaseDefinition 23 | { 24 | public function __construct(TypeRepository $typeRepository) 25 | { 26 | $this->typeRepository = $typeRepository; 27 | 28 | $this->setOptions(array()); 29 | } 30 | 31 | public function setName($name) 32 | { 33 | $this->name = $name; 34 | 35 | return $this; 36 | } 37 | 38 | public function setNamespace($namespace) 39 | { 40 | $this->namespace = $namespace; 41 | 42 | return $this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ServiceDefinition/Loader/AnnotationClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceDefinition\Loader; 14 | 15 | use BeSimple\SoapBundle\ServiceDefinition as Definition; 16 | use BeSimple\SoapBundle\ServiceDefinition\Annotation; 17 | use BeSimple\SoapCommon\Definition\Type\ComplexType; 18 | use BeSimple\SoapCommon\Definition\Type\TypeRepository; 19 | use Doctrine\Common\Annotations\Reader; 20 | use Symfony\Component\Config\Loader\Loader; 21 | 22 | /** 23 | * AnnotationClassLoader loads ServiceDefinition from a PHP class and its methods. 24 | * 25 | * Based on \Symfony\Component\Routing\Loader\AnnotationClassLoader 26 | * 27 | * @author Christian Kerl 28 | * @author Francis Besset 29 | */ 30 | class AnnotationClassLoader extends Loader 31 | { 32 | protected $reader; 33 | 34 | protected $typeRepository; 35 | 36 | /** 37 | * Constructor. 38 | * 39 | * @param \Doctrine\Common\Annotations\Reader $reader 40 | */ 41 | public function __construct(Reader $reader, TypeRepository $typeRepository) 42 | { 43 | $this->reader = $reader; 44 | $this->typeRepository = $typeRepository; 45 | } 46 | 47 | /** 48 | * Loads a ServiceDefinition from annotations from a class. 49 | * 50 | * @param string $class A class name 51 | * @param string $type The resource type 52 | * 53 | * @return \BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition A ServiceDefinition instance 54 | * 55 | * @throws \InvalidArgumentException When route can't be parsed 56 | */ 57 | public function load($class, $type = null) 58 | { 59 | if (!class_exists($class)) { 60 | throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); 61 | } 62 | 63 | $class = new \ReflectionClass($class); 64 | $definition = new Definition\Definition($this->typeRepository); 65 | 66 | $sharedHeaders = array(); 67 | foreach ($this->reader->getClassAnnotations($class) as $annotation) { 68 | if ($annotation instanceof Annotation\Header) { 69 | $sharedHeaders[$annotation->getValue()] = $this->loadType($annotation->getPhpType()); 70 | } 71 | } 72 | 73 | foreach ($class->getMethods() as $method) { 74 | $serviceHeaders = $sharedHeaders; 75 | $serviceArguments = array(); 76 | $serviceMethod = 77 | $serviceReturn = null; 78 | 79 | foreach ($this->reader->getMethodAnnotations($method) as $annotation) { 80 | if ($annotation instanceof Annotation\Header) { 81 | $serviceHeaders[$annotation->getValue()] = $this->loadType($annotation->getPhpType()); 82 | } elseif ($annotation instanceof Annotation\Param) { 83 | $serviceArguments[$annotation->getValue()] = $this->loadType($annotation->getPhpType()); 84 | } elseif ($annotation instanceof Annotation\Method) { 85 | if ($serviceMethod) { 86 | throw new \LogicException(sprintf('@Soap\Method defined twice for "%s".', $method->getName())); 87 | } 88 | 89 | $serviceMethod = new Definition\Method( 90 | $annotation->getValue(), 91 | $this->getController($class, $method, $annotation) 92 | ); 93 | } elseif ($annotation instanceof Annotation\Result) { 94 | if ($serviceReturn) { 95 | throw new \LogicException(sprintf('@Soap\Result defined twice for "%s".', $method->getName())); 96 | } 97 | 98 | $serviceReturn = $annotation->getPhpType(); 99 | } 100 | } 101 | 102 | if (!$serviceMethod && (!empty($serviceArguments) || $serviceReturn)) { 103 | throw new \LogicException(sprintf('@Soap\Method non-existent for "%s".', $method->getName())); 104 | } 105 | 106 | if ($serviceMethod) { 107 | foreach ($serviceHeaders as $name => $type) { 108 | $serviceMethod->addHeader($name, $type); 109 | } 110 | 111 | foreach ($serviceArguments as $name => $type) { 112 | $serviceMethod->addInput($name, $type); 113 | } 114 | 115 | if (!$serviceReturn) { 116 | throw new \LogicException(sprintf('@Soap\Result non-existent for "%s".', $method->getName())); 117 | } 118 | 119 | $serviceMethod->setOutput($this->loadType($serviceReturn)); 120 | 121 | $definition->addMethod($serviceMethod); 122 | } 123 | } 124 | 125 | return $definition; 126 | } 127 | 128 | /** 129 | * @param \ReflectionMethod $method 130 | * @param \BeSimple\SoapBundle\ServiceDefinition\Annotation\Method $annotation 131 | * 132 | * @return string 133 | */ 134 | private function getController(\ReflectionClass $class, \ReflectionMethod $method, Annotation\Method $annotation) 135 | { 136 | if(null !== $annotation->getService()) { 137 | return $annotation->getService() . ':' . $method->name; 138 | } else { 139 | return $class->name . '::' . $method->name; 140 | } 141 | } 142 | 143 | private function loadType($phpType) 144 | { 145 | if (false !== $arrayOf = $this->typeRepository->getArrayOf($phpType)) { 146 | $this->loadType($arrayOf); 147 | } 148 | 149 | if (!$this->typeRepository->hasType($phpType)) { 150 | $complexTypeResolver = $this->resolve($phpType, 'annotation_complextype'); 151 | if (!$complexTypeResolver) { 152 | throw new \Exception(); 153 | } 154 | 155 | $loaded = $complexTypeResolver->load($phpType); 156 | $complexType = new ComplexType($phpType, isset($loaded['alias']) ? $loaded['alias'] : $phpType); 157 | foreach ($loaded['properties'] as $name => $property) { 158 | $complexType->add($name, $this->loadType($property->getValue()), $property->isNillable()); 159 | } 160 | 161 | $this->typeRepository->addComplexType($complexType); 162 | } 163 | 164 | return $phpType; 165 | } 166 | 167 | /** 168 | * Returns true if this class supports the given resource. 169 | * 170 | * @param mixed $resource A resource 171 | * @param string $type The resource type 172 | * 173 | * @return Boolean True if this class supports the given resource, false otherwise 174 | */ 175 | public function supports($resource, $type = null) 176 | { 177 | return is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type); 178 | } 179 | 180 | /** 181 | * @return null 182 | */ 183 | public function getResolver() 184 | { 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /ServiceDefinition/Loader/AnnotationComplexTypeLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceDefinition\Loader; 14 | 15 | use BeSimple\SoapBundle\ServiceDefinition\ComplexType; 16 | use BeSimple\SoapBundle\Util\Collection; 17 | 18 | /** 19 | * AnnotationComplexTypeLoader loads ServiceDefinition from a PHP class and its methods. 20 | * 21 | * Based on \Symfony\Component\Routing\Loader\AnnotationClassLoader 22 | * 23 | * @author Francis Besset 24 | */ 25 | class AnnotationComplexTypeLoader extends AnnotationClassLoader 26 | { 27 | private $aliasClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\Alias'; 28 | private $complexTypeClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\ComplexType'; 29 | 30 | /** 31 | * Loads a ServiceDefinition from annotations from a class. 32 | * 33 | * @param string $class A class name 34 | * @param string $type The resource type 35 | * 36 | * @return ServiceDefinition A ServiceDefinition instance 37 | * 38 | * @throws \InvalidArgumentException When route can't be parsed 39 | */ 40 | public function load($class, $type = null) 41 | { 42 | if (!class_exists($class)) { 43 | throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); 44 | } 45 | 46 | $annotations = array(); 47 | 48 | $class = new \ReflectionClass($class); 49 | if ($alias = $this->reader->getClassAnnotation($class, $this->aliasClass)) { 50 | $annotations['alias'] = $alias->getValue(); 51 | } 52 | 53 | $annotations['properties'] = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\ComplexType'); 54 | foreach ($class->getProperties() as $property) { 55 | $complexType = $this->reader->getPropertyAnnotation($property, $this->complexTypeClass); 56 | 57 | if ($complexType) { 58 | $propertyComplexType = new ComplexType(); 59 | $propertyComplexType->setValue($complexType->getValue()); 60 | $propertyComplexType->setNillable($complexType->isNillable()); 61 | $propertyComplexType->setName($property->getName()); 62 | $annotations['properties']->add($propertyComplexType); 63 | } 64 | } 65 | 66 | return $annotations; 67 | } 68 | 69 | /** 70 | * Returns true if this class supports the given resource. 71 | * 72 | * @param mixed $resource A resource 73 | * @param string $type The resource type 74 | * 75 | * @return Boolean True if this class supports the given resource, false otherwise 76 | */ 77 | public function supports($resource, $type = null) 78 | { 79 | return is_string($resource) && class_exists($resource) && 'annotation_complextype' === $type; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ServiceDefinition/Loader/AnnotationFileLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceDefinition\Loader; 14 | 15 | use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition; 16 | 17 | use Symfony\Component\Config\FileLocator; 18 | use Symfony\Component\Config\Loader\FileLoader; 19 | 20 | /** 21 | * AnnotationFileLoader loads ServiceDefinition from annotations set 22 | * on a PHP class and its methods. 23 | * 24 | * Based on \Symfony\Component\Routing\Loader\AnnotationFileLoader 25 | * 26 | * @author Christian Kerl 27 | */ 28 | class AnnotationFileLoader extends FileLoader 29 | { 30 | protected $loader; 31 | 32 | /** 33 | * Constructor. 34 | * 35 | * @param AnnotationClassLoader $loader An AnnotationClassLoader instance 36 | * @param string|array $paths A path or an array of paths where to look for resources 37 | */ 38 | public function __construct(FileLocator $locator, AnnotationClassLoader $loader, $paths = array()) 39 | { 40 | if (!function_exists('token_get_all')) { 41 | throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.'); 42 | } 43 | 44 | parent::__construct($locator, $paths); 45 | 46 | $this->loader = $loader; 47 | } 48 | 49 | /** 50 | * Loads from annotations from a file. 51 | * 52 | * @param string $file A PHP file path 53 | * @param string $type The resource type 54 | * 55 | * @return ServiceDefinition A ServiceDefinition instance 56 | * 57 | * @throws \InvalidArgumentException When the file does not exist 58 | */ 59 | public function load($file, $type = null) 60 | { 61 | $path = $this->locator->locate($file); 62 | 63 | if ($class = $this->findClass($path)) { 64 | return $this->loader->load($class, $type); 65 | } 66 | 67 | return null; 68 | } 69 | 70 | /** 71 | * Returns true if this class supports the given resource. 72 | * 73 | * @param mixed $resource A resource 74 | * @param string $type The resource type 75 | * 76 | * @return Boolean True if this class supports the given resource, false otherwise 77 | */ 78 | public function supports($resource, $type = null) 79 | { 80 | return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type); 81 | } 82 | 83 | /** 84 | * Returns the full class name for the first class in the file. 85 | * 86 | * @param string $file A PHP file path 87 | * 88 | * @return string|false Full class name if found, false otherwise 89 | */ 90 | protected function findClass($file) 91 | { 92 | $class = false; 93 | $namespace = false; 94 | $tokens = token_get_all(file_get_contents($file)); 95 | while ($token = array_shift($tokens)) { 96 | if (!is_array($token)) { 97 | continue; 98 | } 99 | 100 | if (true === $class && T_STRING === $token[0]) { 101 | return $namespace.'\\'.$token[1]; 102 | } 103 | 104 | if (true === $namespace && T_STRING === $token[0]) { 105 | $namespace = ''; 106 | do { 107 | $namespace .= $token[1]; 108 | $token = array_shift($tokens); 109 | } while ($tokens && is_array($token) && in_array($token[0], array(T_NS_SEPARATOR, T_STRING))); 110 | } 111 | 112 | if (T_CLASS === $token[0]) { 113 | $class = true; 114 | } 115 | 116 | if (T_NAMESPACE === $token[0]) { 117 | $namespace = true; 118 | } 119 | } 120 | 121 | return false; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ServiceDefinition/Loader/schema/servicedefinition-1.0.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /ServiceDefinition/Method.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\ServiceDefinition; 14 | 15 | use BeSimple\SoapCommon\Definition\Method as BaseMethod; 16 | use BeSimple\SoapCommon\Definition\Type\TypeRepository; 17 | 18 | /** 19 | * @author Christian Kerl 20 | * @author Francis Besset 21 | */ 22 | class Method extends BaseMethod 23 | { 24 | private $controller; 25 | 26 | public function __construct($name, $controller) 27 | { 28 | parent::__construct($name); 29 | 30 | $this->controller = $controller; 31 | } 32 | 33 | public function getController() 34 | { 35 | return $this->controller; 36 | } 37 | 38 | public function getVersions() 39 | { 40 | return array(\SOAP_1_1); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Soap/SoapAttachment.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Soap; 12 | 13 | class SoapAttachment 14 | { 15 | private $id; 16 | private $type; 17 | private $content; 18 | 19 | public function __construct($id, $type, $content) 20 | { 21 | $this->id = $id; 22 | $this->type = $type; 23 | $this->content = $content; 24 | } 25 | 26 | public function getId() 27 | { 28 | return $this->id; 29 | } 30 | 31 | public function getType() 32 | { 33 | return $this->type; 34 | } 35 | 36 | public function getContent() 37 | { 38 | return $this->content; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Soap/SoapClientBuilder.php: -------------------------------------------------------------------------------- 1 | checkOptions($options); 18 | 19 | $this 20 | ->withWsdl($wsdl) 21 | ->withTrace($options['debug']) 22 | ; 23 | 24 | if (isset($options['user_agent'])) { 25 | $this->withUserAgent($options['user_agent']); 26 | } 27 | 28 | if (isset($options['cache_type'])) { 29 | $this->withWsdlCache($options['cache_type']); 30 | } 31 | 32 | if ($classmap) { 33 | $this->withClassmap($classmap); 34 | } 35 | 36 | if ($converters) { 37 | $this->withTypeConverters($converters); 38 | } 39 | } 40 | 41 | public function build() 42 | { 43 | if (!$this->soapClient) { 44 | $this->soapClient = parent::build(); 45 | } 46 | 47 | return $this->soapClient; 48 | } 49 | 50 | protected function checkOptions(array $options) 51 | { 52 | $checkOptions = array( 53 | 'debug' => false, 54 | 'cache_type' => null, 55 | 'exceptions' => true, 56 | 'user_agent' => 'BeSimpleSoap', 57 | ); 58 | 59 | // check option names and live merge, if errors are encountered Exception will be thrown 60 | $invalid = array(); 61 | $isInvalid = false; 62 | foreach ($options as $key => $value) { 63 | if (!array_key_exists($key, $checkOptions)) { 64 | $isInvalid = true; 65 | $invalid[] = $key; 66 | } 67 | } 68 | 69 | if ($isInvalid) { 70 | throw new \InvalidArgumentException(sprintf( 71 | 'The "%s" class does not support the following options: "%s".', 72 | get_class($this), 73 | implode('\', \'', $invalid) 74 | )); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Soap/SoapHeader.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Soap; 12 | 13 | class SoapHeader 14 | { 15 | private $namespace; 16 | private $name; 17 | private $data; 18 | 19 | public function __construct($namespace, $name, $data) 20 | { 21 | $this->namespace = $namespace; 22 | $this->name = $name; 23 | $this->data = $data; 24 | } 25 | 26 | public function __toString() 27 | { 28 | return $this->data; 29 | } 30 | 31 | public function getNamespace() 32 | { 33 | return $this->namespace; 34 | } 35 | 36 | public function getName() 37 | { 38 | return $this->name; 39 | } 40 | 41 | public function getData() 42 | { 43 | return $this->data; 44 | } 45 | 46 | public function toNativeSoapHeader() 47 | { 48 | return new \SoapHeader($this->namespace, $this->name, $this->data); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Soap/SoapRequest.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Soap; 12 | 13 | use BeSimple\SoapBundle\Util\Collection; 14 | use Symfony\Component\HttpFoundation\Request; 15 | use Zend\Mime\Message; 16 | 17 | /** 18 | * SoapRequest. 19 | * 20 | * @author Christian Kerl 21 | */ 22 | class SoapRequest extends Request 23 | { 24 | /** 25 | * @var string 26 | */ 27 | protected $soapMessage; 28 | 29 | /** 30 | * @var string 31 | */ 32 | protected $soapAction; 33 | 34 | /** 35 | * @var \BeSimple\SoapBundle\Util\Collection 36 | */ 37 | protected $soapHeaders; 38 | 39 | /** 40 | * @var \BeSimple\SoapBundle\Util\Collection 41 | */ 42 | protected $soapAttachments; 43 | 44 | /** 45 | * @param \Symfony\Component\HttpFoundation\Request $request 46 | * 47 | * @return SoapRequest 48 | */ 49 | public static function createFromHttpRequest(Request $request) 50 | { 51 | return new static($request->query->all(), $request->request->all(), $request->attributes->all(), $request->cookies->all(), $request->files->all(), $request->server->all(), $request->content); 52 | } 53 | 54 | public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) 55 | { 56 | parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content); 57 | 58 | $this->soapMessage = null; 59 | $this->soapHeaders = new Collection('getName', 'BeSimple\SoapBundle\Soap\SoapHeader'); 60 | $this->soapAttachments = new Collection('getId', 'BeSimple\SoapBundle\Soap\SoapAttachment'); 61 | 62 | $this->setRequestFormat('soap'); 63 | } 64 | 65 | /** 66 | * Gets the XML string of the SOAP message. 67 | * 68 | * @return string 69 | */ 70 | public function getSoapMessage() 71 | { 72 | if(null === $this->soapMessage) { 73 | $this->soapMessage = $this->initializeSoapMessage(); 74 | } 75 | 76 | return $this->soapMessage; 77 | } 78 | 79 | public function getSoapHeaders() 80 | { 81 | return $this->soapHeaders; 82 | } 83 | 84 | public function getSoapAttachments() 85 | { 86 | return $this->soapAttachments; 87 | } 88 | 89 | protected function initializeSoapMessage() 90 | { 91 | if($this->server->has('CONTENT_TYPE')) { 92 | $type = $this->splitContentTypeHeader($this->server->get('CONTENT_TYPE')); 93 | 94 | switch($type['_type']) { 95 | case 'multipart/related': 96 | if($type['type'] == 'application/xop+xml') { 97 | return $this->initializeMtomSoapMessage($type, $this->getContent()); 98 | } else { 99 | //log error 100 | } 101 | break; 102 | case 'application/soap+xml': 103 | // goto fallback 104 | break; 105 | default: 106 | // log error 107 | break; 108 | } 109 | } 110 | 111 | // fallback 112 | return $this->getContent(); 113 | } 114 | 115 | protected function initializeMtomSoapMessage(array $contentTypeHeader, $content) 116 | { 117 | if(!isset($contentTypeHeader['start']) || !isset($contentTypeHeader['start-info']) || !isset($contentTypeHeader['boundary'])) { 118 | throw new \InvalidArgumentException(); 119 | } 120 | 121 | $mimeMessage = Message::createFromMessage($content, $contentTypeHeader['boundary']); 122 | $mimeParts = $mimeMessage->getParts(); 123 | 124 | $soapMimePartId = trim($contentTypeHeader['start'], '<>'); 125 | $soapMimePartType = $contentTypeHeader['start-info']; 126 | 127 | $rootPart = array_shift($mimeParts); 128 | $rootPartType = $this->splitContentTypeHeader($rootPart->type); 129 | 130 | // TODO: add more checks to achieve full compatibility to MTOM spec 131 | // http://www.w3.org/TR/soap12-mtom/ 132 | if($rootPart->id != $soapMimePartId || $rootPartType['_type'] != 'application/xop+xml' || $rootPartType['type'] != $soapMimePartType) { 133 | throw new \InvalidArgumentException(); 134 | } 135 | 136 | foreach($mimeParts as $mimePart) { 137 | $this->soapAttachments->add(new SoapAttachment( 138 | $mimePart->id, 139 | $mimePart->type, 140 | // handle content decoding / prevent encoding 141 | $mimePart->getContent() 142 | )); 143 | } 144 | 145 | // handle content decoding / prevent encoding 146 | return $rootPart->getContent(); 147 | } 148 | 149 | protected function splitContentTypeHeader($header) 150 | { 151 | $result = array(); 152 | $parts = explode(';', strtolower($header)); 153 | 154 | $result['_type'] = array_shift($parts); 155 | 156 | foreach($parts as $part) { 157 | list($key, $value) = explode('=', trim($part), 2); 158 | 159 | $result[$key] = trim($value, '"'); 160 | } 161 | 162 | return $result; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /Soap/SoapResponse.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Soap; 12 | 13 | use BeSimple\SoapBundle\Util\Collection; 14 | 15 | use Symfony\Component\HttpFoundation\Response; 16 | 17 | /** 18 | * SoapResponse. 19 | * 20 | * @author Christian Kerl 21 | */ 22 | class SoapResponse extends Response 23 | { 24 | /** 25 | * @var \BeSimple\SoapBundle\Util\Collection 26 | */ 27 | protected $soapHeaders; 28 | 29 | /** 30 | * @var mixed 31 | */ 32 | protected $soapReturnValue; 33 | 34 | public function __construct($returnValue = null) 35 | { 36 | parent::__construct(); 37 | 38 | $this->soapHeaders = new Collection('getName', 'BeSimple\SoapBundle\Soap\SoapHeader'); 39 | $this->setReturnValue($returnValue); 40 | } 41 | 42 | /** 43 | * @param SoapHeader $soapHeader 44 | */ 45 | public function addSoapHeader(SoapHeader $soapHeader) 46 | { 47 | $this->soapHeaders->add($soapHeader); 48 | } 49 | 50 | /** 51 | * @return \BeSimple\SoapBundle\Util\Collection 52 | */ 53 | public function getSoapHeaders() 54 | { 55 | return $this->soapHeaders; 56 | } 57 | 58 | public function setReturnValue($value) 59 | { 60 | $this->soapReturnValue = $value; 61 | 62 | return $this; 63 | } 64 | 65 | public function getReturnValue() 66 | { 67 | return $this->soapReturnValue; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ==== 3 | 4 | * Move RequestBinding in a future ParamConverterListener 5 | * See SensioFrameworkExtraBundle 6 | * Add a security layer to log an user with Symfony2 Security Component 7 | -------------------------------------------------------------------------------- /Tests/ServiceBinding/RpcLiteralRequestMessageBinderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\Tests\ServiceBinding; 14 | 15 | use BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder; 16 | use BeSimple\SoapBundle\ServiceDefinition as Definition; 17 | use BeSimple\SoapBundle\Tests\fixtures\ServiceBinding as Fixtures; 18 | use BeSimple\SoapBundle\Util\Collection; 19 | use BeSimple\SoapCommon\Definition\Type\ComplexType; 20 | use BeSimple\SoapCommon\Definition\Type\TypeRepository; 21 | 22 | class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase 23 | { 24 | /** 25 | * @dataProvider messageProvider 26 | */ 27 | public function testProcessMessage(Definition\Method $method, array $message, array $assert) 28 | { 29 | $messageBinder = new RpcLiteralRequestMessageBinder(); 30 | $result = $messageBinder->processMessage($method, $message, $this->getTypeRepository()); 31 | 32 | $this->assertSame($assert, $result); 33 | } 34 | 35 | public function testProcessMessageWithComplexType() 36 | { 37 | $typeRepository = $this->addComplexTypes($this->getTypeRepository()); 38 | $messageBinder = new RpcLiteralRequestMessageBinder(); 39 | 40 | $method = new Definition\Method('complextype_argument', null); 41 | $method->addInput('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo'); 42 | 43 | $foo = new Fixtures\Foo('foobar', 19395); 44 | $result = $messageBinder->processMessage( 45 | $method, 46 | array($foo), 47 | $typeRepository 48 | ); 49 | 50 | $this->assertEquals(array('foo' => $foo), $result); 51 | 52 | $foo1 = new Fixtures\Foo('foobar', 29291); 53 | $foo2 = new Fixtures\Foo('barfoo', 39392); 54 | $foos = new \stdClass(); 55 | $foos->item = array($foo1, $foo2); 56 | 57 | $method = new Definition\Method('complextype_argument', null); 58 | $method->addInput('foos', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]'); 59 | 60 | $result = $messageBinder->processMessage( 61 | $method, 62 | array($foos), 63 | $typeRepository 64 | ); 65 | 66 | $this->assertEquals(array('foos' => array($foo1, $foo2)), $result); 67 | } 68 | 69 | public function testProcessMessageSoapFault() 70 | { 71 | $messageBinder = new RpcLiteralRequestMessageBinder(); 72 | 73 | $method = new Definition\Method('complextype_argument', null); 74 | $method->addInput('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo'); 75 | 76 | $foo = new Fixtures\Foo('foo', null); 77 | 78 | $this->setExpectedException('SoapFault'); 79 | $messageBinder->processMessage( 80 | $method, 81 | array($foo), 82 | $this->addComplexTypes($this->getTypeRepository()) 83 | ); 84 | } 85 | 86 | public function testProcessMessageWithComplexTypeReference() 87 | { 88 | $messageBinder = new RpcLiteralRequestMessageBinder(); 89 | 90 | $method = new Definition\Method('complextype_argument', null); 91 | $method->addInput('foos', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]'); 92 | 93 | $foo = new Fixtures\Foo('foo', 2499104); 94 | $foos = new \stdClass(); 95 | $foos->item = array($foo, $foo); 96 | 97 | $result = $messageBinder->processMessage( 98 | $method, 99 | array($foos), 100 | $this->addComplexTypes($this->getTypeRepository()) 101 | ); 102 | 103 | $this->assertEquals(array('foos' => array($foo, $foo)), $result); 104 | } 105 | 106 | public function testProcessMessageWithComplexTypeIntoComplexType() 107 | { 108 | $messageBinder = new RpcLiteralRequestMessageBinder(); 109 | 110 | $method = new Definition\Method('complextype_argument', null); 111 | $method->addInput('fooBar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooBar'); 112 | 113 | $foo = new Fixtures\Foo('foo', 38845); 114 | $bar = new Fixtures\Bar('bar', null); 115 | $fooBar = new Fixtures\FooBar($foo, $bar); 116 | 117 | $result = $messageBinder->processMessage( 118 | $method, 119 | array($fooBar), 120 | $this->addComplexTypes($this->getTypeRepository()) 121 | ); 122 | 123 | $this->assertEquals(array('fooBar' => $fooBar), $result); 124 | } 125 | 126 | public function testProcessMessageComplexTypeWithArrays() 127 | { 128 | $messageBinder = new RpcLiteralRequestMessageBinder(); 129 | 130 | $method = new Definition\Method('complextype_with_array', null); 131 | $method->addInput('simple_arrays', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\SimpleArrays'); 132 | 133 | $array = array(1, 2, 3, 4); 134 | $stdClass = new \stdClass(); 135 | $stdClass->item = $array; 136 | $simpleArrays = new Fixtures\SimpleArrays(null, new \stdClass(), $stdClass); 137 | 138 | $result = $messageBinder->processMessage( 139 | $method, 140 | array($simpleArrays), 141 | $this->addComplexTypes($this->getTypeRepository()) 142 | ); 143 | 144 | $result = $result['simple_arrays']; 145 | $this->assertEquals(null, $result->array1); 146 | $this->assertEquals(array(), $result->getArray2()); 147 | $this->assertEquals($array, $result->getArray3()); 148 | } 149 | 150 | public function testProcessMessageWithEmptyArrayComplexType() 151 | { 152 | $messageBinder = new RpcLiteralRequestMessageBinder(); 153 | 154 | $method = new Definition\Method('empty_array_complex_type', null); 155 | $method->addInput('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]'); 156 | 157 | $result = $messageBinder->processMessage( 158 | $method, 159 | array(new \stdClass()), 160 | $this->addComplexTypes($this->getTypeRepository()) 161 | ); 162 | 163 | $this->assertEquals(array('foo' => array()), $result); 164 | } 165 | 166 | public function testProccessMessagePreventInfiniteRecursion() 167 | { 168 | $messageBinder = new RpcLiteralRequestMessageBinder(); 169 | 170 | $method = new Definition\Method('prevent_infinite_recursion', null); 171 | $method->addInput('foo_recursive', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive'); 172 | 173 | $foo = new Fixtures\FooRecursive('foo', ''); 174 | $bar = new Fixtures\BarRecursive($foo, 10394); 175 | $foo->bar = $bar; 176 | 177 | $result = $messageBinder->processMessage( 178 | $method, 179 | array($foo), 180 | $this->addComplexTypes($this->getTypeRepository()) 181 | ); 182 | 183 | $this->assertEquals(array('foo_recursive' => $foo), $result); 184 | } 185 | 186 | public function messageProvider() 187 | { 188 | $messages = array(); 189 | 190 | $messages[] = array( 191 | new Definition\Method('no_argument', null), 192 | array(), 193 | array(), 194 | ); 195 | 196 | $method = new Definition\Method('string_argument', null); 197 | $method->addInput('foo', 'string'); 198 | $messages[] = array( 199 | $method, 200 | array('bar'), 201 | array('foo' => 'bar'), 202 | ); 203 | 204 | $method = new Definition\Method('string_int_arguments', null); 205 | $method->addInput('foo', 'string'); 206 | $method->addInput('bar', 'int'); 207 | $messages[] = array( 208 | $method, 209 | array('test', 20), 210 | array('foo' => 'test', 'bar' => 20), 211 | ); 212 | 213 | $method = new Definition\Method('array_string_arguments', null); 214 | $method->addInput('foo', 'string[]'); 215 | $method->addInput('bar', 'int'); 216 | $strings = new \stdClass(); 217 | $strings->item = array('foo', 'bar', 'barfoo'); 218 | $messages[] = array( 219 | $method, 220 | array($strings, 4), 221 | array('foo' => array('foo', 'bar', 'barfoo'), 'bar' => 4), 222 | ); 223 | 224 | $method = new Definition\Method('empty_array', null); 225 | $method->addInput('foo', 'string[]'); 226 | $messages[] = array( 227 | $method, 228 | array(new \stdClass()), 229 | array('foo' => array()), 230 | ); 231 | 232 | return $messages; 233 | } 234 | 235 | private function addComplexTypes(TypeRepository $typeRepository) 236 | { 237 | $foo = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo', 'Foo'); 238 | $foo->add('foo', 'string'); 239 | $foo->add('bar', 'int'); 240 | $typeRepository->addComplexType($foo); 241 | 242 | $bar = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Bar', 'Bar'); 243 | $bar->add('foo', 'string'); 244 | $bar->add('bar', 'int', true); 245 | $typeRepository->addComplexType($bar); 246 | 247 | $fooBar = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooBar', 'FooBar'); 248 | $fooBar->add('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo'); 249 | $fooBar->add('bar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Bar'); 250 | $typeRepository->addComplexType($fooBar); 251 | 252 | $simpleArrays = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\SimpleArrays', 'SimpleArrays'); 253 | $simpleArrays->add('array1', 'string[]', true); 254 | $simpleArrays->add('array2', 'string[]'); 255 | $simpleArrays->add('array3', 'string[]'); 256 | $typeRepository->addComplexType($simpleArrays); 257 | 258 | $fooRecursive = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive', 'FooRecursive'); 259 | $fooRecursive->add('bar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\BarRecursive'); 260 | $typeRepository->addComplexType($fooRecursive); 261 | 262 | $barRecursive = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\BarRecursive', 'BarRecursive'); 263 | $barRecursive->add('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive'); 264 | $typeRepository->addComplexType($barRecursive); 265 | 266 | return $typeRepository; 267 | } 268 | 269 | private function createComplexTypeCollection(array $properties) 270 | { 271 | $collection = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\ComplexType'); 272 | 273 | foreach ($properties as $property) { 274 | $complexType = new Definition\ComplexType(); 275 | $complexType->setName($property[0]); 276 | $complexType->setValue($property[1]); 277 | 278 | if (isset($property[2])) { 279 | $complexType->setNillable($property[2]); 280 | } 281 | 282 | $collection->add($complexType); 283 | } 284 | 285 | return array('properties' => $collection); 286 | } 287 | 288 | private function getTypeRepository() 289 | { 290 | $typeRepository = new TypeRepository(); 291 | $typeRepository->addXmlNamespace('xsd', 'http://www.w3.org/2001/XMLSchema'); 292 | $typeRepository->addType('string', 'xsd:string'); 293 | $typeRepository->addType('boolean', 'xsd:boolean'); 294 | $typeRepository->addType('int', 'xsd:int'); 295 | $typeRepository->addType('float', 'xsd:float'); 296 | $typeRepository->addType('date', 'xsd:date'); 297 | $typeRepository->addType('dateTime', 'xsd:dateTime'); 298 | 299 | return $typeRepository; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /Tests/ServiceBinding/fixtures/Attributes.php: -------------------------------------------------------------------------------- 1 | foo; 14 | } 15 | 16 | public function setFoo($foo) 17 | { 18 | $this->foo = $foo; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/ServiceBinding/fixtures/Setters.php: -------------------------------------------------------------------------------- 1 | foo; 14 | } 15 | 16 | public function setFoo($foo) 17 | { 18 | $this->foo = $foo; 19 | } 20 | 21 | public function getBar() 22 | { 23 | return $this->bar; 24 | } 25 | 26 | public function setBar($bar) 27 | { 28 | $this->bar = $bar; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Soap/SoapRequestTest.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Francis Besset 8 | * 9 | * This source file is subject to the MIT license that is bundled 10 | * with this source code in the file LICENSE. 11 | */ 12 | 13 | namespace BeSimple\SoapBundle\Tests\Soap; 14 | 15 | use BeSimple\SoapBundle\Soap\SoapRequest; 16 | 17 | /** 18 | * UnitTest for \BeSimple\SoapBundle\Soap\SoapRequest. 19 | * 20 | * @author Christian Kerl 21 | */ 22 | class SoapRequestTest extends \PHPUnit_Framework_TestCase 23 | { 24 | public function testMtomMessage() 25 | { 26 | $this->markTestSkipped('Skip because I\'m not sure that SoapRequest is used in a HTTP Request process.'); 27 | 28 | $content = $this->loadRequestContentFixture('mtom/simple.txt'); 29 | 30 | $request = new SoapRequest(array(), array(), array(), array(), array(), array(), $content); 31 | $request->server->set('CONTENT_TYPE', 'multipart/related; type="application/xop+xml";start="";boundary="uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7";start-info="application/soap+xml"'); 32 | 33 | $message = $request->getSoapMessage(); 34 | 35 | $this->assertEquals(735, strlen(trim($message))); 36 | $this->assertEquals(1, count($request->getSoapAttachments())); 37 | 38 | $attachment = $request->getSoapAttachments()->get('http://tempuri.org/1/632618206527087310'); 39 | 40 | $this->assertNotNull($attachment); 41 | $this->assertEquals('application/octet-stream', $attachment->getType()); 42 | $this->assertEquals(767, strlen(trim($attachment->getContent()))); 43 | } 44 | 45 | private function loadRequestContentFixture($name) 46 | { 47 | return file_get_contents(__DIR__.'/../fixtures/Soap/'.$name); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Tests/fixtures/ServiceBinding/Bar.php: -------------------------------------------------------------------------------- 1 | foo = $foo; 14 | $this->bar = $bar; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/fixtures/ServiceBinding/BarRecursive.php: -------------------------------------------------------------------------------- 1 | foo = $foo; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/fixtures/ServiceBinding/Foo.php: -------------------------------------------------------------------------------- 1 | foo = $foo; 13 | $this->bar = $bar; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tests/fixtures/ServiceBinding/FooBar.php: -------------------------------------------------------------------------------- 1 | foo = $foo; 13 | $this->bar = $bar; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tests/fixtures/ServiceBinding/FooRecursive.php: -------------------------------------------------------------------------------- 1 | array1 = $array1; 16 | $this->array2 = $array2; 17 | $this->array3 = $array3; 18 | } 19 | 20 | public function getArray2() 21 | { 22 | return $this->array2; 23 | } 24 | 25 | public function getArray3() 26 | { 27 | return $this->array3; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/fixtures/Soap/api-servicedefinition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Tests/fixtures/Soap/api.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /Tests/fixtures/Soap/mtom/simple.txt: -------------------------------------------------------------------------------- 1 | 2 | --uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 3 | Content-ID: 4 | Content-Transfer-Encoding: 8bit 5 | Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml" 6 | 7 | http://xmlsoap.org/echoBinaryAsStringurn:uuid:1bf061d6-d532-4b0c-930b-8b8202c38b8ahttp://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymoushttp://131.107.72.15/Mtom/svc/service.svc/Soap12MtomUTF8 8 | 9 | --uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 10 | Content-ID: 11 | Content-Transfer-Encoding: binary 12 | Content-Type: application/octet-stream 13 | 14 | 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 ! 15 | 16 | --uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7-- -------------------------------------------------------------------------------- /Util/Assert.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Util; 12 | 13 | /** 14 | * 15 | * @author Christian Kerl 16 | */ 17 | class Assert 18 | { 19 | const ARGUMENT_INVALID = 'Argument "%s" is invalid.'; 20 | const ARGUMENT_NULL = 'Argument "%s" can not be null.'; 21 | 22 | public static function thatArgument($name, $condition, $message = self::ARGUMENT_INVALID) 23 | { 24 | if(!$condition) { 25 | throw new \InvalidArgumentException(sprintf($message, $name)); 26 | } 27 | } 28 | 29 | public static function thatArgumentNotNull($name, $value) 30 | { 31 | self::thatArgument($name, null !== $value, self::ARGUMENT_NULL); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Util/Collection.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Util; 12 | 13 | class Collection implements \IteratorAggregate, \Countable 14 | { 15 | private $elements = array(); 16 | private $getter; 17 | private $class; 18 | 19 | public function __construct($getter, $class = null) 20 | { 21 | $this->getter = $getter; 22 | $this->class = $class; 23 | } 24 | 25 | public function add($element) 26 | { 27 | if ($this->class && !$element instanceof $this->class) { 28 | throw new \InvalidArgumentException(sprintf('Cannot add class "%s" because it is not an instance of "%s"', get_class($element), $this->class)); 29 | } 30 | 31 | $this->elements[$element->{$this->getter}()] = $element; 32 | } 33 | 34 | public function addAll($elements) 35 | { 36 | foreach ($elements as $element) { 37 | $this->add($element); 38 | } 39 | } 40 | 41 | public function has($key) 42 | { 43 | return isset($this->elements[$key]); 44 | } 45 | 46 | public function get($key) 47 | { 48 | return $this->has($key) ? $this->elements[$key] : null; 49 | } 50 | 51 | public function clear() 52 | { 53 | $this->elements = array(); 54 | } 55 | 56 | public function count() 57 | { 58 | return count($this->elements); 59 | } 60 | 61 | public function getIterator() 62 | { 63 | return new \ArrayIterator($this->elements); 64 | } 65 | } -------------------------------------------------------------------------------- /Util/QName.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Util; 12 | 13 | /** 14 | * @author Christian Kerl 15 | */ 16 | class QName 17 | { 18 | private $namespace; 19 | private $name; 20 | 21 | public static function isPrefixedQName($qname) 22 | { 23 | return false !== strpos($qname, ':') ? true : false; 24 | } 25 | 26 | public static function fromPrefixedQName($qname, $resolveNamespacePrefixCallable) 27 | { 28 | Assert::thatArgument('qname', self::isPrefixedQName($qname)); 29 | 30 | list($prefix, $name) = explode(':', $qname); 31 | 32 | return new self(call_user_func($resolveNamespacePrefixCallable, $prefix), $name); 33 | } 34 | 35 | public static function fromPackedQName($qname) 36 | { 37 | Assert::thatArgument('qname', preg_match('/^\{(.+)\}(.+)$/', $qname, $matches)); 38 | 39 | return new self($matches[1], $matches[2]); 40 | } 41 | 42 | public function __construct($namespace, $name) 43 | { 44 | $this->namespace = $namespace; 45 | $this->name = $name; 46 | } 47 | 48 | public function getNamespace() 49 | { 50 | return $this->namespace; 51 | } 52 | 53 | public function getName() 54 | { 55 | return $this->name; 56 | } 57 | 58 | public function __toString() 59 | { 60 | return sprintf('{%s}%s', $this->getNamespace(), $this->getName()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Util/String.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace BeSimple\SoapBundle\Util; 12 | 13 | /** 14 | * String provides utility methods for strings. 15 | * 16 | * @author Christian Kerl 17 | */ 18 | class String 19 | { 20 | /** 21 | * Checks if a string starts with a given string. 22 | * 23 | * @param string $str A string 24 | * @param string $substr A string to check against 25 | * 26 | * @return bool True if str starts with substr 27 | */ 28 | public static function startsWith($str, $substr) 29 | { 30 | if(is_string($str) && is_string($substr) && strlen($str) >= strlen($substr)) { 31 | return $substr == substr($str, 0, strlen($substr)); 32 | } 33 | } 34 | 35 | /** 36 | * Checks if a string ends with a given string. 37 | * 38 | * @param string $str A string 39 | * @param string $substr A string to check against 40 | * 41 | * @return bool True if str ends with substr 42 | */ 43 | public static function endsWith($str, $substr) 44 | { 45 | if(is_string($str) && is_string($substr) && strlen($str) >= strlen($substr)) { 46 | return $substr == substr($str, strlen($str) - strlen($substr)); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /WebServiceContext.php: -------------------------------------------------------------------------------- 1 | 6 | * (c) Francis Besset 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace BeSimple\SoapBundle; 13 | 14 | use BeSimple\SoapBundle\ServiceBinding\ServiceBinder; 15 | use BeSimple\SoapCommon\Converter\TypeConverterCollection; 16 | use BeSimple\SoapWsdl\Dumper\Dumper; 17 | use BeSimple\SoapServer\SoapServerBuilder; 18 | use Symfony\Component\Config\ConfigCache; 19 | use Symfony\Component\Config\Loader\LoaderInterface; 20 | 21 | /** 22 | * WebServiceContext. 23 | * 24 | * @author Christian Kerl 25 | * @author Francis Besset 26 | */ 27 | class WebServiceContext 28 | { 29 | private $options; 30 | 31 | private $serviceDefinition; 32 | private $serviceBinder; 33 | private $serverBuilder; 34 | 35 | public function __construct(LoaderInterface $loader, TypeConverterCollection $converters, array $options) 36 | { 37 | $this->loader = $loader; 38 | $this->converters = $converters; 39 | $this->options = $options; 40 | } 41 | 42 | public function getServiceDefinition() 43 | { 44 | if (null === $this->serviceDefinition) { 45 | $cache = new ConfigCache(sprintf('%s/%s.definition.php', $this->options['cache_dir'], $this->options['name']), $this->options['debug']); 46 | if ($cache->isFresh()) { 47 | $this->serviceDefinition = include (string) $cache; 48 | } else { 49 | if (!$this->loader->supports($this->options['resource'], $this->options['resource_type'])) { 50 | throw new \LogicException(sprintf('Cannot load "%s" (%s)', $this->options['resource'], $this->options['resource_type'])); 51 | } 52 | 53 | $this->serviceDefinition = $this->loader->load($this->options['resource'], $this->options['resource_type']); 54 | $this->serviceDefinition->setName($this->options['name']); 55 | $this->serviceDefinition->setNamespace($this->options['namespace']); 56 | 57 | $cache->write('serviceDefinition), true).');'); 58 | } 59 | } 60 | 61 | return $this->serviceDefinition; 62 | } 63 | 64 | public function getWsdlFileContent($endpoint = null) 65 | { 66 | return file_get_contents($this->getWsdlFile($endpoint)); 67 | } 68 | 69 | public function getWsdlFile($endpoint = null) 70 | { 71 | $file = sprintf ('%s/%s.%s.wsdl', $this->options['cache_dir'], $this->options['name'], md5($endpoint)); 72 | $cache = new ConfigCache($file, $this->options['debug']); 73 | 74 | if(!$cache->isFresh()) { 75 | $definition = $this->getServiceDefinition(); 76 | 77 | if ($endpoint) { 78 | $definition->setOption('location', $endpoint); 79 | } 80 | 81 | $dumper = new Dumper($definition, array('stylesheet' => $this->options['wsdl_stylesheet'])); 82 | $cache->write($dumper->dump()); 83 | } 84 | 85 | return (string) $cache; 86 | } 87 | 88 | public function getServiceBinder() 89 | { 90 | if (null === $this->serviceBinder) { 91 | $this->serviceBinder = new ServiceBinder( 92 | $this->getServiceDefinition(), 93 | new $this->options['binder_request_header_class'](), 94 | new $this->options['binder_request_class'](), 95 | new $this->options['binder_response_class']() 96 | ); 97 | } 98 | 99 | return $this->serviceBinder; 100 | } 101 | 102 | public function getServerBuilder() 103 | { 104 | if (null === $this->serverBuilder) { 105 | $this->serverBuilder = SoapServerBuilder::createWithDefaults() 106 | ->withWsdl($this->getWsdlFile()) 107 | ->withClassmap($this->getServiceDefinition()->getTypeRepository()->getClassmap()) 108 | ->withTypeConverters($this->converters) 109 | ; 110 | 111 | if (null !== $this->options['cache_type']) { 112 | $this->serverBuilder->withWsdlCache($this->options['cache_type']); 113 | } 114 | } 115 | 116 | return $this->serverBuilder; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "besimple/soap-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Build and consume SOAP and WSDL based web services with Symfony2", 5 | "keywords": [ "soap", "soap-bundle" ], 6 | "homepage": "https://github.com/BeSimple/BeSimpleSoapBundle", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Francis Besset", 11 | "email": "francis.besset@gmail.com" 12 | }, 13 | { 14 | "name": "Christian Kerl", 15 | "email": "christian-kerl@web.de" 16 | }, 17 | { 18 | "name": "Andreas Schamberger", 19 | "email": "mail@andreass.net" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=5.3.0", 24 | "ext-soap": "*", 25 | "besimple/soap-common": "0.3.*", 26 | "besimple/soap-wsdl": "0.3.*", 27 | "ass/xmlsecurity": "~1.0", 28 | "symfony/framework-bundle": "~2.6", 29 | "symfony/twig-bundle": "~2.6", 30 | "zendframework/zend-mime": "2.1.*" 31 | }, 32 | "suggest": { 33 | "besimple/soap-client": "0.3.*", 34 | "besimple/soap-server": "0.3.*" 35 | }, 36 | "autoload": { 37 | "psr-0": { "BeSimple\\SoapBundle": "" } 38 | }, 39 | "target-dir": "BeSimple/SoapBundle", 40 | "extra": { 41 | "branch-alias": { 42 | "dev-master": "0.3-dev" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | ./Tests 18 | 19 | 20 | 21 | 22 | 23 | ./ 24 | 25 | ./Tests/ 26 | ./vendor/ 27 | 28 | 29 | 30 | 31 | 32 | --------------------------------------------------------------------------------