added annotation support
This commit is contained in:
parent
05a96310b3
commit
8367556323
|
@ -37,8 +37,9 @@ QuickStart
|
||||||
|
|
||||||
// src/Acme/DemoBundle/Controller/DemoController.php
|
// src/Acme/DemoBundle/Controller/DemoController.php
|
||||||
/**
|
/**
|
||||||
* @ws:Method('hello')
|
* @ws:Method("Hello")
|
||||||
* @ws:Param('name', type = 'string')
|
* @ws:Param("name", type = "string")
|
||||||
|
* @ws:Result(type = "string")
|
||||||
*/
|
*/
|
||||||
public function helloAction($name)
|
public function helloAction($name)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
<parameters>
|
<parameters>
|
||||||
<parameter key="webservice.annotations.reader.class">Doctrine\Common\Annotations\AnnotationReader</parameter>
|
<parameter key="webservice.annotations.reader.class">Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationReader</parameter>
|
||||||
<parameter key="webservice.annotations.parser.class">Doctrine\Common\Annotations\Parser</parameter>
|
<parameter key="webservice.annotations.parser.class">Bundle\WebServiceBundle\ServiceDefinition\Loader\AnnotationParser</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of the WebServiceBundle.
|
||||||
|
*
|
||||||
|
* (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 Bundle\WebServiceBundle\ServiceDefinition\Annotation;
|
||||||
|
|
||||||
|
class Method
|
||||||
|
{
|
||||||
|
private $name;
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
public function __construct($values)
|
||||||
|
{
|
||||||
|
$this->name = isset($values['value']) ? $values['value'] : null;
|
||||||
|
$this->service = isset($values['service']) ? $values['service'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName($default = null)
|
||||||
|
{
|
||||||
|
return $this->name !== null ? $this->name : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getService()
|
||||||
|
{
|
||||||
|
return $this->service;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of the WebServiceBundle.
|
||||||
|
*
|
||||||
|
* (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 Bundle\WebServiceBundle\ServiceDefinition\Annotation;
|
||||||
|
|
||||||
|
class Param extends TypedElement
|
||||||
|
{
|
||||||
|
private $name;
|
||||||
|
|
||||||
|
public function __construct($values)
|
||||||
|
{
|
||||||
|
parent::__construct($values);
|
||||||
|
|
||||||
|
$this->name = $values['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of the WebServiceBundle.
|
||||||
|
*
|
||||||
|
* (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 Bundle\WebServiceBundle\ServiceDefinition\Annotation;
|
||||||
|
|
||||||
|
class Result extends TypedElement
|
||||||
|
{
|
||||||
|
public function __construct($values)
|
||||||
|
{
|
||||||
|
parent::__construct($values);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of the WebServiceBundle.
|
||||||
|
*
|
||||||
|
* (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 Bundle\WebServiceBundle\ServiceDefinition\Annotation;
|
||||||
|
|
||||||
|
abstract class TypedElement
|
||||||
|
{
|
||||||
|
private $phpType;
|
||||||
|
private $xmlType;
|
||||||
|
|
||||||
|
public function __construct($values)
|
||||||
|
{
|
||||||
|
foreach(array('type', 'phpType') as $key)
|
||||||
|
{
|
||||||
|
if(isset($values[$key]))
|
||||||
|
{
|
||||||
|
$this->phpType = $values[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->xmlType = isset($values['xmlType']) ? $values['xmlType'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPhpType()
|
||||||
|
{
|
||||||
|
return $this->phpType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getXmlType()
|
||||||
|
{
|
||||||
|
return $this->xmlType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,59 +1,41 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the Symfony package.
|
* This file is part of the WebServiceBundle.
|
||||||
*
|
*
|
||||||
* (c) Fabien Potencier <fabien@symfony.com>
|
* (c) Christian Kerl <christian-kerl@web.de>
|
||||||
*
|
*
|
||||||
* For the full copyright and license information, please view the LICENSE
|
* This source file is subject to the MIT license that is bundled
|
||||||
* file that was distributed with this source code.
|
* with this source code in the file LICENSE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace Bundle\WebServiceBundle\ServiceDefinition\Loader;
|
namespace Bundle\WebServiceBundle\ServiceDefinition\Loader;
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\AnnotationReader;
|
|
||||||
use Symfony\Component\Config\Resource\FileResource;
|
|
||||||
|
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
|
||||||
|
use Bundle\WebServiceBundle\ServiceDefinition\Method;
|
||||||
|
use Bundle\WebServiceBundle\ServiceDefinition\Argument;
|
||||||
|
use Bundle\WebServiceBundle\ServiceDefinition\Type;
|
||||||
|
|
||||||
|
use Bundle\WebServiceBundle\ServiceDefinition\Annotation\Method as MethodAnnotation;
|
||||||
|
|
||||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||||
use Symfony\Component\Config\Loader\LoaderResolver;
|
use Symfony\Component\Config\Loader\LoaderResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AnnotationClassLoader loads routing information from a PHP class and its methods.
|
* AnnotationClassLoader loads ServiceDefinition from a PHP class and its methods.
|
||||||
*
|
*
|
||||||
* You need to define an implementation for the getRouteDefaults() method. Most of the
|
* Based on \Symfony\Component\Routing\Loader\AnnotationClassLoader
|
||||||
* time, this method should define some PHP callable to be called for the route
|
|
||||||
* (a controller in MVC speak).
|
|
||||||
*
|
*
|
||||||
* The @Route annotation can be set on the class (for global parameters),
|
* @author Christian Kerl <christian-kerl@web.de>
|
||||||
* and on each method.
|
|
||||||
*
|
|
||||||
* The @Route annotation main value is the route pattern. The annotation also
|
|
||||||
* recognizes three parameters: requirements, options, and name. The name parameter
|
|
||||||
* is mandatory. Here is an example of how you should be able to use it:
|
|
||||||
*
|
|
||||||
* /**
|
|
||||||
* * @Route("/Blog")
|
|
||||||
* * /
|
|
||||||
* class Blog
|
|
||||||
* {
|
|
||||||
* /**
|
|
||||||
* * @Route("/", name="blog_index")
|
|
||||||
* * /
|
|
||||||
* public function index()
|
|
||||||
* {
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* /**
|
|
||||||
* * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"})
|
|
||||||
* * /
|
|
||||||
* public function show()
|
|
||||||
* {
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
|
||||||
*/
|
*/
|
||||||
class AnnotationClassLoader implements LoaderInterface
|
class AnnotationClassLoader implements LoaderInterface
|
||||||
{
|
{
|
||||||
|
private $wsMethodAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Method';
|
||||||
|
private $wsParamAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Param';
|
||||||
|
private $wsResultAnnotationClass = 'Bundle\\WebServiceBundle\\ServiceDefinition\\Annotation\\Result';
|
||||||
|
|
||||||
protected $reader;
|
protected $reader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,33 +49,72 @@ class AnnotationClassLoader implements LoaderInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads from annotations from a class.
|
* Loads a ServiceDefinition from annotations from a class.
|
||||||
*
|
*
|
||||||
* @param string $class A class name
|
* @param string $class A class name
|
||||||
* @param string $type The resource type
|
* @param string $type The resource type
|
||||||
*
|
*
|
||||||
* @return RouteCollection A RouteCollection instance
|
* @return ServiceDefinition A ServiceDefinition instance
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When route can't be parsed
|
* @throws \InvalidArgumentException When route can't be parsed
|
||||||
*/
|
*/
|
||||||
public function load($class, $type = null)
|
public function load($class, $type = null)
|
||||||
{
|
{
|
||||||
if (!class_exists($class)) {
|
if (!class_exists($class))
|
||||||
|
{
|
||||||
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
|
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
|
||||||
}
|
}
|
||||||
|
|
||||||
$class = new \ReflectionClass($class);
|
$class = new \ReflectionClass($class);
|
||||||
|
|
||||||
$collection = new RouteCollection();
|
$definition = new ServiceDefinition();
|
||||||
$collection->addResource(new FileResource($class->getFileName()));
|
|
||||||
|
foreach ($class->getMethods() as $method)
|
||||||
foreach ($class->getMethods() as $method) {
|
{
|
||||||
|
$wsMethodAnnot = $this->reader->getMethodAnnotation($method, $this->wsMethodAnnotationClass);
|
||||||
|
|
||||||
|
if($wsMethodAnnot !== null)
|
||||||
|
{
|
||||||
|
$wsParamAnnots = $this->reader->getMethodAnnotations($method, $this->wsParamAnnotationClass);
|
||||||
|
$wsResultAnnot = $this->reader->getMethodAnnotation($method, $this->wsResultAnnotationClass);
|
||||||
|
|
||||||
|
$serviceMethod = new Method();
|
||||||
|
$serviceMethod->setName($wsMethodAnnot->getName($method->getName()));
|
||||||
|
$serviceMethod->setController($this->getController($method, $wsMethodAnnot));
|
||||||
|
|
||||||
|
foreach($wsParamAnnots as $wsParamAnnot)
|
||||||
|
{
|
||||||
|
$serviceArgument = new Argument();
|
||||||
|
$serviceArgument->setName($wsParamAnnot->getName());
|
||||||
|
$serviceArgument->setType(new Type($wsParamAnnot->getPhpType(), $wsParamAnnot->getXmlType()));
|
||||||
|
|
||||||
|
$serviceMethod->getArguments()->add($serviceArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($wsResultAnnot !== null)
|
||||||
|
{
|
||||||
|
$serviceMethod->setReturn(new Type($wsResultAnnot->getPhpType(), $wsResultAnnot->getXmlType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$definition->getMethods()->add($serviceMethod);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $collection;
|
return $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getController(\ReflectionMethod $method, MethodAnnotation $annotation)
|
||||||
|
{
|
||||||
|
if($annotation->getService() !== null)
|
||||||
|
{
|
||||||
|
return $annotation->getService() . ':' . $method->name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $method->class . '::' . $method->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this class supports the given resource.
|
* Returns true if this class supports the given resource.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the Symfony package.
|
* This file is part of the WebServiceBundle.
|
||||||
*
|
*
|
||||||
* (c) Fabien Potencier <fabien@symfony.com>
|
* (c) Christian Kerl <christian-kerl@web.de>
|
||||||
*
|
*
|
||||||
* For the full copyright and license information, please view the LICENSE
|
* This source file is subject to the MIT license that is bundled
|
||||||
* file that was distributed with this source code.
|
* with this source code in the file LICENSE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace Bundle\WebServiceBundle\ServiceDefinition\Loader;
|
namespace Bundle\WebServiceBundle\ServiceDefinition\Loader;
|
||||||
|
|
||||||
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
|
use Bundle\WebServiceBundle\ServiceDefinition\ServiceDefinition;
|
||||||
|
@ -21,7 +21,9 @@ use Symfony\Component\Config\FileLocator;
|
||||||
* AnnotationFileLoader loads ServiceDefinition from annotations set
|
* AnnotationFileLoader loads ServiceDefinition from annotations set
|
||||||
* on a PHP class and its methods.
|
* on a PHP class and its methods.
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* Based on \Symfony\Component\Routing\Loader\AnnotationFileLoader
|
||||||
|
*
|
||||||
|
* @author Christian Kerl <christian-kerl@web.de>
|
||||||
*/
|
*/
|
||||||
class AnnotationFileLoader extends FileLoader
|
class AnnotationFileLoader extends FileLoader
|
||||||
{
|
{
|
||||||
|
@ -50,20 +52,21 @@ class AnnotationFileLoader extends FileLoader
|
||||||
* @param string $file A PHP file path
|
* @param string $file A PHP file path
|
||||||
* @param string $type The resource type
|
* @param string $type The resource type
|
||||||
*
|
*
|
||||||
* @return RouteCollection A RouteCollection instance
|
* @return ServiceDefinition A ServiceDefinition instance
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed
|
* @throws \InvalidArgumentException When the file does not exist
|
||||||
*/
|
*/
|
||||||
public function load($file, $type = null)
|
public function load($file, $type = null)
|
||||||
{
|
{
|
||||||
$path = $this->locator->locate($file);
|
$path = $this->locator->locate($file);
|
||||||
|
|
||||||
$definiton = new ServiceDefinition();
|
$definition = new ServiceDefinition();
|
||||||
|
|
||||||
if ($class = $this->findClass($path)) {
|
if ($class = $this->findClass($path)) {
|
||||||
|
$definition = $this->loader->load($class, $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $definiton;
|
return $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of the WebServiceBundle.
|
||||||
|
*
|
||||||
|
* (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 Bundle\WebServiceBundle\ServiceDefinition\Loader;
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\Lexer;
|
||||||
|
use Doctrine\Common\Annotations\Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AnnotationParser allows multiple annotations of the same class to be present.
|
||||||
|
*
|
||||||
|
* @author Christian Kerl <christian-kerl@web.de>
|
||||||
|
*/
|
||||||
|
class AnnotationParser extends Parser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Annotations ::= Annotation {[ "*" ]* [Annotation]}*
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function Annotations()
|
||||||
|
{
|
||||||
|
$this->isNestedAnnotation = false;
|
||||||
|
|
||||||
|
$annotations = array();
|
||||||
|
$annot = $this->Annotation();
|
||||||
|
|
||||||
|
if ($annot !== false) {
|
||||||
|
$annotations[get_class($annot)][] = $annot;
|
||||||
|
$this->getLexer()->skipUntil(Lexer::T_AT);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($this->getLexer()->lookahead !== null && $this->getLexer()->isNextToken(Lexer::T_AT)) {
|
||||||
|
$this->isNestedAnnotation = false;
|
||||||
|
$annot = $this->Annotation();
|
||||||
|
|
||||||
|
if ($annot !== false) {
|
||||||
|
$annotations[get_class($annot)][] = $annot;
|
||||||
|
$this->getLexer()->skipUntil(Lexer::T_AT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $annotations;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of the WebServiceBundle.
|
||||||
|
*
|
||||||
|
* (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 Bundle\WebServiceBundle\ServiceDefinition\Loader;
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\AnnotationReader as BaseAnnotationReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AnnotationReader.
|
||||||
|
*
|
||||||
|
* @author Christian Kerl <christian-kerl@web.de>
|
||||||
|
*/
|
||||||
|
class AnnotationReader extends BaseAnnotationReader
|
||||||
|
{
|
||||||
|
public function getMethodAnnotation(\ReflectionMethod $method, $type)
|
||||||
|
{
|
||||||
|
$annotation = parent::getMethodAnnotation($method, $type);
|
||||||
|
|
||||||
|
if($annotation !== null && count($annotation) > 1)
|
||||||
|
{
|
||||||
|
throw new \LogicException(sprintf("There is more than one annotation of type '%s'!", $type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $annotation !== null ? $annotation[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMethodAnnotations(\ReflectionMethod $method, $type = null)
|
||||||
|
{
|
||||||
|
$annotations = parent::getMethodAnnotations($method);
|
||||||
|
|
||||||
|
return $type !== null && isset($annotations[$type]) ? $annotations[$type] : $annotations;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue