Add 'src/BeSimple/SoapBundle/' from commit 'e99f707b105c0a0472260d8d42a5a14a7fb7a211'

git-subtree-dir: src/BeSimple/SoapBundle
git-subtree-mainline: 9a8d23fa23
git-subtree-split: e99f707b10
This commit is contained in:
Francis Besset
2013-07-19 17:00:11 +02:00
94 changed files with 5786 additions and 0 deletions

View File

@ -0,0 +1,200 @@
<?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\Loader;
use BeSimple\SoapBundle\ServiceDefinition as Definition;
use BeSimple\SoapBundle\ServiceDefinition\Annotation;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
/**
* AnnotationClassLoader loads ServiceDefinition from a PHP class and its methods.
*
* Based on \Symfony\Component\Routing\Loader\AnnotationClassLoader
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class AnnotationClassLoader implements LoaderInterface
{
protected $reader;
/**
* Constructor.
*
* @param \Doctrine\Common\Annotations\Reader $reader
*/
public function __construct(Reader $reader)
{
$this->reader = $reader;
}
/**
* Loads a ServiceDefinition from annotations from a class.
*
* @param string $class A class name
* @param string $type The resource type
*
* @return \BeSimple\SoapBundle\ServiceDefinition\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);
$definition = new Definition\ServiceDefinition();
$serviceMethodHeaders = array();
foreach ($this->reader->getClassAnnotations($class) as $annotation) {
if ($annotation instanceof Annotation\Header) {
$serviceMethodHeaders[$annotation->getValue()] = $annotation;
}
}
foreach ($class->getMethods() as $method) {
$serviceArguments =
$serviceHeaders = array();
$serviceMethod =
$serviceReturn = null;
foreach ($serviceMethodHeaders as $annotation) {
$serviceHeaders[$annotation->getValue()] = new Definition\Header(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
}
foreach ($this->reader->getMethodAnnotations($method) as $annotation) {
if ($annotation instanceof Annotation\Header) {
$serviceHeaders[$annotation->getValue()] = new Definition\Header(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
} elseif ($annotation instanceof Annotation\Param) {
$serviceArguments[] = new Definition\Argument(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
} elseif ($annotation instanceof Annotation\Method) {
if ($serviceMethod) {
throw new \LogicException(sprintf('@Soap\Method defined twice for "%s".', $method->getName()));
}
$serviceMethod = new Definition\Method(
$annotation->getValue(),
$this->getController($class, $method, $annotation)
);
} elseif ($annotation instanceof Annotation\Result) {
if ($serviceReturn) {
throw new \LogicException(sprintf('@Soap\Result defined twice for "%s".', $method->getName()));
}
$serviceReturn = new Definition\Type($annotation->getPhpType(), $annotation->getXmlType());
}
}
if (!$serviceMethod && (!empty($serviceArguments) || $serviceReturn)) {
throw new \LogicException(sprintf('@Soap\Method non-existent for "%s".', $method->getName()));
}
if ($serviceMethod) {
$serviceMethod->setArguments($serviceArguments);
$serviceMethod->setHeaders($serviceHeaders);
if (!$serviceReturn) {
throw new \LogicException(sprintf('@Soap\Result non-existent for "%s".', $method->getName()));
}
$serviceMethod->setReturn($serviceReturn);
$definition->getMethods()->add($serviceMethod);
}
}
return $definition;
}
/**
* @param \ReflectionMethod $method
* @param \BeSimple\SoapBundle\ServiceDefinition\Annotation\Method $annotation
*
* @return string
*/
private function getController(\ReflectionClass $class, \ReflectionMethod $method, Annotation\Method $annotation)
{
if(null !== $annotation->getService()) {
return $annotation->getService() . ':' . $method->name;
} else {
return $class->name . '::' . $method->name;
}
}
/**
* @param \ReflectionMethod $method
* @param \BeSimple\SoapBundle\ServiceDefinition\Annotation\Param $annotation
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Type
*/
private function getArgumentType(\ReflectionMethod $method, Annotation\Param $annotation)
{
$phpType = $annotation->getPhpType();
$xmlType = $annotation->getXmlType();
if (null === $phpType) {
foreach ($method->getParameters() as $param) {
if ($param->name === $annotation->getName()) {
$phpType = $param->getClass()->name;
break;
}
}
}
return new Definition\Type($phpType, $xmlType);
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return Boolean True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null)
{
return is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type);
}
/**
* Sets the loader resolver.
*
* @param LoaderResolverInterface $resolver A LoaderResolverInterface instance
*/
public function setResolver(LoaderResolverInterface $resolver)
{
}
/**
* Gets the loader resolver.
*
* @return LoaderResolverInterface A LoaderResolverInterface instance
*/
public function getResolver()
{
}
}

