diff --git a/Controller/SoapWebServiceController.php b/Controller/SoapWebServiceController.php
index 90eede1..1816377 100644
--- a/Controller/SoapWebServiceController.php
+++ b/Controller/SoapWebServiceController.php
@@ -53,6 +53,7 @@ class SoapWebServiceController extends ContainerAware
$this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request'));
$this->soapServer = $webServiceContext->getServerFactory()->create($this->soapRequest, $this->soapResponse);
+
$this->soapServer->setObject($this);
ob_start();
diff --git a/Resources/config/loaders.xml b/Resources/config/loaders.xml
index 51c4e10..4347f94 100644
--- a/Resources/config/loaders.xml
+++ b/Resources/config/loaders.xml
@@ -10,6 +10,7 @@
BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationDirectoryLoader
BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationFileLoader
BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationClassLoader
+ BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader
@@ -29,6 +30,10 @@
+
+
+
+
diff --git a/Resources/config/webservice.xml b/Resources/config/webservice.xml
index f43610b..3dfa981 100644
--- a/Resources/config/webservice.xml
+++ b/Resources/config/webservice.xml
@@ -50,7 +50,9 @@
-
+
+
+
diff --git a/Resources/doc/_static/.gitkeep b/Resources/doc/_static/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Resources/doc/conf.py b/Resources/doc/conf.py
new file mode 100644
index 0000000..fcbd938
--- /dev/null
+++ b/Resources/doc/conf.py
@@ -0,0 +1,226 @@
+# -*- coding: utf-8 -*-
+#
+# BeSimpleSoapBundle documentation build configuration file, created by
+# sphinx-quickstart on Mon Aug 1 22:24:10 2011.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+from sphinx.highlighting import lexers
+from pygments.lexers.web import PhpLexer
+lexers['php'] = PhpLexer(startinline=True)
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+sys.path.append(os.path.abspath('_exts'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'BeSimpleSoapBundle'
+copyright = u'2011, Christian Kerl, Francis Besset'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.0.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.0.0-DEV'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+#pygments_style = 'sphinx'
+pygments_style = 'perldoc'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'BeSimpleSoapBundledoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'BeSimpleSoapBundle.tex', u'BeSimpleSoapBundle Documentation',
+ u'Christian Kerl, Francis Besset', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'besimplesoapbundle', u'BeSimpleSoapBundle Documentation',
+ [u'Christian Kerl, Francis Besset'], 1)
+]
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'http://docs.python.org/': None}
diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst
new file mode 100644
index 0000000..07ac795
--- /dev/null
+++ b/Resources/doc/index.rst
@@ -0,0 +1,27 @@
+BeSimpleSoapBundle
+==================
+
+The BeSimpleSoapBundle is a Symfony2 bundle to build WSDL and SOAP based web services.
+It is based on the `ckWebServicePlugin`_ for symfony.
+
+Reference Guide
+---------------
+
+.. toctree::
+ :maxdepth: 1
+ :numbered:
+
+ reference/installation
+ reference/configuration
+
+Tutorial
+--------
+
+.. toctree::
+ :maxdepth: 1
+ :numbered:
+
+ tutorial/array
+ tutorial/complex_type
+
+.. _`ckWebServicePlugin`: http://www.symfony-project.org/plugins/ckWebServicePlugin
\ No newline at end of file
diff --git a/Resources/doc/reference/configuration.rst b/Resources/doc/reference/configuration.rst
new file mode 100644
index 0000000..4c2d469
--- /dev/null
+++ b/Resources/doc/reference/configuration.rst
@@ -0,0 +1,51 @@
+Configuration
+=============
+
+Routing
+-------
+
+Include the `BeSimpleSoapBundle`'s routing configuration in your routing file (you can choose the prefix arbitrarily):
+
+.. code-block:: yaml
+
+ # app/config/routing.yml
+ _besimple_soap:
+ resource: "@BeSimpleSoapBundle/Resources/config/routing/webservicecontroller.xml"
+ prefix: /ws
+
+Config
+------
+
+Configure your first web service in your config file:
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_soap:
+ services:
+ DemoApi:
+ namespace: http://mysymfonyapp.com/ws/DemoApi/1.0/
+ binding: rpc-literal
+ resource: "@AcmeDemoBundle/Controller/DemoController.php"
+ resource_type: annotation
+
+Annotations for Controllers
+---------------------------
+
+.. code-block:: php
+
+ use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
+ use BeSimple\SoapBundle\Soap\SoapResponse;
+
+ class DemoController extends Controller
+ {
+ /**
+ * @Soap\Method("Hello")
+ * @Soap\Param("name", phpType = "string")
+ * @Soap\Result(phpType = "string")
+ */
+ public function helloAction($name)
+ {
+ return new SoapResponse(sprintf('Hello %s!', $name));
+ }
+ }
\ No newline at end of file
diff --git a/Resources/doc/reference/installation.rst b/Resources/doc/reference/installation.rst
new file mode 100644
index 0000000..9711348
--- /dev/null
+++ b/Resources/doc/reference/installation.rst
@@ -0,0 +1,70 @@
+Installation
+============
+
+Requirements
+------------
+
+Install and enable PHP's SOAP extension
+Download `Zend\\Soap`_ and `Zend\\Mime`_ or add in `deps` file
+
+.. code-block:: ini
+
+ ; deps file
+ [Zend\Soap]
+ git=http://github.com/BeSimple/zend-soap.git
+ target=/zend-framework/library/Zend/Soap
+
+ [Zend\Mime]
+ git=http://github.com/BeSimple/zend-mime.git
+ target=/zend-framework/library/Zend/Mime
+
+Add `Zend` library in autoload.php
+
+.. code-block:: php
+
+ // app/autoload.php
+ $loader->registerNamespaces(array(
+ 'Zend' => __DIR__.'/../vendor/zend-framework/library',
+ // your other namespaces
+ ));
+
+Installation
+------------
+
+`Download`_ the bundle or add in `deps` file
+
+.. code-block:: ini
+
+ ; deps file
+ [BeSimpleSoapBundle]
+ git=http://github.com/BeSimple/BeSimpleSoapBundle.git
+ target=/bundles/BeSimple/SoapBundle
+
+Add `BeSimple` in autoload.php
+
+.. code-block:: php
+
+ // app/autoload.php
+ $loader->registerNamespaces(array(
+ 'BeSimple' => __DIR__.'/../vendor/bundles',
+ // your other namespaces
+ ));
+
+Add `BeSimpleSoapBundle` in your Kernel class
+
+.. code-block:: php
+
+ // app/AppKernel.php
+ public function registerBundles()
+ {
+ return array(
+ // ...
+ new new BeSimple\SoapBundle\BeSimpleSoapBundle(),
+ // ...
+ );
+ }
+
+
+.. _`Zend\\Soap`: http://github.com/BeSimple/zend-soap
+.. _`Zend\\Mime`: http://github.com/BeSimple/zend-mime
+.. _`Download`: http://github.com/BeSimple/BeSimpleSoapBundle
diff --git a/Resources/doc/tutorial/array.rst b/Resources/doc/tutorial/array.rst
new file mode 100644
index 0000000..32b28ff
--- /dev/null
+++ b/Resources/doc/tutorial/array.rst
@@ -0,0 +1,26 @@
+Array
+=====
+
+Controller
+----------
+
+.. code-block:: php
+
+ namespace My\App\Controller;
+
+ use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
+ use BeSimple\SoapBundle\Soap\SoapResponse;
+ use Symfony\Component\DependencyInjection\ContainerAware;
+
+ class DemoController extends ContainerAware
+ {
+ /**
+ * @Soap\Method("isString")
+ * @Soap\Param("strings", phpType = "string[]")
+ * @Soap\Result(phpType = "boolean")
+ */
+ public function helloAction(array $strings)
+ {
+ return new SoapResponse(true);
+ }
+ }
\ No newline at end of file
diff --git a/Resources/doc/tutorial/complex_type.rst b/Resources/doc/tutorial/complex_type.rst
new file mode 100644
index 0000000..de60eb8
--- /dev/null
+++ b/Resources/doc/tutorial/complex_type.rst
@@ -0,0 +1,120 @@
+Complex Type
+============
+
+This tutorial explains how to do to return a complex type.
+
+If your SOAP function takes a complex type as input, this tutorial is
+valid. You'll just have to adapt the input parameters of your method.
+
+
+Controller
+----------
+
+.. code-block:: php
+
+ namespace My\App\Controller;
+
+ use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
+ use BeSimple\SoapBundle\Soap\SoapResponse;
+ use Symfony\Component\DependencyInjection\ContainerAware;
+
+ class DemoController extends ContainerAware
+ {
+ /**
+ * @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")
+ */
+ public function getUserAction($name)
+ {
+ $user = $this->container->getDoctrine()->getRepository('MyApp:User')->findOneByName($name);
+
+ if (!$user) {
+ throw new \SoapFault('USER_NOT_FOUND', sprintf('The user with the name "%s" can not be found', $name));
+ }
+
+ return new SoapResponse($user);
+ }
+ }
+
+User class
+----------
+
+You can expose public property and public method (getter and setter).
+
+.. code-block:: php
+
+ namespace My\App\Entity;
+
+ use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
+
+ class User
+ {
+ /**
+ * @Soap\PropertyComplexType("string")
+ */
+ public $firstname;
+
+ /**
+ * @Soap\PropertyComplexType("string")
+ */
+ public $lastname;
+
+ private $id;
+ private $username;
+ 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
+-------------------
+
+`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.*
\ No newline at end of file
diff --git a/ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php b/ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php
index 3245448..5d779b0 100644
--- a/ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php
+++ b/ServiceBinding/DocumentLiteralWrappedRequestMessageBinder.php
@@ -12,6 +12,9 @@ namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
+/**
+ * @author Christian Kerl
+ */
class DocumentLiteralWrappedRequestMessageBinder implements MessageBinderInterface
{
public function processMessage(Method $messageDefinition, $message)
diff --git a/ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php b/ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php
index d8d7e78..21c72e0 100644
--- a/ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php
+++ b/ServiceBinding/DocumentLiteralWrappedResponseMessageBinder.php
@@ -12,6 +12,9 @@ namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
+/**
+ * @author Christian Kerl
+ */
class DocumentLiteralWrappedResponseMessageBinder implements MessageBinderInterface
{
public function processMessage(Method $messageDefinition, $message)
diff --git a/ServiceBinding/MessageBinderInterface.php b/ServiceBinding/MessageBinderInterface.php
index f355f76..85ca02f 100644
--- a/ServiceBinding/MessageBinderInterface.php
+++ b/ServiceBinding/MessageBinderInterface.php
@@ -12,6 +12,9 @@ namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
+/**
+ * @author Christian Kerl
+ */
interface MessageBinderInterface
{
/**
@@ -20,5 +23,5 @@ interface MessageBinderInterface
*
* @return mixed
*/
- function processMessage(Method $messageDefinition, $message);
+ function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array());
}
\ No newline at end of file
diff --git a/ServiceBinding/RpcLiteralRequestMessageBinder.php b/ServiceBinding/RpcLiteralRequestMessageBinder.php
index 12c8e78..2d3c32d 100644
--- a/ServiceBinding/RpcLiteralRequestMessageBinder.php
+++ b/ServiceBinding/RpcLiteralRequestMessageBinder.php
@@ -11,17 +11,25 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
+/**
+ * @author Christian Kerl
+ * @author Francis Besset
+ */
class RpcLiteralRequestMessageBinder implements MessageBinderInterface
{
- public function processMessage(Method $messageDefinition, $message)
+ private $messageRefs = array();
+
+ public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
{
$result = array();
$i = 0;
- foreach($messageDefinition->getArguments() as $argument) {
+ foreach ($messageDefinition->getArguments() as $argument) {
if (isset($message[$i])) {
- $result[$argument->getName()] = $message[$i];
+ $result[$argument->getName()] = $this->processType($argument->getType()->getPhpType(), $message[$i], $definitionComplexTypes);
}
$i++;
@@ -29,4 +37,62 @@ class RpcLiteralRequestMessageBinder implements MessageBinderInterface
return $result;
}
+
+ private function processType($phpType, $message, array $definitionComplexTypes)
+ {
+ if (preg_match('/^([^\[]+)\[\]$/', $phpType, $match)) {
+ $isArray = true;
+ $type = $match[1];
+ } else {
+ $isArray = false;
+ $type = $phpType;
+ }
+
+ if (isset($definitionComplexTypes[$type])) {
+ if ($isArray) {
+ $array = array();
+
+ foreach ($message->item as $complexType) {
+ $array[] = $this->getInstanceOfType($type, $complexType, $definitionComplexTypes);
+ }
+
+ $message = $array;
+ } else {
+ $message = $this->getInstanceOfType($type, $message, $definitionComplexTypes);
+ }
+ } elseif ($isArray) {
+ $message = $message->item;
+ }
+
+ return $message;
+ }
+
+ private function getInstanceOfType($phpType, $message, array $definitionComplexTypes)
+ {
+ $hash = spl_object_hash($message);
+ if (isset($this->messageRefs[$hash])) {
+ return $this->messageRefs[$hash];
+ }
+
+ $this->messageRefs[$hash] =
+ $instanceType = new $phpType();
+
+ foreach ($definitionComplexTypes[$phpType] as $type) {
+ $value = $this->processType($type->getValue(), $message->{$type->getName()}, $definitionComplexTypes);
+
+ if ($type instanceof PropertyComplexType) {
+ $instanceType->{$type->getOriginalName()} = $value;
+ } elseif ($type instanceof MethodComplexType) {
+ if (!$type->getSetter()) {
+ throw new \LogicException();
+ }
+
+ $instanceType->{$type->getSetter()}($value);
+ } else {
+ throw new \InvalidArgumentException();
+ }
+ }
+
+ return $instanceType;
+ }
}
\ No newline at end of file
diff --git a/ServiceBinding/RpcLiteralResponseMessageBinder.php b/ServiceBinding/RpcLiteralResponseMessageBinder.php
index e4b4aa2..fcf5f77 100644
--- a/ServiceBinding/RpcLiteralResponseMessageBinder.php
+++ b/ServiceBinding/RpcLiteralResponseMessageBinder.php
@@ -11,11 +11,86 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
+/**
+ * @author Christian Kerl
+ * @author Francis Besset
+ */
class RpcLiteralResponseMessageBinder implements MessageBinderInterface
{
- public function processMessage(Method $messageDefinition, $message)
+ private $messageRefs = array();
+
+ public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
{
+ $return = $messageDefinition->getReturn();
+ $class = $return->getPhpType();
+
+ $message = $this->processType($messageDefinition->getReturn()->getPhpType(), $message, $definitionComplexTypes);
+
return $message;
}
+
+ private function processType($phpType, $message, array $definitionComplexTypes)
+ {
+ if (preg_match('/^([^\[]+)\[\]$/', $phpType, $match)) {
+ $isArray = true;
+ $type = $match[1];
+ } else {
+ $isArray = false;
+ $type = $phpType;
+ }
+
+ if (isset($definitionComplexTypes[$type])) {
+ if ($isArray) {
+ $array = array();
+
+ foreach ($message as $complexType) {
+ $array[] = $this->getInstanceOfStdClass($type, $complexType, $definitionComplexTypes);
+ }
+
+ $message = $array;
+ } else {
+ $message = $this->getInstanceOfStdClass($type, $message, $definitionComplexTypes);
+ }
+ }
+
+ return $message;
+ }
+
+ private function getInstanceOfStdClass($phpType, $message, $definitionComplexTypes)
+ {
+ $hash = spl_object_hash($message);
+ if (isset($this->messageRefs[$hash])) {
+ return $this->messageRefs[$hash];
+ }
+
+ $class = $phpType;
+ if ($class[0] == '\\') {
+ $class = substr($class, 1);
+ }
+
+ if (get_class($message) !== $class) {
+ throw new \InvalidArgumentException();
+ }
+
+ $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()}();
+ } else {
+ throw new \InvalidArgumentException();
+ }
+
+ $stdClass->{$type->getName()} = $this->processType($type->getValue(), $value, $definitionComplexTypes);
+ }
+
+ return $stdClass;
+ }
}
\ No newline at end of file
diff --git a/ServiceBinding/ServiceBinder.php b/ServiceBinding/ServiceBinder.php
index aa329f1..57065e6 100644
--- a/ServiceBinding/ServiceBinder.php
+++ b/ServiceBinding/ServiceBinder.php
@@ -15,6 +15,9 @@ use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\Soap\SoapHeader;
use BeSimple\SoapBundle\Util\QName;
+/**
+ * @author Christian Kerl
+ */
class ServiceBinder
{
/**
@@ -61,7 +64,7 @@ class ServiceBinder
$result = array();
$result['_controller'] = $methodDefinition->getController();
- $result = array_merge($result, $this->requestMessageBinder->processMessage($methodDefinition, $arguments));
+ $result = array_merge($result, $this->requestMessageBinder->processMessage($methodDefinition, $arguments, $this->definition->getDefinitionComplexTypes()));
return $result;
}
@@ -70,7 +73,7 @@ class ServiceBinder
{
$methodDefinition = $this->definition->getMethods()->get($name);
- return $this->responseMessageBinder->processMessage($methodDefinition, $return);
+ return $this->responseMessageBinder->processMessage($methodDefinition, $return, $this->definition->getDefinitionComplexTypes());
}
protected function createSoapHeader(Header $headerDefinition, $data)
diff --git a/ServiceDefinition/Annotation/MethodComplexType.php b/ServiceDefinition/Annotation/MethodComplexType.php
new file mode 100644
index 0000000..cf1cdaf
--- /dev/null
+++ b/ServiceDefinition/Annotation/MethodComplexType.php
@@ -0,0 +1,67 @@
+
+ *
+ * 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/Annotation/PropertyComplexType.php b/ServiceDefinition/Annotation/PropertyComplexType.php
new file mode 100644
index 0000000..6f1f046
--- /dev/null
+++ b/ServiceDefinition/Annotation/PropertyComplexType.php
@@ -0,0 +1,56 @@
+
+ *
+ * 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 PropertyComplexType extends Configuration
+{
+ private $name;
+ private $value;
+ private $isNillable = false;
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ public function isNillable()
+ {
+ return $this->isNillable;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ public function setValue($value)
+ {
+ $this->value = $value;
+ }
+
+ public function setNillable($isNillable)
+ {
+ $this->isNillable = (bool) $isNillable;
+ }
+
+ public function getAliasName()
+ {
+ return 'propertycomplextype';
+ }
+}
\ No newline at end of file
diff --git a/ServiceDefinition/Dumper/WsdlDumper.php b/ServiceDefinition/Dumper/WsdlDumper.php
index 09548b5..7c07064 100644
--- a/ServiceDefinition/Dumper/WsdlDumper.php
+++ b/ServiceDefinition/Dumper/WsdlDumper.php
@@ -13,6 +13,7 @@ namespace BeSimple\SoapBundle\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Type;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
+use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
use BeSimple\SoapBundle\Util\Assert;
use BeSimple\SoapBundle\Util\QName;
@@ -23,9 +24,15 @@ use Zend\Soap\Wsdl;
*/
class WsdlDumper implements DumperInterface
{
+ private $loader;
private $wsdl;
private $definition;
+ public function __construct(AnnotationComplexTypeLoader $loader)
+ {
+ $this->loader = $loader;
+ }
+
public function dumpServiceDefinition(ServiceDefinition $definition, array $options = array())
{
Assert::thatArgumentNotNull('definition', $definition);
@@ -33,7 +40,7 @@ class WsdlDumper implements DumperInterface
$options = array_merge(array('endpoint' => '', 'stylesheet' => null), $options);
$this->definition = $definition;
- $this->wsdl = new Wsdl($definition->getName(), $definition->getNamespace(), new WsdlTypeStrategy());
+ $this->wsdl = new Wsdl($definition->getName(), $definition->getNamespace(), new WsdlTypeStrategy($this->loader, $definition));
$port = $this->wsdl->addPortType($this->getPortTypeName());
$binding = $this->wsdl->addBinding($this->getBindingName(), $this->qualify($this->getPortTypeName()));
diff --git a/ServiceDefinition/Dumper/WsdlTypeStrategy.php b/ServiceDefinition/Dumper/WsdlTypeStrategy.php
index 8ca9acb..77aa131 100644
--- a/ServiceDefinition/Dumper/WsdlTypeStrategy.php
+++ b/ServiceDefinition/Dumper/WsdlTypeStrategy.php
@@ -10,13 +10,15 @@
namespace BeSimple\SoapBundle\ServiceDefinition\Dumper;
+use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
+use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\ComplexType;
use BeSimple\SoapBundle\Util\String;
use Zend\Soap\Exception;
use Zend\Soap\Wsdl;
use Zend\Soap\Wsdl\Strategy;
use Zend\Soap\Wsdl\Strategy\ArrayOfTypeSequence;
-use Zend\Soap\Wsdl\Strategy\DefaultComplexType;
class WsdlTypeStrategy implements Strategy
{
@@ -27,13 +29,16 @@ class WsdlTypeStrategy implements Strategy
*/
private $context;
+ private $loader;
+ private $definition;
+
private $typeStrategy;
private $arrayStrategy;
- public function __construct()
+ public function __construct(AnnotationComplexTypeLoader $loader, ServiceDefinition $definition)
{
- $this->typeStrategy = new DefaultComplexType();
- $this->arrayStrategy = new ArrayOfTypeSequence();
+ $this->loader = $loader;
+ $this->definition = $definition;
}
/**
@@ -51,9 +56,11 @@ class WsdlTypeStrategy implements Strategy
/**
* Create a complex type based on a strategy
*
- * @throws \Zend\Soap\WsdlException
* @param string $type
+ *
* @return string XSD type
+ *
+ * @throws \Zend\Soap\WsdlException
*/
public function addComplexType($type)
{
@@ -61,9 +68,28 @@ class WsdlTypeStrategy implements Strategy
throw new \LogicException(sprintf('Cannot add complex type "%s", no context is set for this composite strategy.', $type));
}
- $strategy = String::endsWith($type, '[]') ? $this->arrayStrategy : $this->typeStrategy;
- $strategy->setContext($this->context);
+ $strategy = String::endsWith($type, '[]') ? $this->getArrayStrategy() : $this->getTypeStrategy();
return $strategy->addComplexType($type);
}
+
+ private function getArrayStrategy()
+ {
+ if (!$this->arrayStrategy) {
+ $this->arrayStrategy = new ArrayOfTypeSequence();
+ $this->arrayStrategy->setContext($this->context);
+ }
+
+ return $this->arrayStrategy;
+ }
+
+ private function getTypeStrategy()
+ {
+ if (!$this->typeStrategy) {
+ $this->typeStrategy = new ComplexType($this->loader, $this->definition);
+ $this->typeStrategy->setContext($this->context);
+ }
+
+ return $this->typeStrategy;
+ }
}
\ No newline at end of file
diff --git a/ServiceDefinition/Loader/AnnotationClassLoader.php b/ServiceDefinition/Loader/AnnotationClassLoader.php
index 2faccbc..b71732f 100644
--- a/ServiceDefinition/Loader/AnnotationClassLoader.php
+++ b/ServiceDefinition/Loader/AnnotationClassLoader.php
@@ -32,10 +32,6 @@ use Symfony\Component\Config\Loader\LoaderResolver;
*/
class AnnotationClassLoader implements LoaderInterface
{
- private $methodAnnotationClass = 'BeSimple\\SoapBundle\\ServiceDefinition\\Annotation\\Method';
- private $paramAnnotationClass = 'BeSimple\\SoapBundle\\ServiceDefinition\\Annotation\\Param';
- private $resultAnnotationClass = 'BeSimple\\SoapBundle\\ServiceDefinition\\Annotation\\Result';
-
protected $reader;
/**
diff --git a/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php b/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php
new file mode 100644
index 0000000..21c9085
--- /dev/null
+++ b/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php
@@ -0,0 +1,94 @@
+
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapBundle\ServiceDefinition\Loader;
+
+use BeSimple\SoapBundle\ServiceDefinition\Annotation\ComplexType;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
+use BeSimple\SoapBundle\Util\Collection;
+
+/**
+ * AnnotationComplexTypeLoader loads ServiceDefinition from a PHP class and its methods.
+ *
+ * Based on \Symfony\Component\Routing\Loader\AnnotationClassLoader
+ *
+ * @author Francis Besset
+ */
+class AnnotationComplexTypeLoader extends AnnotationClassLoader
+{
+ private $propertyComplexTypeClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\PropertyComplexType';
+ private $methodComplexTypeClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\MethodComplexType';
+
+ /**
+ * Loads a ServiceDefinition from annotations from a class.
+ *
+ * @param string $class A class name
+ * @param string $type The resource type
+ *
+ * @return ServiceDefinition A ServiceDefinition instance
+ *
+ * @throws \InvalidArgumentException When route can't be parsed
+ */
+ public function load($class, $type = null)
+ {
+ if (!class_exists($class)) {
+ throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
+ }
+
+ $class = new \ReflectionClass($class);
+ $collection = new Collection('getName');
+
+ foreach ($class->getProperties() as $property) {
+ if ($property->isPublic()) {
+ $complexType = $this->reader->getPropertyAnnotation($property, $this->propertyComplexTypeClass);
+
+ 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);
+ }
+ }
+ }
+
+ return $collection;
+ }
+}
\ No newline at end of file
diff --git a/ServiceDefinition/ServiceDefinition.php b/ServiceDefinition/ServiceDefinition.php
index d11e285..683de74 100644
--- a/ServiceDefinition/ServiceDefinition.php
+++ b/ServiceDefinition/ServiceDefinition.php
@@ -34,6 +34,8 @@ class ServiceDefinition
*/
private $headers;
+ private $complexTypes = array();
+
public function __construct($name = null, $namespace = null, array $methods = array(), array $headers = array())
{
$this->setName($name);
@@ -127,4 +129,14 @@ class ServiceDefinition
return $types;
}
+
+ public function addDefinitionComplexType($type, Collection $complexType)
+ {
+ $this->complexTypes[$type] = $complexType;
+ }
+
+ public function getDefinitionComplexTypes()
+ {
+ return $this->complexTypes;
+ }
}
\ No newline at end of file
diff --git a/ServiceDefinition/Strategy/BaseComplexType.php b/ServiceDefinition/Strategy/BaseComplexType.php
new file mode 100644
index 0000000..917fb57
--- /dev/null
+++ b/ServiceDefinition/Strategy/BaseComplexType.php
@@ -0,0 +1,62 @@
+
+ *
+ * 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
+ */
+abstract class BaseComplexType
+{
+ private $name;
+ private $originalName;
+ private $value;
+ private $isNillable = false;
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function getOriginalName()
+ {
+ return $this->originalName ?: $this->name;
+ }
+
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ public function isNillable()
+ {
+ return $this->isNillable;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ public function setOriginalName($originalName)
+ {
+ $this->originalName = $originalName;
+ }
+
+ public function setValue($value)
+ {
+ $this->value = $value;
+ }
+
+ public function setNillable($isNillable)
+ {
+ $this->isNillable = (bool) $isNillable;
+ }
+}
\ No newline at end of file
diff --git a/ServiceDefinition/Strategy/ComplexType.php b/ServiceDefinition/Strategy/ComplexType.php
new file mode 100644
index 0000000..8af3fb1
--- /dev/null
+++ b/ServiceDefinition/Strategy/ComplexType.php
@@ -0,0 +1,81 @@
+
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapBundle\ServiceDefinition\Strategy;
+
+use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
+
+use Zend\Soap\Wsdl;
+use Zend\Soap\Wsdl\Strategy\AbstractStrategy;
+
+/**
+ * @author Francis Besset
+ */
+class ComplexType extends AbstractStrategy
+{
+ private $loader;
+ private $definition;
+
+ public function __construct(AnnotationComplexTypeLoader $loader, $definition)
+ {
+ $this->loader = $loader;
+ $this->definition = $definition;
+ }
+
+ /**
+ * Add a complex type by recursivly using all the class properties fetched via Reflection.
+ *
+ * @param string $type Name of the class to be specified
+ * @return string XSD Type for the given PHP type
+ */
+ public function addComplexType($type)
+ {
+ if (null !== $soapType = $this->scanRegisteredTypes($type)) {
+ return $soapType;
+ }
+
+ if (!$this->loader->supports($type)) {
+ throw new \InvalidArgumentException(sprintf('Cannot add a complex type "%s" that is not an object or where class could not be found in "ComplexType" strategy.', $type));
+ }
+
+ $dom = $this->getContext()->toDomDocument();
+
+ $soapTypeName = Wsdl::translateType($type);
+ $soapType = 'tns:'.$soapTypeName;
+
+ // Register type here to avoid recursion
+ $this->getContext()->addType($type, $soapType);
+
+ $complexType = $dom->createElement('xsd:complexType');
+ $complexType->setAttribute('name', $soapTypeName);
+
+ $all = $dom->createElement('xsd:all');
+
+ $definitionComplexType = $this->loader->load($type);
+ $this->definition->addDefinitionComplexType($type, $definitionComplexType);
+
+ foreach ($definitionComplexType as $annotationComplexType) {
+ $element = $dom->createElement('xsd:element');
+ $element->setAttribute('name', $propertyName = $annotationComplexType->getName());
+ $element->setAttribute('type', $this->getContext()->getType(trim($annotationComplexType->getValue())));
+
+ if ($annotationComplexType->isNillable()) {
+ $element->setAttribute('nillable', 'true');
+ }
+
+ $all->appendChild($element);
+ }
+
+ $complexType->appendChild($all);
+ $this->getContext()->getSchema()->appendChild($complexType);
+
+ return $soapType;
+ }
+}
\ No newline at end of file
diff --git a/ServiceDefinition/Strategy/MethodComplexType.php b/ServiceDefinition/Strategy/MethodComplexType.php
new file mode 100644
index 0000000..7205ab5
--- /dev/null
+++ b/ServiceDefinition/Strategy/MethodComplexType.php
@@ -0,0 +1,29 @@
+
+ *
+ * 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
new file mode 100644
index 0000000..7545f0e
--- /dev/null
+++ b/ServiceDefinition/Strategy/PropertyComplexType.php
@@ -0,0 +1,18 @@
+
+ *
+ * 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/Tests/ServiceBinding/RpcLiteralRequestMessageBinderTest.php b/Tests/ServiceBinding/RpcLiteralRequestMessageBinderTest.php
new file mode 100644
index 0000000..0eb1997
--- /dev/null
+++ b/Tests/ServiceBinding/RpcLiteralRequestMessageBinderTest.php
@@ -0,0 +1,267 @@
+
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapBundle\Tests\ServiceBinding;
+
+use BeSimple\SoapBundle\ServiceDefinition\Method;
+use BeSimple\SoapBundle\ServiceDefinition\Argument;
+use BeSimple\SoapBundle\ServiceDefinition\Type;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
+use BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder;
+use BeSimple\SoapBundle\Util\Collection;
+
+/**
+ * UnitTest for \BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder.
+ *
+ * @author Francis Besset
+ */
+class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider messageProvider
+ */
+ public function testProcessMessage(Method $method, $message, $assert)
+ {
+ $messageBinder = new RpcLiteralRequestMessageBinder();
+ $result = $messageBinder->processMessage($method, $message);
+
+ $this->assertSame($assert, $result);
+ }
+
+ public function testProcessMessageWithComplexType()
+ {
+ $attributes = new \stdClass();
+ $attributes->foo = 'bar';
+ $attributes->bar = 10;
+
+ $messageBinder = new RpcLiteralRequestMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype_argument', null, array(
+ new Argument('attributes', new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes')),
+ )),
+ array($attributes),
+ $this->getDefinitionComplexTypes()
+ );
+
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes', $result['attributes']);
+ $this->assertSame('bar', $result['attributes']->foo);
+ $this->assertSame(10, $result['attributes']->bar);
+
+ $attributes1 = new \stdClass();
+ $attributes1->foo = 'foobar';
+ $attributes1->bar = 11;
+ $attributes2 = new \stdClass();
+ $attributes2->foo = 'barfoo';
+ $attributes2->bar = 12;
+
+ $message = new \stdClass();
+ $message->item = array($attributes1, $attributes2);
+
+ $messageBinder = new RpcLiteralRequestMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype_argument', null, array(
+ new Argument('attributes', new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes[]')),
+ )),
+ array($message),
+ $this->getDefinitionComplexTypes()
+ );
+
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes', $result['attributes'][0]);
+ $this->assertSame('foobar', $result['attributes'][0]->foo);
+ $this->assertSame(11, $result['attributes'][0]->bar);
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes', $result['attributes'][1]);
+ $this->assertSame('barfoo', $result['attributes'][1]->foo);
+ $this->assertSame(12, $result['attributes'][1]->bar);
+ }
+
+ public function testProcessMessageWithComplexTypeMethods()
+ {
+ $methods = new \stdClass();
+ $methods->foo = 'bar';
+ $methods->bar = 23;
+
+ $messageBinder = new RpcLiteralRequestMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype_methods', null, array(
+ new Argument('setters', new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters')),
+ )),
+ array($methods),
+ $this->getDefinitionComplexTypes()
+ );
+
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters', $result['setters']);
+ $this->assertSame('bar', $result['setters']->getFoo());
+ $this->assertSame(23, $result['setters']->getBar());
+ }
+
+ public function testProcessMessageWithComplexTypeIntoComplexType()
+ {
+ $complexType = new \stdClass();
+ $foo = $complexType->foo = new \stdClass();
+ $foo->foo = 'hello';
+ $foo->bar = 24;
+
+ $bar = $complexType->bar = new \stdClass();
+ $bar->foo = 'bonjour';
+ $bar->bar = 1012;
+
+ $messageBinder = new RpcLiteralRequestMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype_complextype', null, array(
+ new Argument('complex_type', new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType')),
+ )),
+ array($complexType),
+ $this->getDefinitionComplexTypes()
+ );
+
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType', $result['complex_type']);
+
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes', $result['complex_type']->getFoo());
+ $this->assertSame('hello', $result['complex_type']->getFoo()->foo);
+ $this->assertSame(24, $result['complex_type']->getFoo()->bar);
+
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters', $result['complex_type']->bar);
+ $this->assertSame('bonjour', $result['complex_type']->bar->getFoo());
+ $this->assertSame(1012, $result['complex_type']->bar->getBar());
+ }
+
+ public function testProcessMessageWithComplexTypeReferences()
+ {
+ $complexType1 = new \stdClass();
+ $foo = $complexType1->foo = new \stdClass();
+ $foo->foo = 'hello';
+ $foo->bar = 24;
+
+ $bar = $complexType1->bar = new \stdClass();
+ $bar->foo = 'bonjour';
+ $bar->bar = 1012;
+
+ $complexType2 = new \stdClass();
+ $complexType2->foo = $foo;
+ $complexType2->bar = $bar;
+
+ $complexTypes = new \stdClass();
+ $complexTypes->item = array($complexType1, $complexType2);
+
+ $messageBinder = new RpcLiteralRequestMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextypes_references', null, array(
+ new Argument('complex_types', new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType[]')),
+ )),
+ array($complexTypes),
+ $this->getDefinitionComplexTypes()
+ );
+
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType', $result['complex_types'][0]);
+ $this->assertInstanceOf('BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType', $result['complex_types'][1]);
+
+ $this->assertSame($result['complex_types'][0]->getFoo(), $result['complex_types'][1]->getFoo());
+ $this->assertSame($result['complex_types'][0]->bar, $result['complex_types'][1]->bar);
+ }
+
+ public function messageProvider()
+ {
+ $messages = array();
+
+ $messages[] = array(
+ new Method('no_argument'),
+ array(),
+ array(),
+ );
+
+ $messages[] = array(
+ new Method('string_argument', null, array(
+ new Argument('foo', new Type('string')),
+ )),
+ array('bar'),
+ array('foo' => 'bar'),
+ );
+
+ $messages[] = array(
+ new Method('string_int_arguments', null, array(
+ new Argument('foo', new Type('string')),
+ new Argument('bar', new Type('int')),
+ )),
+ array('test', 20),
+ array('foo' => 'test', 'bar' => 20),
+ );
+
+ $strings = new \stdClass();
+ $strings->item = array('foo', 'bar', 'barfoo');
+ $messages[] = array(
+ new Method('array_string_arguments', null, array(
+ new Argument('foo', new Type('string[]')),
+ new Argument('bar', new Type('int')),
+ )),
+ array($strings, 4),
+ array('foo' => array('foo', 'bar', 'barfoo'), 'bar' => 4),
+ );
+
+ return $messages;
+ }
+
+ private function getDefinitionComplexTypes()
+ {
+ $this->definitionComplexTypes = array();
+
+ $this->definitionComplexTypes['\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes'] = $this->createPropertiesCollection(array(
+ array('foo', 'string'),
+ array('bar', 'int'),
+ ));
+
+ $this->definitionComplexTypes['\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters'] = $this->createMethodsCollection(array(
+ array('foo', 'string', 'getFoo', 'setFoo'),
+ array('bar', 'int', 'getBar', 'setBar'),
+ ));
+
+ $collection = $this->createMethodsCollection(array(
+ array('foo', '\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes', 'getFoo', 'setFoo'),
+ ));
+ $this->createPropertiesCollection(array(
+ array('bar', '\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters'),
+ ), $collection);
+ $this->definitionComplexTypes['\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType'] = $collection;
+
+ return $this->definitionComplexTypes;
+ }
+
+ private function createPropertiesCollection(array $properties, Collection $collection = null)
+ {
+ $collection = $collection ?: new Collection('getName');
+
+ foreach ($properties as $property) {
+ $collectionProperty = new PropertyComplexType();
+ $collectionProperty->setName($property[0]);
+ $collectionProperty->setValue($property[1]);
+
+ $collection->add($collectionProperty);
+ }
+
+ return $collection;
+ }
+
+ private function createMethodsCollection(array $methods, Collection $collection = null)
+ {
+ $collection = $collection ?: new Collection('getName');
+
+ foreach ($methods as $method) {
+ $collectionMethod = new MethodComplexType();
+ $collectionMethod->setName($method[0]);
+ $collectionMethod->setValue($method[1]);
+ $collectionMethod->setOriginalName($method[2]);
+ $collectionMethod->setSetter($method[3]);
+
+ $collection->add($collectionMethod);
+ }
+
+ return $collection;
+ }
+}
diff --git a/Tests/ServiceBinding/RpcLiteralResponseMessageBinderTest.php b/Tests/ServiceBinding/RpcLiteralResponseMessageBinderTest.php
new file mode 100644
index 0000000..bc6129f
--- /dev/null
+++ b/Tests/ServiceBinding/RpcLiteralResponseMessageBinderTest.php
@@ -0,0 +1,227 @@
+
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapBundle\Tests\ServiceBinding;
+
+use BeSimple\SoapBundle\ServiceDefinition\Method;
+use BeSimple\SoapBundle\ServiceDefinition\Type;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
+use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
+use BeSimple\SoapBundle\ServiceBinding\RpcLiteralResponseMessageBinder;
+use BeSimple\SoapBundle\Util\Collection;
+use BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes;
+use BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType;
+use BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters;
+
+/**
+ * UnitTest for \BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder.
+ *
+ * @author Francis Besset
+ */
+class RpcLiteralResponseMessageBinderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider messageProvider
+ */
+ public function testProcessMessage(Method $method, $message, $assert)
+ {
+ $messageBinder = new RpcLiteralResponseMessageBinder();
+ $result = $messageBinder->processMessage($method, $message);
+
+ $this->assertSame($assert, $result);
+ }
+
+ public function testProcessMessageWithComplexType()
+ {
+ $definitionComplexTypes = $this->getDefinitionComplexTypes();
+
+ $attributes = new Attributes();
+ $attributes->foo = 'foobar';
+ $attributes->bar = 20349;
+ $messageBinder = new RpcLiteralResponseMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype', null, array(), new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes')),
+ $attributes,
+ $definitionComplexTypes
+ );
+
+ $this->assertInstanceOf('stdClass', $result);
+ $this->assertSame('foobar', $result->foo);
+ $this->assertSame(20349, $result->bar);
+
+ $attributes1 = new Attributes();
+ $attributes1->foo = 'bar';
+ $attributes1->bar = 2929;
+ $attributes2 = new Attributes();
+ $attributes2->foo = 'foo';
+ $attributes2->bar = 123992;
+ $message = array($attributes1, $attributes2);
+ $messageBinder = new RpcLiteralResponseMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype_argument', null, array(), new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes[]')),
+ $message,
+ $definitionComplexTypes
+ );
+
+ $this->assertTrue(is_array($result));
+
+ $this->assertInstanceOf('stdClass', $result[0]);
+ $this->assertSame('bar', $result[0]->foo);
+ $this->assertSame(2929, $result[0]->bar);
+
+ $this->assertInstanceOf('stdClass', $result[1]);
+ $this->assertSame('foo', $result[1]->foo);
+ $this->assertSame(123992, $result[1]->bar);
+ }
+
+ public function testProcessMessageWithComplexTypeMethods()
+ {
+ $setters = new Setters();
+ $setters->setFoo('foobar');
+ $setters->setBar(42);
+
+ $messageBinder = new RpcLiteralResponseMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype_methods', null, array(), new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters')),
+ $setters,
+ $this->getDefinitionComplexTypes()
+ );
+
+ $this->assertInstanceOf('stdClass', $result);
+ $this->assertSame('foobar', $result->foo);
+ $this->assertSame(42, $result->bar);
+ }
+
+ public function testProcessMessageWithComplexTypeIntoComplexType()
+ {
+ $complexType = new ComplexType();
+
+ $foo = new Attributes();
+ $foo->foo = 'Hello world!';
+ $foo->bar = 4242;
+ $complexType->setFoo($foo);
+
+ $bar = new Setters();
+ $bar->setFoo('bar foo');
+ $bar->setBar(2424);
+ $complexType->bar = $bar;
+
+ $messageBinder = new RpcLiteralResponseMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype_complextype', null, array(), new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType')),
+ $complexType,
+ $this->getDefinitionComplexTypes()
+ );
+
+ $this->assertInstanceOf('stdClass', $result);
+
+ $this->assertInstanceOf('stdClass', $result->foo);
+ $this->assertSame('Hello world!', $result->foo->foo);
+ $this->assertSame(4242, $result->foo->bar);
+
+ $this->assertInstanceOf('stdClass', $result->bar);
+ $this->assertSame('bar foo', $result->bar->foo);
+ $this->assertSame(2424, $result->bar->bar);
+ }
+
+ public function testProcessMessageWithComplexTypeReferences()
+ {
+ $attributes = new Attributes();
+ $attributes->foo = 'bar';
+ $attributes->bar = 2929;
+
+ $message = array($attributes, $attributes);
+ $messageBinder = new RpcLiteralResponseMessageBinder();
+ $result = $messageBinder->processMessage(
+ new Method('complextype_argument', null, array(), new Type('\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes[]')),
+ $message,
+ $this->getDefinitionComplexTypes()
+ );
+
+ $this->assertInstanceOf('stdClass', $result[0]);
+ $this->assertSame($result[0], $result[1]);
+ }
+
+ public function messageProvider()
+ {
+ $messages = array();
+
+ $messages[] = array(
+ new Method('boolean', null, array(), new Type('boolean')),
+ true,
+ true,
+ );
+
+ $messages[] = array(
+ new Method('strings', null, array(), new Type('string[]')),
+ array('hello', 'world'),
+ array('hello', 'world'),
+ );
+
+ return $messages;
+ }
+
+ private function getDefinitionComplexTypes()
+ {
+ $this->definitionComplexTypes = array();
+
+ $this->definitionComplexTypes['\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes'] = $this->createPropertiesCollection(array(
+ array('foo', 'string'),
+ array('bar', 'int'),
+ ));
+
+ $this->definitionComplexTypes['\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters'] = $this->createMethodsCollection(array(
+ array('foo', 'string', 'getFoo', 'setFoo'),
+ array('bar', 'int', 'getBar', 'setBar'),
+ ));
+
+ $collection = $this->createMethodsCollection(array(
+ array('foo', '\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Attributes', 'getFoo', 'setFoo'),
+ ));
+ $this->createPropertiesCollection(array(
+ array('bar', '\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\Setters'),
+ ), $collection);
+ $this->definitionComplexTypes['\BeSimple\SoapBundle\Tests\ServiceBinding\fixtures\ComplexType'] = $collection;
+
+ return $this->definitionComplexTypes;
+ }
+
+ private function createPropertiesCollection(array $properties, Collection $collection = null)
+ {
+ $collection = $collection ?: new Collection('getName');
+
+ foreach ($properties as $property) {
+ $collectionProperty = new PropertyComplexType();
+ $collectionProperty->setName($property[0]);
+ $collectionProperty->setValue($property[1]);
+
+ $collection->add($collectionProperty);
+ }
+
+ return $collection;
+ }
+
+ private function createMethodsCollection(array $methods, Collection $collection = null)
+ {
+ $collection = $collection ?: new Collection('getName');
+
+ foreach ($methods as $method) {
+ $collectionMethod = new MethodComplexType();
+ $collectionMethod->setName($method[0]);
+ $collectionMethod->setValue($method[1]);
+ $collectionMethod->setOriginalName($method[2]);
+ $collectionMethod->setSetter($method[3]);
+
+ $collection->add($collectionMethod);
+ }
+
+ return $collection;
+ }
+}
diff --git a/Tests/ServiceBinding/fixtures/Attributes.php b/Tests/ServiceBinding/fixtures/Attributes.php
new file mode 100644
index 0000000..53d4aee
--- /dev/null
+++ b/Tests/ServiceBinding/fixtures/Attributes.php
@@ -0,0 +1,9 @@
+foo;
+ }
+
+ public function setFoo($foo)
+ {
+ $this->foo = $foo;
+ }
+}
\ No newline at end of file
diff --git a/Tests/ServiceBinding/fixtures/Setters.php b/Tests/ServiceBinding/fixtures/Setters.php
new file mode 100644
index 0000000..a8de35a
--- /dev/null
+++ b/Tests/ServiceBinding/fixtures/Setters.php
@@ -0,0 +1,29 @@
+foo;
+ }
+
+ public function setFoo($foo)
+ {
+ $this->foo = $foo;
+ }
+
+ public function getBar()
+ {
+ return $this->bar;
+ }
+
+ public function setBar($bar)
+ {
+ $this->bar = $bar;
+ }
+}
\ No newline at end of file
diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php
index 8fa8a1e..4d7062a 100644
--- a/Tests/bootstrap.php
+++ b/Tests/bootstrap.php
@@ -22,4 +22,4 @@ spl_autoload_register(function($class) {
return false;
}
-});
\ No newline at end of file
+});
diff --git a/WebServiceContext.php b/WebServiceContext.php
index 0a429ee..f0acaf6 100644
--- a/WebServiceContext.php
+++ b/WebServiceContext.php
@@ -70,6 +70,11 @@ class WebServiceContext
return $this->serviceDefinition;
}
+ public function getWsdlFileContent($endpoint = null)
+ {
+ return file_get_contents($this->getWsdlFile($endpoint));
+ }
+
public function getWsdlFile($endpoint = null)
{
$file = sprintf('%s/%s.%s.wsdl', $this->options['cache_dir'], $this->options['name'], md5($endpoint));
@@ -82,11 +87,6 @@ class WebServiceContext
return (string) $cache;
}
- public function getWsdlFileContent($endpoint = null)
- {
- return file_get_contents($this->getWsdlFile($endpoint));
- }
-
public function getServiceBinder()
{
if (null === $this->serviceBinder) {
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 4024132..c40be1e 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -13,7 +13,7 @@
>
-
+
@@ -21,4 +21,4 @@
./Tests
-
\ No newline at end of file
+
diff --git a/vendors.php b/vendors.php
index 3c473b8..098bddc 100755
--- a/vendors.php
+++ b/vendors.php
@@ -23,7 +23,8 @@ if (!is_dir($vendorDir = dirname(__FILE__).'/vendor')) {
$deps = array(
array('symfony', 'http://github.com/symfony/symfony.git', 'origin/HEAD'),
- array('zend', 'http://github.com/zendframework/zf2.git', 'origin/HEAD'),
+ array('zend-framework/library/Zend/Soap', 'http://github.com/BeSimple/zend-soap.git', 'origin/HEAD'),
+ array('zend-framework/library/Zend/Mime', 'http://github.com/BeSimple/zend-mime.git', 'origin/HEAD'),
);
foreach ($deps as $dep) {