Updated definition of ComplexType and use classmap option of SoapServer

Please to refer to the documentation for the changes:
http://besim.pl/SoapBundle/tutorial/complex_type.html
This commit is contained in:
Francis Besset 2011-08-24 23:36:49 +02:00
parent 51a36dfb87
commit c154463b33
12 changed files with 117 additions and 291 deletions

View File

@ -54,9 +54,12 @@ class SoapWebServiceController extends ContainerAware
public function callAction($webservice)
{
$webServiceContext = $this->getWebServiceContext($webservice);
$this->serviceBinder = $webServiceContext->getServiceBinder();
//$this->serviceBinder->fixXmlRequestContent();
$this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request'));
//$this->soapRequest->setContent($this->serviceBinder->fixXmlRequestContent($this->soapRequest->getContent()));
$this->soapServer = $webServiceContext->getServerFactory()->create($this->soapRequest, $this->soapResponse);
$this->soapServer->setObject($this);

View File

@ -23,14 +23,13 @@ Controller
/**
* @Soap\Method("getUser")
* @Soap\Param("name", phpType = "string")
*
* Specify \My\App\Entity\User phpType
* Warning: Do not forget the first backslash
* @Soap\Result(phpType = "\My\App\Entity\User")
* @Soap\Result(phpType = "My\App\Entity\User")
*/
public function getUserAction($name)
{
$user = $this->container->getDoctrine()->getRepository('MyApp:User')->findOneByName($name);
$user = $this->container->getDoctrine()->getRepository('MyApp:User')->findOneBy(array(
'name' => $name,
);
if (!$user) {
throw new \SoapFault('USER_NOT_FOUND', sprintf('The user with the name "%s" can not be found', $name));
@ -43,7 +42,7 @@ Controller
User class
----------
You can expose public property and public method (getter and setter).
You can expose only the properties (public, protected or private) of a complex type.
.. code-block:: php
@ -54,67 +53,34 @@ You can expose public property and public method (getter and setter).
class User
{
/**
* @Soap\PropertyComplexType("string")
* @Soap\ComplexType("string")
*/
public $firstname;
/**
* @Soap\PropertyComplexType("string")
* @Soap\ComplexType("string")
*/
public $lastname;
/**
* @Soap\ComplexType("int", nillable=true)
*/
private $id;
/**
* @Soap\ComplexType("string")
*/
private $username;
/**
* @Soap\ComplexType("string")
*/
private $email;
/**
* @Soap\MethodComplexType("int", name="user_id", nillable=true)
*/
public function getId()
{
return $this->id;
}
/**
* @Soap\MethodComplexType("string", setter="setUsername")
*/
public function getUsername()
{
return $this->username;
}
ComplexType
-----------
/**
* @Soap\MethodComplexType("string", setter="setEmail")
*/
public function getEmail()
{
return $this->email;
}
`ComplexType` accepts the following options:
public function setUsername($username)
{
$this->username = $username;
}
public function setEmail($email)
{
$this->email = $email;
}
}
PropertyComplexType
-------------------
`PropertyComplexType` accepts the following options:
* **name**: To override the original name of the property
* **nillable**: To specify that the value can be null
MethodComplexType
-------------------
`MethodComplexType` accepts the following options:
* **name**: To override the original name of the property
* **nillable**: To specify that the value can be null
* **setter**: The set method name value. *Mandatory if the complex type is passed as a parameter to a function.*
* nillable: To specify that the value can be null

View File

@ -14,6 +14,8 @@ use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
use Zend\Soap\Wsdl;
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
@ -21,15 +23,18 @@ use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
class RpcLiteralRequestMessageBinder implements MessageBinderInterface
{
private $messageRefs = array();
private $definitionComplexTypes;
public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
{
$this->definitionComplexTypes = $definitionComplexTypes;
$result = array();
$i = 0;
foreach ($messageDefinition->getArguments() as $argument) {
if (isset($message[$i])) {
$result[$argument->getName()] = $this->processType($argument->getType()->getPhpType(), $message[$i], $definitionComplexTypes);
$result[$argument->getName()] = $this->processType($argument->getType()->getPhpType(), $message[$i]);
}
$i++;
@ -38,27 +43,27 @@ class RpcLiteralRequestMessageBinder implements MessageBinderInterface
return $result;
}
protected function processType($phpType, $message, array $definitionComplexTypes)
protected function processType($phpType, $message)
{
$isArray = false;
if (preg_match('/^([^\[]+)\[\]$/', $phpType, $match)) {
$isArray = true;
$type = $match[1];
} else {
$isArray = false;
$type = $phpType;
$phpType = $match[1];
}
if (isset($definitionComplexTypes[$type])) {
// @TODO Fix array reference
if (isset($this->definitionComplexTypes[$phpType])) {
if ($isArray) {
$array = array();
foreach ($message->item as $complexType) {
$array[] = $this->getInstanceOfType($type, $complexType, $definitionComplexTypes);
$array[] = $this->checkComplexType($phpType, $complexType);
}
$message = $array;
} else {
$message = $this->getInstanceOfType($type, $message, $definitionComplexTypes);
$message = $this->checkComplexType($phpType, $message);
}
} elseif ($isArray) {
$message = $message->item;
@ -67,36 +72,30 @@ class RpcLiteralRequestMessageBinder implements MessageBinderInterface
return $message;
}
private function getInstanceOfType($phpType, $message, array $definitionComplexTypes)
protected function checkComplexType($phpType, $message)
{
$hash = spl_object_hash($message);
if (isset($this->messageRefs[$hash])) {
return $this->messageRefs[$hash];
return $message;
}
$this->messageRefs[$hash] =
$instanceType = new $phpType();
foreach ($definitionComplexTypes[$phpType] as $type) {
$value = $this->processType($type->getValue(), $message->{$type->getName()}, $definitionComplexTypes);
if (null === $value && $type->isNillable()) {
continue;
}
if ($type instanceof PropertyComplexType) {
$instanceType->{$type->getOriginalName()} = $value;
} elseif ($type instanceof MethodComplexType) {
if (!$type->getSetter()) {
throw new \LogicException(sprintf('"setter" option must be specified to hydrate "%s::%s()"', $phpType, $type->getOriginalName()));
}
$instanceType->{$type->getSetter()}($value);
$r = new \ReflectionClass($message);
foreach ($this->definitionComplexTypes[$phpType] as $type) {
$p = $r->getProperty($type->getName());
if ($p->isPublic()) {
$value = $message->{$type->getName()};
} else {
throw new \InvalidArgumentException();
$p->setAccessible(true);
$value = $p->getValue($message);
}
$value = $this->processType($type->getValue(), $value);
if (!$type->isNillable() && null === $value) {
throw new \SoapFault('SOAP_ERROR_COMPLEX_TYPE', sprintf('"%s:%s" cannot be null.', ucfirst(Wsdl::translateType($phpType)), $type->getName()));
}
}
return $instanceType;
return $this->messageRefs[$hash] = $message;
}
}

View File

@ -14,6 +14,8 @@ use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
use Zend\Soap\Wsdl;
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
@ -21,45 +23,42 @@ use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
class RpcLiteralResponseMessageBinder implements MessageBinderInterface
{
private $messageRefs = array();
private $definitionComplexTypes;
public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
{
$return = $messageDefinition->getReturn();
$class = $return->getPhpType();
$this->definitionComplexTypes = $definitionComplexTypes;
$message = $this->processType($messageDefinition->getReturn()->getPhpType(), $message, $definitionComplexTypes);
return $message;
return $this->processType($messageDefinition->getReturn()->getPhpType(), $message);
}
private function processType($phpType, $message, array $definitionComplexTypes)
private function processType($phpType, $message)
{
$isArray = false;
if (preg_match('/^([^\[]+)\[\]$/', $phpType, $match)) {
$isArray = true;
$type = $match[1];
} else {
$isArray = false;
$type = $phpType;
$phpType = $match[1];
}
if (isset($definitionComplexTypes[$type])) {
if (isset($this->definitionComplexTypes[$phpType])) {
if ($isArray) {
$array = array();
foreach ($message as $complexType) {
$array[] = $this->getInstanceOfStdClass($type, $complexType, $definitionComplexTypes);
$array[] = $this->checkComplexType($phpType, $complexType);
}
$message = $array;
} else {
$message = $this->getInstanceOfStdClass($type, $message, $definitionComplexTypes);
$message = $this->checkComplexType($phpType, $message);
}
}
return $message;
}
private function getInstanceOfStdClass($phpType, $message, $definitionComplexTypes)
private function checkComplexType($phpType, $message)
{
$hash = spl_object_hash($message);
if (isset($this->messageRefs[$hash])) {
@ -71,26 +70,27 @@ class RpcLiteralResponseMessageBinder implements MessageBinderInterface
$class = substr($class, 1);
}
if (get_class($message) !== $class) {
throw new \InvalidArgumentException();
if (!$message instanceof $class) {
throw new \InvalidArgumentException(sprintf('The instance class must be "%s", "%s" given.', get_class($message), $class));
}
$stdClass = new \stdClass();
$this->messageRefs[$hash] = $stdClass;
foreach ($definitionComplexTypes[$phpType] as $type) {
if ($type instanceof PropertyComplexType) {
$value = $message->{$type->getOriginalName()};
} elseif ($type instanceof MethodComplexType) {
$value = $message->{$type->getOriginalName()}();
$r = new \ReflectionClass($message);
foreach ($this->definitionComplexTypes[$class] as $type) {
$p = $r->getProperty($type->getName());
if ($p->isPublic()) {
$value = $message->{$type->getName()};
} else {
throw new \InvalidArgumentException();
$p->setAccessible(true);
$value = $p->getValue($message);
}
$stdClass->{$type->getName()} = $this->processType($type->getValue(), $value, $definitionComplexTypes);
$value = $this->processType($type->getValue(), $value);
if (!$type->isNillable() && null === $value) {
throw new \InvalidArgumentException(sprintf('"%s::%s" cannot be null.', $class, $type->getName()));
}
}
return $stdClass;
return $this->messageRefs[$hash] = $message;
}
}

View File

@ -13,7 +13,7 @@ namespace BeSimple\SoapBundle\ServiceDefinition\Annotation;
/**
* @Annotation
*/
class PropertyComplexType extends Configuration
class ComplexType extends Configuration
{
private $name;
private $value;
@ -51,6 +51,6 @@ class PropertyComplexType extends Configuration
public function getAliasName()
{
return 'propertycomplextype';
return 'complextype';
}
}

View File

@ -1,67 +0,0 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\ServiceDefinition\Annotation;
/**
* @Annotation
*/
class MethodComplexType extends Configuration
{
private $name;
private $value;
private $setter;
private $isNillable = false;
public function getName()
{
return $this->name;
}
public function getValue()
{
return $this->value;
}
public function getSetter()
{
return $this->setter;
}
public function isNillable()
{
return $this->isNillable;
}
public function setName($name)
{
$this->name = $name;
}
public function setValue($value)
{
$this->value = $value;
}
public function setSetter($setter)
{
$this->setter = $setter;
}
public function setNillable($isNillable)
{
$this->isNillable = (bool) $isNillable;
}
public function getAliasName()
{
return 'methodcomplextype';
}
}

View File

@ -8,15 +8,14 @@
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\ServiceDefinition\Strategy;
namespace BeSimple\SoapBundle\ServiceDefinition;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
abstract class BaseComplexType
class ComplexType
{
private $name;
private $originalName;
private $value;
private $isNillable = false;
@ -25,11 +24,6 @@ abstract class BaseComplexType
return $this->name;
}
public function getOriginalName()
{
return $this->originalName ?: $this->name;
}
public function getValue()
{
return $this->value;
@ -45,11 +39,6 @@ abstract class BaseComplexType
$this->name = $name;
}
public function setOriginalName($originalName)
{
$this->originalName = $originalName;
}
public function setValue($value)
{
$this->value = $value;

View File

@ -10,9 +10,7 @@
namespace BeSimple\SoapBundle\ServiceDefinition\Loader;
use BeSimple\SoapBundle\ServiceDefinition\Annotation\ComplexType;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
use BeSimple\SoapBundle\ServiceDefinition\ComplexType;
use BeSimple\SoapBundle\Util\Collection;
/**
@ -24,8 +22,7 @@ use BeSimple\SoapBundle\Util\Collection;
*/
class AnnotationComplexTypeLoader extends AnnotationClassLoader
{
private $propertyComplexTypeClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\PropertyComplexType';
private $methodComplexTypeClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\MethodComplexType';
private $complexTypeClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\ComplexType';
/**
* Loads a ServiceDefinition from annotations from a class.
@ -44,50 +41,19 @@ class AnnotationComplexTypeLoader extends AnnotationClassLoader
}
$class = new \ReflectionClass($class);
$collection = new Collection('getName');
$collection = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\ComplexType');
foreach ($class->getProperties() as $property) {
if ($property->isPublic()) {
$complexType = $this->reader->getPropertyAnnotation($property, $this->propertyComplexTypeClass);
$complexType = $this->reader->getPropertyAnnotation($property, $this->complexTypeClass);
if ($complexType) {
$propertyComplexType = new PropertyComplexType();
$propertyComplexType = new ComplexType();
$propertyComplexType->setValue($complexType->getValue());
$propertyComplexType->setNillable($complexType->isNillable());
if (!$complexType->getName()) {
$propertyComplexType->setName($property->getName());
} else {
$propertyComplexType->setName($complexType->getName());
$propertyComplexType->setOriginalName($property->getName());
}
$collection->add($propertyComplexType);
}
}
}
foreach ($class->getMethods() as $method) {
if ($method->isPublic()) {
$complexType = $this->reader->getMethodAnnotation($method, $this->methodComplexTypeClass);
if ($complexType) {
$methodComplexType = new MethodComplexType();
$methodComplexType->setValue($complexType->getValue());
$methodComplexType->setSetter($complexType->getSetter());
$methodComplexType->setNillable($complexType->isNillable());
if (!$complexType->getName()) {
$methodComplexType->setName($property->getName());
} else {
$methodComplexType->setName($complexType->getName());
$methodComplexType->setOriginalName($method->getName());
}
$collection->add($methodComplexType);
}
}
}
return $collection;
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\ServiceDefinition\Strategy;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class MethodComplexType extends BaseComplexType
{
private $setter;
public function getSetter()
{
return $this->setter;
}
public function setSetter($setter)
{
$this->setter = $setter;
}
}

View File

@ -1,18 +0,0 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\ServiceDefinition\Strategy;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class PropertyComplexType extends BaseComplexType
{
}

View File

@ -11,6 +11,7 @@
namespace BeSimple\SoapBundle\Soap;
use BeSimple\SoapBundle\Converter\ConverterRepository;
use Zend\Soap\Wsdl;
/**
* @author Christian Kerl <christian-kerl@web.de>
@ -25,7 +26,7 @@ class SoapServerFactory
public function __construct($wsdlFile, array $classmap, ConverterRepository $converters, $debug = false)
{
$this->wsdlFile = $wsdlFile;
$this->classmap = $classmap;
$this->classmap = $this->fixSoapServerClassmap($classmap);
$this->converters = $converters;
$this->debug = $debug;
}
@ -62,4 +63,15 @@ class SoapServerFactory
return $typemap;
}
private function fixSoapServerClassmap($classmap)
{
$classmapFixed = array();
foreach ($classmap as $class => $definition) {
$classmapFixed[Wsdl::translateType($class)] = $class;
}
return $classmapFixed;
}
}

View File

@ -99,7 +99,12 @@ class WebServiceContext
public function getServerFactory()
{
if (null === $this->serverFactory) {
$this->serverFactory = new SoapServerFactory($this->getWsdlFile(), array(), $this->converterRepository, $this->options['debug']);
$this->serverFactory = new SoapServerFactory(
$this->getWsdlFile(),
$this->serviceDefinition->getDefinitionComplexTypes(),
$this->converterRepository,
$this->options['debug']
);
}
return $this->serverFactory;