View File

@ -0,0 +1,60 @@
<?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\Loader;
use BeSimple\SoapBundle\ServiceDefinition\ComplexType;
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 <francis.besset@gmail.com>
*/
class AnnotationComplexTypeLoader extends AnnotationClassLoader
{
private $complexTypeClass = 'BeSimple\SoapBundle\ServiceDefinition\Annotation\ComplexType';
/**
* 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', 'BeSimple\SoapBundle\ServiceDefinition\ComplexType');
foreach ($class->getProperties() as $property) {
$complexType = $this->reader->getPropertyAnnotation($property, $this->complexTypeClass);
if ($complexType) {
$propertyComplexType = new ComplexType();
$propertyComplexType->setValue($complexType->getValue());
$propertyComplexType->setNillable($complexType->isNillable());
$propertyComplexType->setName($property->getName());
$collection->add($propertyComplexType);
}
}
return $collection;
}
}

View File

@ -0,0 +1,122 @@
<?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\Loader;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Config\Resource\FileResource;
/**
* AnnotationFileLoader loads ServiceDefinition from annotations set
* on a PHP class and its methods.
*
* Based on \Symfony\Component\Routing\Loader\AnnotationFileLoader
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class AnnotationFileLoader extends FileLoader
{
protected $loader;
/**
* Constructor.
*
* @param AnnotationClassLoader $loader An AnnotationClassLoader instance
* @param string|array $paths A path or an array of paths where to look for resources
*/
public function __construct(FileLocator $locator, AnnotationClassLoader $loader, $paths = array())
{
if (!function_exists('token_get_all')) {
throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.');
}
parent::__construct($locator, $paths);
$this->loader = $loader;
}
/**
* Loads from annotations from a file.
*
* @param string $file A PHP file path
* @param string $type The resource type
*
* @return ServiceDefinition A ServiceDefinition instance
*
* @throws \InvalidArgumentException When the file does not exist
*/
public function load($file, $type = null)
{
$path = $this->locator->locate($file);
if ($class = $this->findClass($path)) {
return $definition = $this->loader->load($class, $type);
}
return null;
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return Boolean True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null)
{
return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type);
}
/**
* Returns the full class name for the first class in the file.
*
* @param string $file A PHP file path
*
* @return string|false Full class name if found, false otherwise
*/
protected function findClass($file)
{
$class = false;
$namespace = false;
$tokens = token_get_all(file_get_contents($file));
while ($token = array_shift($tokens)) {
if (!is_array($token)) {
continue;
}
if (true === $class && T_STRING === $token[0]) {
return $namespace.'\\'.$token[1];
}
if (true === $namespace && T_STRING === $token[0]) {
$namespace = '';
do {
$namespace .= $token[1];
$token = array_shift($tokens);
} while ($tokens && is_array($token) && in_array($token[0], array(T_NS_SEPARATOR, T_STRING)));
}
if (T_CLASS === $token[0]) {
$class = true;
}
if (T_NAMESPACE === $token[0]) {
$namespace = true;
}
}
return false;
}
}

View File

