From c154463b33a0391937a2c586892f6faf20a4c47f Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Wed, 24 Aug 2011 23:36:49 +0200 Subject: [PATCH] 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 --- Controller/SoapWebServiceController.php | 5 +- Resources/doc/tutorial/complex_type.rst | 78 ++++++------------- .../RpcLiteralRequestMessageBinder.php | 61 +++++++-------- .../RpcLiteralResponseMessageBinder.php | 56 ++++++------- ...ropertyComplexType.php => ComplexType.php} | 4 +- .../Annotation/MethodComplexType.php | 67 ---------------- .../BaseComplexType.php => ComplexType.php} | 15 +--- .../Loader/AnnotationComplexTypeLoader.php | 54 +++---------- .../Strategy/MethodComplexType.php | 29 ------- .../Strategy/PropertyComplexType.php | 18 ----- Soap/SoapServerFactory.php | 14 +++- WebServiceContext.php | 7 +- 12 files changed, 117 insertions(+), 291 deletions(-) rename ServiceDefinition/Annotation/{PropertyComplexType.php => ComplexType.php} (91%) delete mode 100644 ServiceDefinition/Annotation/MethodComplexType.php rename ServiceDefinition/{Strategy/BaseComplexType.php => ComplexType.php} (72%) delete mode 100644 ServiceDefinition/Strategy/MethodComplexType.php delete mode 100644 ServiceDefinition/Strategy/PropertyComplexType.php diff --git a/Controller/SoapWebServiceController.php b/Controller/SoapWebServiceController.php index 0a57bfb..4d6f315 100644 --- a/Controller/SoapWebServiceController.php +++ b/Controller/SoapWebServiceController.php @@ -53,10 +53,13 @@ class SoapWebServiceController extends ContainerAware */ public function callAction($webservice) { - $webServiceContext = $this->getWebServiceContext($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); diff --git a/Resources/doc/tutorial/complex_type.rst b/Resources/doc/tutorial/complex_type.rst index de60eb8..edf03cd 100644 --- a/Resources/doc/tutorial/complex_type.rst +++ b/Resources/doc/tutorial/complex_type.rst @@ -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; - } - - /** - * @Soap\MethodComplexType("string", setter="setEmail") - */ - public function getEmail() - { - return $this->email; - } - - public function setUsername($username) - { - $this->username = $username; - } - - public function setEmail($email) - { - $this->email = $email; - } } -PropertyComplexType -------------------- +ComplexType +----------- -`PropertyComplexType` accepts the following options: +`ComplexType` 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.* \ No newline at end of file + * nillable: To specify that the value can be null \ No newline at end of file diff --git a/ServiceBinding/RpcLiteralRequestMessageBinder.php b/ServiceBinding/RpcLiteralRequestMessageBinder.php index 4d215b4..557ff4b 100644 --- a/ServiceBinding/RpcLiteralRequestMessageBinder.php +++ b/ServiceBinding/RpcLiteralRequestMessageBinder.php @@ -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 * @author Francis Besset @@ -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; } } \ No newline at end of file diff --git a/ServiceBinding/RpcLiteralResponseMessageBinder.php b/ServiceBinding/RpcLiteralResponseMessageBinder.php index fcf5f77..2a9c8cb 100644 --- a/ServiceBinding/RpcLiteralResponseMessageBinder.php +++ b/ServiceBinding/RpcLiteralResponseMessageBinder.php @@ -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 * @author Francis Besset @@ -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; } } \ No newline at end of file diff --git a/ServiceDefinition/Annotation/PropertyComplexType.php b/ServiceDefinition/Annotation/ComplexType.php similarity index 91% rename from ServiceDefinition/Annotation/PropertyComplexType.php rename to ServiceDefinition/Annotation/ComplexType.php index 6f1f046..85072d4 100644 --- a/ServiceDefinition/Annotation/PropertyComplexType.php +++ b/ServiceDefinition/Annotation/ComplexType.php @@ -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'; } } \ No newline at end of file diff --git a/ServiceDefinition/Annotation/MethodComplexType.php b/ServiceDefinition/Annotation/MethodComplexType.php deleted file mode 100644 index cf1cdaf..0000000 --- a/ServiceDefinition/Annotation/MethodComplexType.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * 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'; - } -} \ No newline at end of file diff --git a/ServiceDefinition/Strategy/BaseComplexType.php b/ServiceDefinition/ComplexType.php similarity index 72% rename from ServiceDefinition/Strategy/BaseComplexType.php rename to ServiceDefinition/ComplexType.php index 917fb57..2d78bbe 100644 --- a/ServiceDefinition/Strategy/BaseComplexType.php +++ b/ServiceDefinition/ComplexType.php @@ -8,15 +8,14 @@ * with this source code in the file LICENSE. */ -namespace BeSimple\SoapBundle\ServiceDefinition\Strategy; +namespace BeSimple\SoapBundle\ServiceDefinition; /** * @author Francis Besset */ -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; diff --git a/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php b/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php index 21c9085..0cc7579 100644 --- a/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php +++ b/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php @@ -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,48 +41,17 @@ 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->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); - } + if ($complexType) { + $propertyComplexType = new ComplexType(); + $propertyComplexType->setValue($complexType->getValue()); + $propertyComplexType->setNillable($complexType->isNillable()); + $propertyComplexType->setName($property->getName()); + $collection->add($propertyComplexType); } } diff --git a/ServiceDefinition/Strategy/MethodComplexType.php b/ServiceDefinition/Strategy/MethodComplexType.php deleted file mode 100644 index 7205ab5..0000000 --- a/ServiceDefinition/Strategy/MethodComplexType.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * 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 - */ -class MethodComplexType extends BaseComplexType -{ - private $setter; - - public function getSetter() - { - return $this->setter; - } - - public function setSetter($setter) - { - $this->setter = $setter; - } -} \ No newline at end of file diff --git a/ServiceDefinition/Strategy/PropertyComplexType.php b/ServiceDefinition/Strategy/PropertyComplexType.php deleted file mode 100644 index 7545f0e..0000000 --- a/ServiceDefinition/Strategy/PropertyComplexType.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * 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 - */ -class PropertyComplexType extends BaseComplexType -{ -} \ No newline at end of file diff --git a/Soap/SoapServerFactory.php b/Soap/SoapServerFactory.php index 1c83499..1e18767 100644 --- a/Soap/SoapServerFactory.php +++ b/Soap/SoapServerFactory.php @@ -11,6 +11,7 @@ namespace BeSimple\SoapBundle\Soap; use BeSimple\SoapBundle\Converter\ConverterRepository; +use Zend\Soap\Wsdl; /** * @author Christian Kerl @@ -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; + } } \ No newline at end of file diff --git a/WebServiceContext.php b/WebServiceContext.php index 4728d1b..ccf1ad9 100644 --- a/WebServiceContext.php +++ b/WebServiceContext.php @@ -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;