From b0681e5fcfce6bffa99518e16357db9504df91d7 Mon Sep 17 00:00:00 2001 From: Christian Kerl Date: Tue, 5 Oct 2010 21:44:30 +0200 Subject: [PATCH] implemented basic SoapKernel to transform a SoapRequest to a SoapResponse --- DependencyInjection/WebServiceExtension.php | 27 +++- Resources/config/services.xml | 14 +- Soap/SoapRequest.php | 68 ++++++++++ Soap/SoapResponse.php | 48 +++++++ SoapKernel.php | 140 +++++++++++++++++++- SoapRequest.php | 14 -- Tests/SoapKernelTest.php | 40 ++++++ Util/OutputBuffer.php | 33 +++++ Util/String.php | 51 +++++++ WebServiceBundle.php | 10 +- 10 files changed, 414 insertions(+), 31 deletions(-) create mode 100644 Soap/SoapRequest.php create mode 100644 Soap/SoapResponse.php delete mode 100644 SoapRequest.php create mode 100644 Tests/SoapKernelTest.php create mode 100644 Util/OutputBuffer.php create mode 100644 Util/String.php diff --git a/DependencyInjection/WebServiceExtension.php b/DependencyInjection/WebServiceExtension.php index 96043fb..0fa0ea2 100644 --- a/DependencyInjection/WebServiceExtension.php +++ b/DependencyInjection/WebServiceExtension.php @@ -1,20 +1,35 @@ + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ namespace Bundle\WebServiceBundle\DependencyInjection; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\DependencyInjection\Extension\Extension; -use Symfony\Component\DependencyInjection\ContainerBuilder; - +/** + * WebServiceExtension. + * + * @author Christian Kerl + */ class WebServiceExtension extends Extension { public function configLoad(array $config, ContainerBuilder $configuration) { - $loader = new XmlFileLoader($configuration, __DIR__ . "/../Resources/config"); - $loader->load("services.xml"); + if(!$configuration->hasDefinition('webservice_http_kernel')) + { + $loader = new XmlFileLoader($configuration, __DIR__ . "/../Resources/config"); + $loader->load("services.xml"); - $configuration->setAlias("http_kernel", "webservice_http_kernel"); + $configuration->setAlias("http_kernel", "webservice_http_kernel"); + } } public function getXsdValidationBasePath() diff --git a/Resources/config/services.xml b/Resources/config/services.xml index be00ff3..64da6a4 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -8,14 +8,18 @@ - - - - - + + + + + + + + + \ No newline at end of file diff --git a/Soap/SoapRequest.php b/Soap/SoapRequest.php new file mode 100644 index 0000000..7c6b29e --- /dev/null +++ b/Soap/SoapRequest.php @@ -0,0 +1,68 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Bundle\WebServiceBundle\Soap; + +use Symfony\Component\HttpFoundation\Request; + +/** + * SoapRequest. + * + * @author Christian Kerl + */ +class SoapRequest extends Request +{ + /** + * @var string + */ + protected $rawContent; + + /** + * @var string + */ + protected $soapAction; + + /** + * @var unknown + */ + protected $soapHeader; + + /** + * @var unknown + */ + protected $soapArguments; + + public function __construct($rawContent = null, array $query = null, array $attributes = null, array $cookies = null, array $server = null) + { + parent::__construct($query, null, $attributes, $cookies, null, $server); + + $this->rawContent = $rawContent != null ? $rawContent : $this->loadRawContent(); + } + + /** + * Gets the SOAP XML content. + * + * @return string + */ + public function getRawContent() + { + return $this->rawContent; + } + + /** + * Loads the plain HTTP POST data. + * + * @return string + */ + protected function loadRawContent() + { + return isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : file_get_contents('php://input'); + } +} \ No newline at end of file diff --git a/Soap/SoapResponse.php b/Soap/SoapResponse.php new file mode 100644 index 0000000..3ef872d --- /dev/null +++ b/Soap/SoapResponse.php @@ -0,0 +1,48 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Bundle\WebServiceBundle\Soap; + +use Symfony\Component\HttpFoundation\Response; + +/** + * SoapResponse. + * + * @author Christian Kerl + */ +class SoapResponse extends Response +{ + protected $soapHeaders; + + protected $soapReturnValue; + + public function __construct($returnValue) + { + parent::__construct(); + + $this->soapHeaders = array(); + $this->soapReturnValue = $returnValue; + } + + public function addSoapHeader(\SoapHeader $header) + { + $this->soapHeaders[] = $header; + } + + public function getSoapHeaders() + { + return $this->soapHeaders; + } + + public function getReturnValue() + { + return $this->soapReturnValue; + } +} \ No newline at end of file diff --git a/SoapKernel.php b/SoapKernel.php index 3298b1f..08f88a9 100644 --- a/SoapKernel.php +++ b/SoapKernel.php @@ -1,23 +1,153 @@ + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ namespace Bundle\WebServiceBundle; use Symfony\Component\HttpFoundation\Request; - -use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Bundle\WebServiceBundle\Soap\SoapRequest; +use Bundle\WebServiceBundle\Soap\SoapResponse; -class SoapKernel extends ContainerAware implements HttpKernelInterface +use Bundle\WebServiceBundle\Util\OutputBuffer; +use Bundle\WebServiceBundle\Util\String; + +/** + * SoapKernel converts a SoapRequest to a SoapResponse. It uses PHP's SoapServer for SOAP message + * handling. The logic for every service method is implemented in a Symfony controller. The controller + * to use for a specific service method is defined in the ServiceDefinition. The controller is invoked + * by Symfony's HttpKernel implementation. + * + * @author Christian Kerl + */ +class SoapKernel implements HttpKernelInterface { + /** + * @var \SoapServer + */ + protected $soapServer; + + /** + * @var \Bundle\WebServiceBundle\Soap\SoapRequest + */ + protected $soapRequest; + + /** + * @var \Bundle\WebServiceBundle\Soap\SoapResponse + */ + protected $soapResponse; + + /** + * @var \Symfony\Component\HttpKernel\HttpKernelInterface + */ + protected $kernel; + + public function __construct(\SoapServer $server, HttpKernelInterface $kernel) + { + $this->soapServer = $server; + $this->soapServer->setObject($this); + + $this->kernel = $kernel; + } + public function getRequest() { - return null; + return $this->soapRequest; } public function handle(Request $request = null, $type = self::MASTER_REQUEST, $raw = false) { - $this->container->getSymfonyHttpKernelService()->handle($request, $type, $raw); + $this->soapRequest = $this->checkRequest($request); + + $this->soapResponse->setContent(OutputBuffer::get( + function() use($this) + { + $this->soapServer->handle($this->soapRequest->getRawContent()); + } + )); + + return $this->soapResponse; + } + + public function __call($method, $arguments) + { + if($this->isSoapHeaderCallback($method)) + { + // $this->soapRequest->addSoapHeader(null); + } + else + { + // TODO: set _controller attribute of request + $this->soapRequest->attributes->set('_controller', $method); + + $response = $this->kernel->handle($this->soapRequest, self::MASTER_REQUEST, true); + + $this->soapResponse = $this->checkResponse($response); + + foreach($this->soapResponse->getSoapHeaders() as $header) + { + $this->soapServer->addSoapHeader($header); + } + + return $this->soapResponse->getReturnValue(); + } + } + + /** + * Checks the given Request, that it is a SoapRequest. If the request is null a new + * SoapRequest is created. + * + * @param Request $request A request to check + * + * @return SoapRequest A valid SoapRequest + * + * @throws InvalidArgumentException if the given Request is not a SoapRequest + */ + protected function checkRequest(Request $request) + { + if($request == null) + { + $request = new SoapRequest(); + } + + if(!is_a($request, __NAMESPACE__ . '\\Soap\\SoapRequest')) + { + throw new InvalidArgumentException(); + } + + return $request; + } + + /** + * Checks the given Response, that it is a SoapResponse. + * + * @param Response $response A response to check + * + * @return SoapResponse A valid SoapResponse + * + * @throws InvalidArgumentException if the given Response is null or not a SoapResponse + */ + protected function checkResponse(Response $response) + { + if($response == null || !is_a($request, __NAMESPACE__ . '\\Soap\\SoapResponse')) + { + throw new InvalidArgumentException(); + } + + return $response; + } + + protected function isSoapHeaderCallback($method) + { + return false; //String::endsWith($method, 'Header'); } } \ No newline at end of file diff --git a/SoapRequest.php b/SoapRequest.php deleted file mode 100644 index 5424cd8..0000000 --- a/SoapRequest.php +++ /dev/null @@ -1,14 +0,0 @@ - + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Bundle\WebServiceBundle\Tests; + +use Symfony\Component\HttpFoundation\Request; + +use Bundle\WebServiceBundle\SoapKernel; + +/** + * UnitTest for \Bundle\WebServiceBundle\SoapKernel. + * + * @author Christian Kerl + */ +class SoapKernelTest extends \PHPUnit_Framework_TestCase +{ + private $soapKernel; + + public function setUp() + { + $soapServer = new \SoapServer(); + + $this->soapKernel = new SoapKernel($soapServer, null); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidRequest() + { + $this->soapKernel->handle(new Request()); + } +} \ No newline at end of file diff --git a/Util/OutputBuffer.php b/Util/OutputBuffer.php new file mode 100644 index 0000000..d40ccc6 --- /dev/null +++ b/Util/OutputBuffer.php @@ -0,0 +1,33 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Bundle\WebServiceBundle\Util; + +/** + * OutputBuffer provides utility methods to work with PHP's output buffer. + * + * @author Christian Kerl + */ +class OutputBuffer +{ + /** + * Gets the output created by the given callable. + * + * @param callable $callback A callable to execute + * + * @return string The output + */ + static public function get($callback) + { + ob_start(); + $callback(); + return ob_get_clean(); + } +} \ No newline at end of file diff --git a/Util/String.php b/Util/String.php new file mode 100644 index 0000000..1c641d5 --- /dev/null +++ b/Util/String.php @@ -0,0 +1,51 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Bundle\WebServiceBundle\Util; + +/** + * String provides utility methods for strings. + * + * @author Christian Kerl + */ +class String +{ + /** + * Checks if a string starts with a given string. + * + * @param string $str A string + * @param string $substr A string to check against + * + * @return bool True if str starts with substr + */ + public static function startsWith($str, $substr) + { + if(is_string($str) && is_string($substr) && strlen($str) >= strlen($substr)) + { + return $substr == substr($str, 0, strlen($substr)); + } + } + + /** + * Checks if a string ends with a given string. + * + * @param string $str A string + * @param string $substr A string to check against + * + * @return bool True if str ends with substr + */ + public static function endsWith($str, $substr) + { + if(is_string($str) && is_string($substr) && strlen($str) >= strlen($substr)) + { + return $substr == substr($str, strlen($str) - strlen($substr)); + } + } +} \ No newline at end of file diff --git a/WebServiceBundle.php b/WebServiceBundle.php index a34e543..2ea3bb6 100644 --- a/WebServiceBundle.php +++ b/WebServiceBundle.php @@ -1,4 +1,12 @@ + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ namespace Bundle\WebServiceBundle; @@ -9,7 +17,7 @@ use Symfony\Component\HttpKernel\Bundle\Bundle; /** * WebServiceBundle. * - * @author Christian Kerl + * @author Christian Kerl */ class WebServiceBundle extends Bundle {