@ -0,0 +1,143 @@
<?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\Loader;
use BeSimple\SoapBundle\ServiceDefinition\Argument;
use BeSimple\SoapBundle\ServiceDefinition\Header;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Type;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use Symfony\Component\Config\Loader\FileLoader;
class XmlFileLoader extends FileLoader
{
public function supports($resource, $type = null)
{
return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
}
public function load($file, $type = null)
{
$path = $this->locator->locate($file);
$xml = $this->parseFile($path);
$definition = new ServiceDefinition();
$definition->setName((string) $xml['name']);
$definition->setNamespace((string) $xml['namespace']);
foreach($xml->header as $header) {
$definition->getHeaders()->add($this->parseHeader($header));
}
foreach($xml->method as $method) {
$definition->getMethods()->add($this->parseMethod($method));
}
return $definition;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Header
*/
protected function parseHeader(\SimpleXMLElement $node)
{
return new Header((string)$node['name'], $this->parseType($node->type));
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Method
*/
protected function parseMethod(\SimpleXMLElement $node)
{
$method = new Method((string)$node['name'], (string)$node['controller']);
foreach($node->argument as $argument) {
$method->getArguments()->add($this->parseArgument($argument));
}
$method->setReturn($this->parseType($node->return->type));
return $method;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Argument
*/
protected function parseArgument(\SimpleXMLElement $node)
{
$argument = new Argument((string)$node['name'], $this->parseType($node->type));
return $argument;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Type
*/
protected function parseType(\SimpleXMLElement $node)
{
$namespaces = $node->getDocNamespaces(true);
$qname = explode(':', $node['xml-type'], 2);
$xmlType = sprintf('{%s}%s', $namespaces[$qname[0]], $qname[1]);
return new Type((string)$node['php-type'], $xmlType, (string)$node['converter']);
}
/**
* @param string $file
*
* @return \SimpleXMLElement
*/
protected function parseFile($file)
{
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
if (!$dom->load($file, LIBXML_COMPACT)) {
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors()));
}
if (!$dom->schemaValidate(__DIR__.'/schema/servicedefinition-1.0.xsd')) {
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors()));
}
$dom->validateOnParse = true;
$dom->normalizeDocument();
libxml_use_internal_errors(false);
return simplexml_import_dom($dom);
}
protected function getXmlErrors()
{
$errors = array();
foreach (libxml_get_errors() as $error) {
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
$error->code,
trim($error->message),
$error->file ? $error->file : 'n/a',
$error->line,
$error->column
);
}
libxml_clear_errors();
libxml_use_internal_errors(false);
return $errors;
}
}

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://github.com/BeSimple/BeSimpleSoapBundle/servicedefinition/1.0/" xmlns:tns="http://github.com/BeSimple/BeSimpleSoapBundle/servicedefinition/1.0/" elementFormDefault="qualified">
<element name="webservice" type="tns:WebserviceType" />
<complexType name="WebserviceType">
<sequence>
<element name="header" type="tns:HeaderType" minOccurs="0" maxOccurs="unbounded" />
<element name="method" type="tns:MethodType" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="string" use="required" />
</complexType>
<complexType name="HeaderType">
<complexContent>
<extension base="tns:TransferObjectType">
<sequence>
<element name="exception" type="tns:ExceptionType" minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="ID" use="required" />
</extension>
</complexContent>
</complexType>
<complexType name="HeaderRefType">
<attribute name="name" type="IDREF" />
<attribute name="direction">
<simpleType>
<restriction base="string">
<enumeration value="in" />
<enumeration value="out" />
<enumeration value="inout" />
</restriction>
</simpleType>
</attribute>
</complexType>
<complexType name="MethodType">
<sequence>
<element name="exception" type="tns:ExceptionType" minOccurs="0" maxOccurs="unbounded" />
<element name="header" type="tns:HeaderRefType" minOccurs="0" maxOccurs="unbounded" />
<element name="argument" type="tns:ArgumentType" minOccurs="0" maxOccurs="unbounded" />
<element name="return" type="tns:ReturnType" minOccurs="0" maxOccurs="1" />
</sequence>
<attribute name="name" type="string" use="required" />
<attribute name="controller" type="string" use="required" />
</complexType>
<complexType name="TransferObjectType" abstract="true">
<sequence>
<element name="type" type="tns:TypeConversionType" />
</sequence>
</complexType>
<complexType name="ExceptionType">
<complexContent>
<extension base="tns:TransferObjectType">
<attribute name="name" type="string" use="required" />
</extension>
</complexContent>
</complexType>
<complexType name="ArgumentType">
<complexContent>
<extension base="tns:TransferObjectType">
<attribute name="name" type="string" use="required" />
</extension>
</complexContent>
</complexType>
<complexType name="ReturnType">
<complexContent>
<extension base="tns:TransferObjectType" />
</complexContent>
</complexType>
<complexType name="TypeConversionType">
<attribute name="php-type" type="string" use="required" />
<attribute name="xml-type" type="QName" use="required" />
<attribute name="converter" type="string" use="optional" />
</complexType>
</schema>