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:
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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>
|
Reference in New Issue
Block a user