Initial commit with Symfony 2.1+Vendors

Signed-off-by: Gergely POLONKAI (W00d5t0ck) <polesz@w00d5t0ck.info>
This commit is contained in:
Polonkai Gergely
2012-07-01 09:52:20 +02:00
commit 082a0130c2
5381 changed files with 416709 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
language: php
php:
- 5.3
- 5.4
before_script:
- wget http://getcomposer.org/composer.phar
- php composer.phar install

View File

@@ -0,0 +1,171 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Cache class handles the @Cache annotation parts.
*
* @author Fabien Potencier <fabien@symfony.com>
* @Annotation
*/
class Cache extends ConfigurationAnnotation
{
/**
* The expiration date as a valid date for the strtotime() function.
*
* @var string
*/
protected $expires;
/**
* The number of seconds that the response is considered fresh by a private
* cache like a web browser.
*
* @var integer
*/
protected $maxage;
/**
* The number of seconds that the response is considered fresh by a public
* cache like a reverse proxy cache.
*
* @var integer
*/
protected $smaxage;
/**
* Whether or not the response is public or not.
*
* @var integer
*/
protected $public;
/**
* Additional "Vary:"-headers
*
* @var array
*/
protected $vary = array();
/**
* Returns the expiration date for the Expires header field.
*
* @return string
*/
public function getExpires()
{
return $this->expires;
}
/**
* Sets the expiration date for the Expires header field.
*
* @param string $expires A valid php date
*/
public function setExpires($expires)
{
$this->expires = $expires;
}
/**
* Sets the number of seconds for the max-age cache-control header field.
*
* @param integer $maxage A number of seconds
*/
public function setMaxAge($maxage)
{
$this->maxage = $maxage;
}
/**
* Returns the number of seconds the response is considered fresh by a
* private cache.
*
* @return integer
*/
public function getMaxAge()
{
return $this->maxage;
}
/**
* Sets the number of seconds for the s-maxage cache-control header field.
*
* @param integer $smaxage A number of seconds
*/
public function setSMaxAge($smaxage)
{
$this->smaxage = $smaxage;
}
/**
* Returns the number of seconds the response is considered fresh by a
* public cache.
*
* @return integer
*/
public function getSMaxAge()
{
return $this->smaxage;
}
/**
* Returns whether or not a response is public.
*
* @return Boolean
*/
public function isPublic()
{
return (Boolean) $this->public;
}
/**
* Sets a response public.
*
* @param Boolean $public A boolean value
*/
public function setPublic($public)
{
$this->public = (Boolean) $public;
}
/**
* Returns the custom "Vary"-headers
*
* @return array
*/
public function getVary()
{
return $this->vary;
}
/**
* Add additional "Vary:"-headers
*
* @param array $vary
*/
public function setVary($vary)
{
$this->vary = $vary;
}
/**
* Returns the annotation alias name.
*
* @return string
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'cache';
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/**
* Base configuration annotation.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
abstract class ConfigurationAnnotation implements ConfigurationInterface
{
public function __construct(array $values)
{
foreach ($values as $k => $v) {
if (!method_exists($this, $name = 'set'.$k)) {
throw new \RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, get_class($this)));
}
$this->$name($v);
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* ConfigurationInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ConfigurationInterface
{
/**
* Returns the alias name for an annotated configuration.
*
* @return string
*/
function getAliasName();
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Method class handles the @Method annotation parts.
*
* @author Fabien Potencier <fabien@symfony.com>
* @Annotation
*/
class Method extends ConfigurationAnnotation
{
/**
* An array of restricted HTTP methods.
*
* @var array
*/
protected $methods = array();
/**
* Returns the array of HTTP methods.
*
* @return array
*/
public function getMethods()
{
return $this->methods;
}
/**
* Sets the HTTP methods.
*
* @param array|string $methods An HTTP method or an array of HTTP methods
*/
public function setMethods($methods)
{
$this->methods = is_array($methods) ? $methods : array($methods);
}
/**
* Sets the HTTP methods.
*
* @param array|string $methods An HTTP method or an array of HTTP methods
*/
public function setValue($methods)
{
$this->setMethods($methods);
}
/**
* Returns the annotation alias name.
*
* @return string
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'method';
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The ParamConverter class handles the @ParamConverter annotation parts.
*
* @ParamConverter("post", class="BlogBundle:Post")
*
* @author Fabien Potencier <fabien@symfony.com>
* @Annotation
*/
class ParamConverter extends ConfigurationAnnotation
{
/**
* The parameter name.
*
* @var string
*/
protected $name;
/**
* The parameter class.
*
* @var string
*/
protected $class;
/**
* An array of options.
*
* @var array
*/
protected $options = array();
/**
* Whether or not the parameter is optional.
*
* @var Boolean
*/
protected $optional = false;
/**
* Returns the parameter name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Sets the parameter name.
*
* @param string $name The parameter name
*/
public function setValue($name)
{
$this->setName($name);
}
/**
* Sets the parameter name.
*
* @param string $name The parameter name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Returns the parameter class name.
*
* @return string $name
*/
public function getClass()
{
return $this->class;
}
/**
* Sets the parameter class name.
*
* @param string $class The parameter class name
*/
public function setClass($class)
{
$this->class = $class;
}
/**
* Returns an array of options.
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* Sets an array of options.
*
* @param array $options An array of options
*/
public function setOptions($options)
{
$this->options = $options;
}
/**
* Sets whether or not the parameter is optional.
*
* @param Boolean $optional Wether the parameter is optional
*/
public function setIsOptional($optional)
{
$this->optional = (Boolean) $optional;
}
/**
* Returns whether or not the parameter is optional.
*
* @return Boolean
*/
public function isOptional()
{
return $this->optional;
}
/**
* Returns the annotation alias name.
*
* @return string
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'converters';
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
use Symfony\Component\Routing\Annotation\Route as BaseRoute;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* @author Kris Wallsmith <kris@symfony.com>
* @Annotation
*/
class Route extends BaseRoute
{
protected $service;
public function setService($service)
{
$this->service = $service;
}
public function getService()
{
return $this->service;
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Template class handles the @Template annotation parts.
*
* @author Fabien Potencier <fabien@symfony.com>
* @Annotation
*/
class Template extends ConfigurationAnnotation
{
/**
* The template reference.
*
* @var TemplateReference
*/
protected $template;
/**
* The template engine used when a specific template isnt specified
*
* @var string
*/
protected $engine = 'twig';
/**
* The associative array of template variables.
*
* @var array
*/
protected $vars = array();
/**
* Should the template be streamed?
*
* @var Boolean
*/
protected $streamable = false;
/**
* Returns the array of templates variables.
*
* @return array
*/
public function getVars()
{
return $this->vars;
}
/**
* @param Boolean $streamable
*/
public function setIsStreamable($streamable)
{
$this->streamable = $streamable;
}
/**
* @return Boolean
*/
public function isStreamable()
{
return (Boolean) $this->streamable;
}
/**
* Sets the template variables
*
* @param array $vars The template variables
*/
public function setVars($vars)
{
$this->vars = $vars;
}
/**
* Returns the engine used when guessing template names
*
* @return string
*/
public function getEngine()
{
return $this->engine;
}
/**
* Sets the engine used when guessing template names
*
* @param string
*/
public function setEngine($engine)
{
$this->engine = $engine;
}
/**
* Sets the template logic name.
*
* @param string $template The template logic name
*/
public function setValue($template)
{
$this->setTemplate($template);
}
/**
* Returns the template reference.
*
* @return TemplateReference
*/
public function getTemplate()
{
return $this->template;
}
/**
* Sets the template reference.
*
* @param TemplateReference|string $template The template reference
*/
public function setTemplate($template)
{
$this->template = $template;
}
/**
* Returns the annotation alias name.
*
* @return string
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'template';
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds tagged request.param_converter services to converter.manager service
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class AddParamConverterPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('sensio_framework_extra.converter.manager')) {
return;
}
$definition = $container->getDefinition('sensio_framework_extra.converter.manager');
foreach ($container->findTaggedServiceIds('request.param_converter') as $id => $attributes) {
$definition->addMethodCall('add', array(new Reference($id), isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0));
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
/**
* FrameworkExtraBundle configuration structure.
*
* @author Henrik Bjornskov <hb@peytz.dk>
*/
class Configuration
{
/**
* Generates the configuration tree.
*
* @return Symfony\Component\Config\Definition\NodeInterface
*/
public function getConfigTree()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('sensio_framework_extra', 'array');
$rootNode
->children()
->arrayNode('router')
->addDefaultsIfNotSet()
->children()
->booleanNode('annotations')->defaultTrue()->end()
->end()
->end()
->arrayNode('request')
->addDefaultsIfNotSet()
->children()
->booleanNode('converters')->defaultTrue()->end()
->end()
->end()
->arrayNode('view')
->addDefaultsIfNotSet()
->children()
->booleanNode('annotations')->defaultTrue()->end()
->end()
->end()
->arrayNode('cache')
->addDefaultsIfNotSet()
->children()
->booleanNode('annotations')->defaultTrue()->end()
->end()
->end()
->end()
;
return $treeBuilder->buildTree();
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Definition\Processor;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* SensioFrameworkExtraExtension.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SensioFrameworkExtraExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$processor = new Processor();
$configuration = new Configuration();
$config = $processor->process($configuration->getConfigTree(), $configs);
$loader->load('services.xml');
$annotationsToLoad = array();
if ($config['router']['annotations']) {
$annotationsToLoad[] = 'routing.xml';
}
if ($config['request']['converters']) {
$annotationsToLoad[] = 'converters.xml';
}
if ($config['view']['annotations']) {
$annotationsToLoad[] = 'view.xml';
}
if ($config['cache']['annotations']) {
$annotationsToLoad[] = 'cache.xml';
}
if ($annotationsToLoad) {
// must be first
$loader->load('annotations.xml');
foreach ($annotationsToLoad as $config) {
$loader->load($config);
}
}
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/symfony_extra';
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The CacheListener class has the responsability to modify the
* Response object when a controller uses the @Cache annotation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CacheListener
{
/**
* Modifies the response to apply HTTP expiration header fields.
*
* @param FilterResponseEvent $event The notified event
*/
public function onKernelResponse(FilterResponseEvent $event)
{
if (!$configuration = $event->getRequest()->attributes->get('_cache')) {
return;
}
$response = $event->getResponse();
if (!$response->isSuccessful()) {
return;
}
if (null !== $configuration->getSMaxAge()) {
$response->setSharedMaxAge($configuration->getSMaxAge());
}
if (null !== $configuration->getMaxAge()) {
$response->setMaxAge($configuration->getMaxAge());
}
if (null !== $configuration->getExpires()) {
$date = \DateTime::createFromFormat('U', strtotime($configuration->getExpires()), new \DateTimeZone('UTC'));
$response->setExpires($date);
}
if (null !== $configuration->getVary()) {
$response->setVary($configuration->getVary());
}
if ($configuration->isPublic()) {
$response->setPublic();
}
$event->setResponse($response);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The ControllerListener class parses annotation blocks located in
* controller classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ControllerListener
{
/**
* @var \Doctrine\Common\Annotations\Reader
*/
protected $reader;
/**
* Constructor.
*
* @param Reader $reader An Reader instance
*/
public function __construct(Reader $reader)
{
$this->reader = $reader;
}
/**
* Modifies the Request object to apply configuration information found in
* controllers annotations like the template to render or HTTP caching
* configuration.
*
* @param FilterControllerEvent $event A FilterControllerEvent instance
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!is_array($controller = $event->getController())) {
return;
}
$object = new \ReflectionObject($controller[0]);
$method = $object->getMethod($controller[1]);
$request = $event->getRequest();
foreach (array_merge($this->reader->getClassAnnotations($object), $this->reader->getMethodAnnotations($method)) as $configuration) {
if ($configuration instanceof ConfigurationInterface) {
$request->attributes->set('_'.$configuration->getAliasName(), $configuration);
}
}
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The ParamConverterListener handles the @ParamConverter annotation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ParamConverterListener
{
/**
* @var Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager
*/
protected $manager;
/**
* Constructor.
*
* @param ParamConverterManager $manager A ParamConverterManager instance
*/
public function __construct(ParamConverterManager $manager)
{
$this->manager = $manager;
}
/**
* Modifies the ParamConverterManager instance.
*
* @param FilterControllerEvent $event A FilterControllerEvent instance
*/
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
$request = $event->getRequest();
$configurations = array();
if ($configuration = $request->attributes->get('_converters')) {
foreach (is_array($configuration) ? $configuration : array($configuration) as $configuration) {
$configurations[$configuration->getName()] = $configuration;
}
}
if (is_array($controller)) {
$r = new \ReflectionMethod($controller[0], $controller[1]);
} else {
$r = new \ReflectionFunction($controller);
}
// automatically apply conversion for non-configured objects
foreach ($r->getParameters() as $param) {
if (!$param->getClass()) {
continue;
}
$name = $param->getName();
if (!isset($configurations[$name])) {
$configuration = new ParamConverter(array());
$configuration->setName($name);
$configuration->setClass($param->getClass()->getName());
$configurations[$name] = $configuration;
}
$configurations[$name]->setIsOptional($param->isOptional());
}
$this->manager->apply($request, $configurations);
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The TemplateListener class handles the @Template annotation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TemplateListener
{
/**
* @var Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
/**
* Constructor.
*
* @param ContainerInterface $container The service container instance
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Guesses the template name to render and its variables and adds them to
* the request object.
*
* @param FilterControllerEvent $event A FilterControllerEvent instance
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!is_array($controller = $event->getController())) {
return;
}
$request = $event->getRequest();
if (!$configuration = $request->attributes->get('_template')) {
return;
}
if (!$configuration->getTemplate()) {
$guesser = $this->container->get('sensio_framework_extra.view.guesser');
$configuration->setTemplate($guesser->guessTemplateName($controller, $request, $configuration->getEngine()));
}
$request->attributes->set('_template', $configuration->getTemplate());
$request->attributes->set('_template_vars', $configuration->getVars());
$request->attributes->set('_template_streamable', $configuration->isStreamable());
// all controller method arguments
if (!$configuration->getVars()) {
$r = new \ReflectionObject($controller[0]);
$vars = array();
foreach ($r->getMethod($controller[1])->getParameters() as $param) {
$vars[] = $param->getName();
}
$request->attributes->set('_template_default_vars', $vars);
}
}
/**
* Renders the template and initializes a new response object with the
* rendered template content.
*
* @param GetResponseForControllerResultEvent $event A GetResponseForControllerResultEvent instance
*/
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$request = $event->getRequest();
$parameters = $event->getControllerResult();
$templating = $this->container->get('templating');
if (null === $parameters) {
if (!$vars = $request->attributes->get('_template_vars')) {
if (!$vars = $request->attributes->get('_template_default_vars')) {
return;
}
}
$parameters = array();
foreach ($vars as $var) {
$parameters[$var] = $request->attributes->get($var);
}
}
if (!is_array($parameters)) {
return $parameters;
}
if (!$template = $request->attributes->get('_template')) {
return $parameters;
}
if (!$request->attributes->get('_template_streamable')) {
$event->setResponse($templating->renderResponse($template, $parameters));
} else {
$callback = function () use ($templating, $template, $parameters) {
return $templating->stream($template, $parameters);
};
$event->setResponse(new StreamedResponse($callback));
}
}
}

View File

@@ -0,0 +1,19 @@
Copyright (c) 2010,2011 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,6 @@
SensioFrameworkExtraBundle
==========================
This bundle provides a way to configure your controllers with annotations.
Read about it on its [official homepage](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html).

View File

@@ -0,0 +1,108 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\Common\Persistence\ManagerRegistry;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* DoctrineParamConverter.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DoctrineParamConverter implements ParamConverterInterface
{
/**
* @var ManagerRegistry
*/
protected $registry;
public function __construct(ManagerRegistry $registry = null)
{
$this->registry = $registry;
}
public function apply(Request $request, ConfigurationInterface $configuration)
{
$class = $configuration->getClass();
$options = $this->getOptions($configuration);
// find by identifier?
if (false === $object = $this->find($class, $request, $options)) {
// find by criteria
if (false === $object = $this->findOneBy($class, $request, $options)) {
throw new \LogicException('Unable to guess how to get a Doctrine instance from the request information.');
}
}
if (null === $object && false === $configuration->isOptional()) {
throw new NotFoundHttpException(sprintf('%s object not found.', $class));
}
$request->attributes->set($configuration->getName(), $object);
return true;
}
protected function find($class, Request $request, $options)
{
if (!$request->attributes->has('id')) {
return false;
}
return $this->registry->getRepository($class, $options['entity_manager'])->find($request->attributes->get('id'));
}
protected function findOneBy($class, Request $request, $options)
{
$criteria = array();
$metadata = $this->registry->getManager($options['entity_manager'])->getClassMetadata($class);
foreach ($request->attributes->all() as $key => $value) {
if ($metadata->hasField($key)) {
$criteria[$key] = $value;
}
}
if (!$criteria) {
return false;
}
return $this->registry->getRepository($class, $options['entity_manager'])->findOneBy($criteria);
}
public function supports(ConfigurationInterface $configuration)
{
if (null === $this->registry) {
return false;
}
if (null === $configuration->getClass()) {
return false;
}
$options = $this->getOptions($configuration);
// Doctrine Entity?
return ! $this->registry->getManager($options['entity_manager'])
->getMetadataFactory()
->isTransient($configuration->getClass());
}
protected function getOptions(ConfigurationInterface $configuration)
{
return array_replace(array(
'entity_manager' => null,
), $configuration->getOptions());
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Symfony\Component\HttpFoundation\Request;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
*
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ParamConverterInterface
{
function apply(Request $request, ConfigurationInterface $configuration);
function supports(ConfigurationInterface $configuration);
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Managers converters.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Henrik Bjornskov <henrik@bjrnskov.dk>
*/
class ParamConverterManager
{
/**
* @var array
*/
protected $converters = array();
/**
* Applies all converters to the passed configurations and stops when a
* converter is applied it will move on to the next configuration and so on.
*
* @param Request $request
* @param array|object $configurations
*/
public function apply(Request $request, $configurations)
{
if (is_object($configurations)) {
$configurations = array($configurations);
}
foreach ($configurations as $configuration) {
// If the value is already an instance of the class we are trying to convert it into
// we should continue as no convertion is required
$value = $request->attributes->get($configuration->getName());
$className = $configuration->getClass();
if (is_object($value) && $value instanceof $className) {
continue;
}
foreach ($this->all() as $converter) {
if ($converter->supports($configuration)) {
if ($converter->apply($request, $configuration)) {
continue 2;
}
}
}
}
}
/**
* Adds a parameter converter.
*
* @param ParamConverterInterface $converter A ParamConverterInterface instance
* @param integer $priority The priority (between -10 and 10)
*/
public function add(ParamConverterInterface $converter, $priority = 0)
{
if (!isset($this->converters[$priority])) {
$this->converters[$priority] = array();
}
$this->converters[$priority][] = $converter;
}
/**
* Returns all registered param converters.
*
* @return array An array of param converters
*/
public function all()
{
krsort($this->converters);
$converters = array();
foreach ($this->converters as $all) {
$converters = array_merge($converters, $all);
}
return $converters;
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sensio_framework_extra.controller.listener.class">Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.controller.listener" class="%sensio_framework_extra.controller.listener.class%">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
<argument type="service" id="annotation_reader" />
</service>
</services>
</container>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sensio_framework_extra.cache.listener" class="Sensio\Bundle\FrameworkExtraBundle\EventListener\CacheListener">
<tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" />
</service>
</services>
</container>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sensio_framework_extra.converter.listener.class">Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener</parameter>
<parameter key="sensio_framework_extra.converter.manager.class">Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager</parameter>
<parameter key="sensio_framework_extra.converter.doctrine.class">Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.converter.listener" class="%sensio_framework_extra.converter.listener.class%">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
<argument type="service" id="sensio_framework_extra.converter.manager" />
</service>
<service id="sensio_framework_extra.converter.manager" class="%sensio_framework_extra.converter.manager.class%" />
<service id="sensio_framework_extra.converter.doctrine.orm" class="%sensio_framework_extra.converter.doctrine.class%">
<tag name="request.param_converter" />
<argument type="service" id="doctrine" on-invalid="ignore" />
</service>
</services>
</container>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sensio_framework_extra.routing.loader.annot_dir.class">Symfony\Component\Routing\Loader\AnnotationDirectoryLoader</parameter>
<parameter key="sensio_framework_extra.routing.loader.annot_file.class">Symfony\Component\Routing\Loader\AnnotationFileLoader</parameter>
<parameter key="sensio_framework_extra.routing.loader.annot_class.class">Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.routing.loader.annot_dir" class="%sensio_framework_extra.routing.loader.annot_dir.class%" public="false">
<tag name="routing.loader" />
<argument type="service" id="file_locator" />
<argument type="service" id="sensio_framework_extra.routing.loader.annot_class" />
</service>
<service id="sensio_framework_extra.routing.loader.annot_file" class="%sensio_framework_extra.routing.loader.annot_file.class%" public="false">
<tag name="routing.loader" />
<argument type="service" id="file_locator" />
<argument type="service" id="sensio_framework_extra.routing.loader.annot_class" />
</service>
<service id="sensio_framework_extra.routing.loader.annot_class" class="%sensio_framework_extra.routing.loader.annot_class.class%" public="false">
<tag name="routing.loader" />
<argument type="service" id="annotation_reader" />
</service>
</services>
</container>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sensio_framework_extra.view.guesser.class">Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.view.guesser" class="%sensio_framework_extra.view.guesser.class%">
<argument type="service" id="kernel" />
</service>
</services>
</container>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sensio_framework_extra.view.listener.class">Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.view.listener" class="%sensio_framework_extra.view.listener.class%">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" />
<argument type="service" id="service_container" />
</service>
</services>
</container>

View File

@@ -0,0 +1,60 @@
@Cache
======
Usage
-----
The ``@Cache`` annotation makes it easy to define HTTP caching::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
/**
* @Cache(expires="tomorrow")
*/
public function indexAction()
{
}
You can also use the annotation on a class to define caching for all methods::
/**
* @Cache(expires="tomorrow")
*/
class BlogController extends Controller
{
}
When there is a conflict between the class configuration and the method
configuration, the latter overrides the former::
/**
* @Cache(expires="tomorrow")
*/
class BlogController extends Controller
{
/**
* @Cache(expires="+2 days")
*/
public function indexAction()
{
}
}
Attributes
----------
Here is a list of accepted attributes and their HTTP header equivalent:
============================== ===============
Annotation Response Method
============================== ===============
``@Cache(expires="tomorrow")`` ``$response->setExpires()``
``@Cache(smaxage="15")`` ``$response->setSharedMaxAge()``
``@Cache(maxage="15")`` ``$response->setMaxAge()``
``@Cache(vary=["Cookie"])`` ``$response->setVary()``
============================== ===============
.. note::
The ``expires`` attribute takes any valid date understood by the PHP
``strtotime()`` function.

View File

@@ -0,0 +1,101 @@
@ParamConverter
===============
Usage
-----
The ``@ParamConverter`` annotation calls *converters* to convert request
parameters to objects. These objects are stored as request attributes and so
they can be injected as controller method arguments::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* @Route("/blog/{id}")
* @ParamConverter("post", class="SensioBlogBundle:Post")
*/
public function showAction(Post $post)
{
}
Several things happens under the hood:
* The converter tries to get a ``SensioBlogBundle:Post`` object from the
request attributes (request attributes comes from route placeholders -- here
``id``);
* If no ``Post`` object is found, a ``404`` Response is generated;
* If a ``Post`` object is found, a new ``post`` request attribute is defined
(accessible via ``$request->attributes->get('post')``);
* As for any other request attribute, it is automatically injected in the
controller when present in the method signature.
If you use type hinting as in the example above, you can even omit the
``@ParamConverter`` annotation altogether::
// automatic with method signature
public function showAction(Post $post)
{
}
Built-in Converters
-------------------
The bundle has only one built-in converter, the Doctrine one.
Doctrine Converter
~~~~~~~~~~~~~~~~~~
By default, the Doctrine converter uses the *default* entity manager. This can
be configured with the ``entity_manager`` option::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* @Route("/blog/{id}")
* @ParamConverter("post", class="SensioBlogBundle:Post", options={"entity_manager" = "foo"})
*/
public function showAction(Post $post)
{
}
Creating a Converter
--------------------
All converters must implement the
:class:`Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterInterface`::
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Symfony\Component\HttpFoundation\Request;
interface ParamConverterInterface
{
function apply(Request $request, ConfigurationInterface $configuration);
function supports(ConfigurationInterface $configuration);
}
The ``supports()`` method must return ``true`` when it is able to convert the
given configuration (a ``ParamConverter`` instance).
The ``ParamConverter`` instance has three information about the annotation:
* ``name``: The attribute name;
* ``class``: The attribute class name (can be any string representing a class
name);
* ``options``: An array of options
The ``apply()`` method is called whenever a configuration is supported. Based
on the request attributes, it should set an attribute named
``$configuration->getName()``, which stores an object of class
``$configuration->getClass()``.
.. tip::
Use the ``DoctrineParamConverter`` class as a template for your own converters.

View File

@@ -0,0 +1,166 @@
@Route and @Method
==================
Usage
-----
The @Route annotation maps a route pattern with a controller::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class PostController extends Controller
{
/**
* @Route("/")
*/
public function indexAction()
{
// ...
}
}
The ``index`` action of the ``Post`` controller is now mapped to the ``/``
URL. This is equivalent to the following YAML configuration:
.. code-block:: yaml
blog_home:
pattern: /
defaults: { _controller: SensioBlogBundle:Post:index }
Like any route pattern, you can define placeholders, requirements, and default
values::
/**
* @Route("/{id}", requirements={"id" = "\d+"}, defaults={"foo" = "bar"})
*/
public function showAction($id)
{
}
You can also match more than one URL by defining additional ``@Route``
annotations::
/**
* @Route("/", defaults={"id" = 1})
* @Route("/{id}")
*/
public function showAction($id)
{
}
Activation
----------
The routes need to be imported to be active as any other routing resources
(note the ``annotation`` type):
.. code-block:: yaml
# app/config/routing.yml
# import routes from a controller class
post:
resource: "@SensioBlogBundle/Controller/PostController.php"
type: annotation
You can also import a whole directory:
.. code-block:: yaml
# import routes from a controller directory
blog:
resource: "@SensioBlogBundle/Controller"
type: annotation
As for any other resource, you can "mount" the routes under a given prefix:
.. code-block:: yaml
post:
resource: "@SensioBlogBundle/Controller/PostController.php"
prefix: /blog
type: annotation
Route Name
----------
A route defined with the ``@Route`` annotation is given a default name composed
of the bundle name, the controller name and the action name. That would be
``sensio_blog_post_index`` for the above example;
The ``name`` attribute can be used to override this default route name::
/**
* @Route("/", name="blog_home")
*/
public function indexAction()
{
// ...
}
Route Prefix
------------
A ``@Route`` annotation on a controller class defines a prefix for all action
routes::
/**
* @Route("/blog")
*/
class PostController extends Controller
{
/**
* @Route("/{id}")
*/
public function showAction($id)
{
}
}
The ``show`` action is now mapped to the ``/blog/{id}`` pattern.
Route Method
------------
There is a shortcut ``@Method`` annotation to specify the HTTP method allowed
for the route. To use it, import the ``Method`` annotation namespace::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/**
* @Route("/blog")
*/
class PostController extends Controller
{
/**
* @Route("/edit/{id}")
* @Method({"GET", "POST"})
*/
public function editAction($id)
{
}
}
The ``edit`` action is now mapped to the ``/blog/edit/{id}`` pattern if the HTTP
method used is either GET or POST.
The ``@Method`` annotation is only considered when an action is annotated with
``@Route``.
Controller as Service
---------------------
The ``@Route`` annotation on a controller class can also be used to assign the
controller class to a service so that the controller resolver will instantiate
the controller by fetching it from the DI container instead of calling ``new
PostController()`` itself::
/**
* @Route(service="my_post_controller_service")
*/
class PostController extends Controller
{
// ...
}

View File

@@ -0,0 +1,74 @@
@Template
=========
Usage
-----
The ``@Template`` annotation associates a controller with a template name::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* @Template("SensioBlogBundle:Post:show.html.twig")
*/
public function showAction($id)
{
// get the Post
$post = ...;
return array('post' => $post);
}
When using the ``@Template`` annotation, the controller should return an
array of parameters to pass to the view instead of a ``Response`` object.
.. tip::
If the action returns a ``Response`` object, the ``@Template``
annotation is simply ignored.
If the template is named after the controller and action names, which is the
case for the above example, you can even omit the annotation value::
/**
* @Template
*/
public function showAction($id)
{
// get the Post
$post = ...;
return array('post' => $post);
}
And if the only parameters to pass to the template are method arguments, you
can use the ``vars`` attribute instead of returning an array. This is very
useful in combination with the ``@ParamConverter`` :doc:`annotation
<converters>`::
/**
* @ParamConverter("post", class="SensioBlogBundle:Post")
* @Template("SensioBlogBundle:Post:show.html.twig", vars={"post"})
*/
public function showAction(Post $post)
{
}
which, thanks to conventions, is equivalent to the following configuration::
/**
* @Template(vars={"post"})
*/
public function showAction(Post $post)
{
}
You can make it even more concise as all method arguments are automatically
passed to the template if the method returns ``null`` and no ``vars``
attribute is defined::
/**
* @Template
*/
public function showAction(Post $post)
{
}

View File

@@ -0,0 +1,145 @@
SensioFrameworkExtraBundle
==========================
The default Symfony2 ``FrameworkBundle`` implements a basic but robust and
flexible MVC framework. `SensioFrameworkExtraBundle`_ extends it to add sweet
conventions and annotations. It allows for more concise controllers.
Installation
------------
`Download`_ the bundle and put it under the ``Sensio\Bundle\`` namespace.
Then, like for any other bundle, include it in your Kernel class::
public function registerBundles()
{
$bundles = array(
...
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
);
...
}
Configuration
-------------
All features provided by the bundle are enabled by default when the bundle is
registered in your Kernel class.
The default configuration is as follow:
.. configuration-block::
.. code-block:: yaml
sensio_framework_extra:
router: { annotations: true }
request: { converters: true }
view: { annotations: true }
cache: { annotations: true }
.. code-block:: xml
<!-- xmlns:sensio-framework-extra="http://symfony.com/schema/dic/symfony_extra" -->
<sensio-framework-extra:config>
<router annotations="true" />
<request converters="true" />
<view annotations="true" />
<cache annotations="true" />
</sensio-framework-extra:config>
.. code-block:: php
// load the profiler
$container->loadFromExtension('sensio_framework_extra', array(
'router' => array('annotations' => true),
'request' => array('converters' => true),
'view' => array('annotations' => true),
'cache' => array('annotations' => true),
));
You can disable some annotations and conventions by defining one or more
settings to false.
Annotations for Controllers
---------------------------
Annotations are a great way to easily configure your controllers, from the
routes to the cache configuration.
Even if annotations are not a native feature of PHP, it still has several
advantages over the classic Symfony2 configuration methods:
* Code and configuration are in the same place (the controller class);
* Simple to learn and to use;
* Concise to write;
* Makes your Controller thin (as its sole responsibility is to get data from
the Model).
.. tip::
If you use view classes, annotations are a great way to avoid creating
view classes for simple and common use cases.
The following annotations are defined by the bundle:
.. toctree::
:maxdepth: 1
annotations/routing
annotations/converters
annotations/view
annotations/cache
This example shows all the available annotations in action::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/**
* @Route("/blog")
* @Cache(expires="tomorrow")
*/
class AnnotController extends Controller
{
/**
* @Route("/")
* @Template
*/
public function indexAction()
{
$posts = ...;
return array('posts' => $posts);
}
/**
* @Route("/{id}")
* @Method("GET")
* @ParamConverter("post", class="SensioBlogBundle:Post")
* @Template("SensioBlogBundle:Annot:post.html.twig", vars={"post"})
* @Cache(smaxage="15")
*/
public function showAction(Post $post)
{
}
}
As the ``showAction`` method follows some conventions, you can omit some
annotations::
/**
* @Route("/{id}")
* @Cache(smaxage="15")
*/
public function showAction(Post $post)
{
}
.. _`SensioFrameworkExtraBundle`: https://github.com/sensio/SensioFrameworkExtraBundle
.. _`Download`: http://github.com/sensio/SensioFrameworkExtraBundle

View File

@@ -0,0 +1,75 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Routing;
use Symfony\Component\Routing\Loader\AnnotationClassLoader;
use Symfony\Component\Routing\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* AnnotatedRouteControllerLoader is an implementation of AnnotationClassLoader
* that sets the '_controller' default based on the class and method names.
*
* It also parse the @Method annotation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class AnnotatedRouteControllerLoader extends AnnotationClassLoader
{
/**
* Configures the _controller default parameter and eventually the _method
* requirement of a given Route instance.
*
* @param Route $route A Route instance
* @param ReflectionClass $class A ReflectionClass instance
* @param ReflectionMethod $method A ReflectionClass method
*/
protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot)
{
// controller
$classAnnot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass);
if ($classAnnot && $service = $classAnnot->getService()) {
$route->setDefault('_controller', $service.':'.$method->getName());
} else {
$route->setDefault('_controller', $class->getName().'::'.$method->getName());
}
// requirements (@Method)
foreach ($this->reader->getMethodAnnotations($method) as $configuration) {
if ($configuration instanceof Method) {
$route->setRequirement('_method', implode('|', $configuration->getMethods()));
}
}
}
/**
* Makes the default route name more sane by removing common keywords.
*
* @param ReflectionClass $class A ReflectionClass instance
* @param ReflectionMethod $method A ReflectionMethod instance
* @return string
*/
protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method)
{
$routeName = parent::getDefaultRouteName($class, $method);
return preg_replace(array(
'/(bundle|controller)_/',
'/action(_\d+)?$/',
'/__/'
), array(
'_',
'\\1',
'_'
), $routeName);
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler\AddParamConverterPass;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* SensioFrameworkExtraBundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SensioFrameworkExtraBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new AddParamConverterPass());
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Templating;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
use CG\Core\ClassUtils;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The TemplateGuesser class handles the guessing of template name based on controller
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TemplateGuesser
{
/**
* @var Symfony\Component\HttpKernel\KernelInterface
*/
protected $kernel;
/**
* Constructor.
*
* @param KernelInterface $kernel A KernelInterface instance
*/
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
}
/**
* Guesses and returns the template name to render based on the controller
* and action names.
*
* @param array $controller An array storing the controller object and action method
* @param Request $request A Request instance
* @param string $engine
* @return TemplateReference template reference
* @throws \InvalidArgumentException
*/
public function guessTemplateName($controller, Request $request, $engine = 'twig')
{
$className = get_class($controller[0]);
// When JMSSecurityExtraBundle is used it generates Controller classes as MyAccountController__CG__
if (class_exists('CG\\Core\\ClassUtils')) {
$className = ClassUtils::getUserClass($className);
}
if (!preg_match('/Controller\\\(.+)Controller$/', $className, $matchController)) {
throw new \InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (it must be in a "Controller" sub-namespace and the class name must end with "Controller")', get_class($controller[0])));
}
if (!preg_match('/^(.+)Action$/', $controller[1], $matchAction)) {
throw new \InvalidArgumentException(sprintf('The "%s" method does not look like an action method (it does not end with Action)', $controller[1]));
}
$bundle = $this->getBundleForClass($className);
return new TemplateReference($bundle->getName(), $matchController[1], $matchAction[1], $request->getRequestFormat(), $engine);
}
/**
* Returns the Bundle instance in which the given class name is located.
*
* @param string $class A fully qualified controller class name
* @param Bundle $bundle A Bundle instance
* @throws \InvalidArgumentException
*/
protected function getBundleForClass($class)
{
$reflectionClass = new \ReflectionClass($class);
$bundles = $this->kernel->getBundles();
do {
$namespace = $reflectionClass->getNamespaceName();
foreach ($bundles as $bundle) {
if (0 === strpos($namespace, $bundle->getNamespace())) {
return $bundle;
}
}
$reflectionClass = $reflectionClass->getParentClass();
} while ($reflectionClass);
throw new \InvalidArgumentException(sprintf('The "%s" class does not belong to a registered bundle.', $class));
}
}

View File

@@ -0,0 +1,26 @@
{
"name": "sensio/framework-extra-bundle",
"description": "This bundle provides a way to configure your controllers with annotations",
"keywords": ["annotations","controllers"],
"type": "symfony-bundle",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"require": {
"symfony/framework-bundle": "2.1.*",
"doctrine/common": ">=2.1,<2.4-dev"
},
"autoload": {
"psr-0": { "Sensio\\Bundle\\FrameworkExtraBundle": "" }
},
"target-dir": "Sensio/Bundle/FrameworkExtraBundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
}
}