Update
This commit is contained in:
parent
a37785b391
commit
33458b2ca3
9915 changed files with 1247019 additions and 0 deletions
6
trunk/_vendor/sensio/framework-extra-bundle/.gitignore
vendored
Normal file
6
trunk/_vendor/sensio/framework-extra-bundle/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/vendor
|
||||
coverage
|
||||
/Tests/Fixtures/cache
|
||||
phpunit.xml
|
||||
composer.lock
|
||||
/.phpunit
|
||||
46
trunk/_vendor/sensio/framework-extra-bundle/.travis.yml
Normal file
46
trunk/_vendor/sensio/framework-extra-bundle/.travis.yml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache/files
|
||||
- .phpunit
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.3
|
||||
dist: precise
|
||||
- php: 7.0
|
||||
env: deps=low
|
||||
# Use the newer stack for HHVM as HHVM does not support Precise anymore since a long time and so Precise has an outdated version
|
||||
- php: hhvm-3.12
|
||||
sudo: required
|
||||
dist: trusty
|
||||
group: edge
|
||||
fast_finish: true
|
||||
|
||||
env:
|
||||
global:
|
||||
- deps=no
|
||||
|
||||
before_install:
|
||||
- export SYMFONY_PHPUNIT_DIR="$(pwd)/.phpunit"
|
||||
- if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi
|
||||
- echo memory_limit = -1 >> $INI_FILE
|
||||
- if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi
|
||||
|
||||
install:
|
||||
- if [[ "$TRAVIS_PHP_VERSION" == "5.3" ]]; then composer remove --dev --no-update symfony/psr-http-message-bridge zendframework/zend-diactoros; fi;
|
||||
- if [ "$deps" = "no" ]; then composer install; fi;
|
||||
- if [ "$deps" = "low" ]; then composer --prefer-lowest --prefer-stable update; fi;
|
||||
|
||||
script:
|
||||
- ./vendor/bin/simple-phpunit
|
||||
25
trunk/_vendor/sensio/framework-extra-bundle/CHANGELOG.md
Normal file
25
trunk/_vendor/sensio/framework-extra-bundle/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
3.0
|
||||
---
|
||||
|
||||
* fixed the Doctrine param converter that sent 500 when an entity was not found under some circumstances
|
||||
* ParamConverterInterface now uses ParamConverter as a type hint instead of ConfigurationInterface
|
||||
* added support for @Security
|
||||
* added support for HTTP validation cache in @Cache (ETag and LastModified)
|
||||
|
||||
2.2
|
||||
---
|
||||
|
||||
* added the possibility to configure the repository method to use for the
|
||||
Doctrine converter via the repository_method option.
|
||||
* [BC break] When defining multiple @Cache, @Method or @Template annotations on
|
||||
a controller class or method, the latter now overrules the former
|
||||
|
||||
2.1
|
||||
---
|
||||
|
||||
* added the possibility to configure the id name for the Doctrine converter via the id option
|
||||
* [BC break] The ParamConverterInterface::apply() method now must return a
|
||||
Boolean value indicating if a conversion was done.
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
<?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\Configuration;
|
||||
|
||||
/**
|
||||
* 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 int
|
||||
*/
|
||||
protected $maxage;
|
||||
|
||||
/**
|
||||
* The number of seconds that the response is considered fresh by a public
|
||||
* cache like a reverse proxy cache.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $smaxage;
|
||||
|
||||
/**
|
||||
* Whether the response is public or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $public;
|
||||
|
||||
/**
|
||||
* Additional "Vary:"-headers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $vary;
|
||||
|
||||
/**
|
||||
* An expression to compute the Last-Modified HTTP header.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $lastModified;
|
||||
|
||||
/**
|
||||
* An expression to compute the ETag HTTP header.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $etag;
|
||||
|
||||
/**
|
||||
* 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 int $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 int
|
||||
*/
|
||||
public function getMaxAge()
|
||||
{
|
||||
return $this->maxage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of seconds for the s-maxage cache-control header field.
|
||||
*
|
||||
* @param int $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 int
|
||||
*/
|
||||
public function getSMaxAge()
|
||||
{
|
||||
return $this->smaxage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not a response is public.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPublic()
|
||||
{
|
||||
return $this->public === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not a response is private.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPrivate()
|
||||
{
|
||||
return $this->public === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a response public.
|
||||
*
|
||||
* @param bool $public A boolean value
|
||||
*/
|
||||
public function setPublic($public)
|
||||
{
|
||||
$this->public = (bool) $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "Last-Modified"-header expression.
|
||||
*
|
||||
* @param string $expression
|
||||
*/
|
||||
public function setLastModified($expression)
|
||||
{
|
||||
$this->lastModified = $expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "Last-Modified"-header expression.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastModified()
|
||||
{
|
||||
return $this->lastModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "ETag"-header expression.
|
||||
*
|
||||
* @param string $expression
|
||||
*/
|
||||
public function setETag($expression)
|
||||
{
|
||||
$this->etag = $expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "ETag"-header expression.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getETag()
|
||||
{
|
||||
return $this->etag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the annotation alias name.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @see ConfigurationInterface
|
||||
*/
|
||||
public function getAliasName()
|
||||
{
|
||||
return 'cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* Only one cache directive is allowed.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @see ConfigurationInterface
|
||||
*/
|
||||
public function allowArray()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?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\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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?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\Configuration;
|
||||
|
||||
/**
|
||||
* ConfigurationInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ConfigurationInterface
|
||||
{
|
||||
/**
|
||||
* Returns the alias name for an annotated configuration.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAliasName();
|
||||
|
||||
/**
|
||||
* Returns whether multiple annotations of this type are allowed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function allowArray();
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<?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\Configuration;
|
||||
|
||||
/**
|
||||
* 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';
|
||||
}
|
||||
|
||||
/**
|
||||
* Only one method directive is allowed.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @see ConfigurationInterface
|
||||
*/
|
||||
public function allowArray()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
<?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\Configuration;
|
||||
|
||||
/**
|
||||
* The ParamConverter class handles the ParamConverter annotation parts.
|
||||
*
|
||||
* @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 bool
|
||||
*/
|
||||
protected $optional = false;
|
||||
|
||||
/**
|
||||
* Use explicitly named converter instead of iterating by priorities.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $converter;
|
||||
|
||||
/**
|
||||
* 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 bool $optional Whether the parameter is optional
|
||||
*/
|
||||
public function setIsOptional($optional)
|
||||
{
|
||||
$this->optional = (bool) $optional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the parameter is optional.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOptional()
|
||||
{
|
||||
return $this->optional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get explicit converter name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConverter()
|
||||
{
|
||||
return $this->converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set explicit converter name.
|
||||
*
|
||||
* @param string $converter
|
||||
*/
|
||||
public function setConverter($converter)
|
||||
{
|
||||
$this->converter = $converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the annotation alias name.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @see ConfigurationInterface
|
||||
*/
|
||||
public function getAliasName()
|
||||
{
|
||||
return 'converters';
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple ParamConverters are allowed.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @see ConfigurationInterface
|
||||
*/
|
||||
public function allowArray()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?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\Configuration;
|
||||
|
||||
use Symfony\Component\Routing\Annotation\Route as BaseRoute;
|
||||
|
||||
/**
|
||||
* @author Kris Wallsmith <kris@symfony.com>
|
||||
* @Annotation
|
||||
*/
|
||||
class Route extends BaseRoute
|
||||
{
|
||||
protected $service;
|
||||
|
||||
public function setService($service)
|
||||
{
|
||||
// avoid a BC notice in case of @Route(service="") with sf ^2.7
|
||||
if (null === $this->getPath()) {
|
||||
$this->setPath('');
|
||||
}
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
public function getService()
|
||||
{
|
||||
return $this->service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple route annotations are allowed.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @see ConfigurationInterface
|
||||
*/
|
||||
public function allowArray()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<?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\Configuration;
|
||||
|
||||
/**
|
||||
* The Security class handles the Security annotation.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @Annotation
|
||||
*/
|
||||
class Security extends ConfigurationAnnotation
|
||||
{
|
||||
protected $expression;
|
||||
|
||||
public function getExpression()
|
||||
{
|
||||
return $this->expression;
|
||||
}
|
||||
|
||||
public function setExpression($expression)
|
||||
{
|
||||
$this->expression = $expression;
|
||||
}
|
||||
|
||||
public function setValue($expression)
|
||||
{
|
||||
$this->setExpression($expression);
|
||||
}
|
||||
|
||||
public function getAliasName()
|
||||
{
|
||||
return 'security';
|
||||
}
|
||||
|
||||
public function allowArray()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
<?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\Configuration;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
|
||||
|
||||
/**
|
||||
* The Template class handles the Template annotation parts.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @Annotation
|
||||
*/
|
||||
class Template extends ConfigurationAnnotation
|
||||
{
|
||||
/**
|
||||
* The template reference.
|
||||
*
|
||||
* @var TemplateReference|string
|
||||
*/
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* The template engine used when a specific template isn't specified.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $engine = 'twig';
|
||||
|
||||
/**
|
||||
* The associative array of template variables.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $vars = array();
|
||||
|
||||
/**
|
||||
* Should the template be streamed?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $streamable = false;
|
||||
|
||||
/**
|
||||
* The controller (+action) this annotation is set to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $owner;
|
||||
|
||||
/**
|
||||
* Returns the array of templates variables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getVars()
|
||||
{
|
||||
return $this->vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $streamable
|
||||
*/
|
||||
public function setIsStreamable($streamable)
|
||||
{
|
||||
$this->streamable = $streamable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isStreamable()
|
||||
{
|
||||
return (bool) $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';
|
||||
}
|
||||
|
||||
/**
|
||||
* Only one template directive is allowed.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @see ConfigurationInterface
|
||||
*/
|
||||
public function allowArray()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $owner
|
||||
*/
|
||||
public function setOwner(array $owner)
|
||||
{
|
||||
$this->owner = $owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* The controller (+action) this annotation is attached to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOwner()
|
||||
{
|
||||
return $this->owner;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?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\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class AddExpressionLanguageProvidersPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if ($container->hasDefinition('sensio_framework_extra.security.expression_language.default')) {
|
||||
$definition = $container->findDefinition('sensio_framework_extra.security.expression_language.default');
|
||||
foreach ($container->findTaggedServiceIds('security.expression_language_provider') as $id => $attributes) {
|
||||
$definition->addMethodCall('registerProvider', array(new Reference($id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?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 => $converters) {
|
||||
foreach ($converters as $converter) {
|
||||
$name = isset($converter['converter']) ? $converter['converter'] : null;
|
||||
$priority = isset($converter['priority']) ? $converter['priority'] : 0;
|
||||
|
||||
if ($priority === 'false' || $priority === false) {
|
||||
$priority = null;
|
||||
}
|
||||
|
||||
$definition->addMethodCall('add', array(new Reference($id), $priority, $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?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\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class LegacyPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (false === $container->hasDefinition('sensio_framework_extra.security.listener')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('sensio_framework_extra.security.listener');
|
||||
|
||||
if ($container->hasDefinition('security.token_storage')) {
|
||||
$definition->replaceArgument(0, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
use Symfony\Component\Config\Definition\NodeInterface;
|
||||
|
||||
/**
|
||||
* FrameworkExtraBundle configuration structure.
|
||||
*
|
||||
* @author Henrik Bjornskov <hb@peytz.dk>
|
||||
*/
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
/**
|
||||
* Generates the configuration tree.
|
||||
*
|
||||
* @return NodeInterface
|
||||
*/
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$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()
|
||||
->booleanNode('auto_convert')->defaultTrue()->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('view')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('annotations')->defaultTrue()->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('cache')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('annotations')->defaultTrue()->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('security')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('annotations')->defaultTrue()->end()
|
||||
->scalarNode('expression_language')->defaultValue('sensio_framework_extra.security.expression_language.default')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('psr_message')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('enabled')->defaultValue(interface_exists('Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface') && class_exists('Zend\Diactoros\ServerRequestFactory'))->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('templating')
|
||||
->fixXmlConfig('controller_pattern')
|
||||
->children()
|
||||
->arrayNode('controller_patterns')
|
||||
->prototype('scalar')
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Alias;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
|
||||
/**
|
||||
* SensioFrameworkExtraExtension.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SensioFrameworkExtraExtension extends Extension
|
||||
{
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$configuration = $this->getConfiguration($configs, $container);
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
$loader->load('services.xml');
|
||||
|
||||
if (!empty($config['templating']['controller_patterns'])) {
|
||||
$container
|
||||
->getDefinition('sensio_framework_extra.view.guesser')
|
||||
->addArgument($config['templating']['controller_patterns']);
|
||||
}
|
||||
|
||||
$annotationsToLoad = array();
|
||||
|
||||
if ($config['router']['annotations']) {
|
||||
$annotationsToLoad[] = 'routing.xml';
|
||||
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
$this->addClassesToCompile(array(
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ControllerListener',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['request']['converters']) {
|
||||
$annotationsToLoad[] = 'converters.xml';
|
||||
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
$this->addClassesToCompile(array(
|
||||
// cannot be added because it has some annotations
|
||||
//'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\ParamConverter',
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ParamConverterListener',
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\DateTimeParamConverter',
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\DoctrineParamConverter',
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterInterface',
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterManager',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['view']['annotations']) {
|
||||
$annotationsToLoad[] = 'view.xml';
|
||||
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
$this->addClassesToCompile(array(
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\TemplateListener',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['cache']['annotations']) {
|
||||
$annotationsToLoad[] = 'cache.xml';
|
||||
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
$this->addClassesToCompile(array(
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\HttpCacheListener',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['security']['annotations']) {
|
||||
$annotationsToLoad[] = 'security.xml';
|
||||
|
||||
if (class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage') && class_exists('Symfony\Component\Security\Core\Authorization\ExpressionLanguage')) {
|
||||
$container->setAlias('sensio_framework_extra.security.expression_language', new Alias($config['security']['expression_language'], false));
|
||||
} else {
|
||||
$container->removeDefinition('sensio_framework_extra.security.expression_language.default');
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
$this->addClassesToCompile(array(
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\SecurityListener',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($annotationsToLoad) {
|
||||
// must be first
|
||||
$loader->load('annotations.xml');
|
||||
|
||||
foreach ($annotationsToLoad as $configFile) {
|
||||
$loader->load($configFile);
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
$this->addClassesToCompile(array(
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\ConfigurationAnnotation',
|
||||
));
|
||||
}
|
||||
|
||||
if ($config['request']['converters']) {
|
||||
$container->getDefinition('sensio_framework_extra.converter.listener')->replaceArgument(1, $config['request']['auto_convert']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['psr_message']['enabled']) {
|
||||
$loader->load('psr7.xml');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
|
||||
|
||||
/**
|
||||
* CacheListener handles HTTP cache headers.
|
||||
*
|
||||
* It can be configured via the Cache, LastModified, and Etag annotations.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated Deprecated since 3.0, to be removed in 4.0. Use the HttpCacheListener instead.
|
||||
*/
|
||||
class CacheListener extends HttpCacheListener
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
|
||||
/**
|
||||
* The ControllerListener class parses annotation blocks located in
|
||||
* controller classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ControllerListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var 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)
|
||||
{
|
||||
$controller = $event->getController();
|
||||
|
||||
if (!is_array($controller) && method_exists($controller, '__invoke')) {
|
||||
$controller = array($controller, '__invoke');
|
||||
}
|
||||
|
||||
if (!is_array($controller)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$className = class_exists('Doctrine\Common\Util\ClassUtils') ? ClassUtils::getClass($controller[0]) : get_class($controller[0]);
|
||||
$object = new \ReflectionClass($className);
|
||||
$method = $object->getMethod($controller[1]);
|
||||
|
||||
$classConfigurations = $this->getConfigurations($this->reader->getClassAnnotations($object));
|
||||
$methodConfigurations = $this->getConfigurations($this->reader->getMethodAnnotations($method));
|
||||
|
||||
$configurations = array();
|
||||
foreach (array_merge(array_keys($classConfigurations), array_keys($methodConfigurations)) as $key) {
|
||||
if (!array_key_exists($key, $classConfigurations)) {
|
||||
$configurations[$key] = $methodConfigurations[$key];
|
||||
} elseif (!array_key_exists($key, $methodConfigurations)) {
|
||||
$configurations[$key] = $classConfigurations[$key];
|
||||
} else {
|
||||
if (is_array($classConfigurations[$key])) {
|
||||
if (!is_array($methodConfigurations[$key])) {
|
||||
throw new \UnexpectedValueException('Configurations should both be an array or both not be an array');
|
||||
}
|
||||
$configurations[$key] = array_merge($classConfigurations[$key], $methodConfigurations[$key]);
|
||||
} else {
|
||||
// method configuration overrides class configuration
|
||||
$configurations[$key] = $methodConfigurations[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
foreach ($configurations as $key => $attributes) {
|
||||
$request->attributes->set($key, $attributes);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getConfigurations(array $annotations)
|
||||
{
|
||||
$configurations = array();
|
||||
foreach ($annotations as $configuration) {
|
||||
if ($configuration instanceof ConfigurationInterface) {
|
||||
if ($configuration->allowArray()) {
|
||||
$configurations['_'.$configuration->getAliasName()][] = $configuration;
|
||||
} elseif (!isset($configurations['_'.$configuration->getAliasName()])) {
|
||||
$configurations['_'.$configuration->getAliasName()] = $configuration;
|
||||
} else {
|
||||
throw new \LogicException(sprintf('Multiple "%s" annotations are not allowed.', $configuration->getAliasName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $configurations;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::CONTROLLER => 'onKernelController',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
|
||||
/**
|
||||
* HttpCacheListener handles HTTP cache headers.
|
||||
*
|
||||
* It can be configured via the Cache annotation.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class HttpCacheListener implements EventSubscriberInterface
|
||||
{
|
||||
private $lastModifiedDates;
|
||||
private $etags;
|
||||
private $expressionLanguage;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->lastModifiedDates = new \SplObjectStorage();
|
||||
$this->etags = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles HTTP validation headers.
|
||||
*/
|
||||
public function onKernelController(FilterControllerEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
if (!$configuration = $request->attributes->get('_cache')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$response = new Response();
|
||||
|
||||
$lastModifiedDate = '';
|
||||
if ($configuration->getLastModified()) {
|
||||
$lastModifiedDate = $this->getExpressionLanguage()->evaluate($configuration->getLastModified(), $request->attributes->all());
|
||||
$response->setLastModified($lastModifiedDate);
|
||||
}
|
||||
|
||||
$etag = '';
|
||||
if ($configuration->getETag()) {
|
||||
$etag = hash('sha256', $this->getExpressionLanguage()->evaluate($configuration->getETag(), $request->attributes->all()));
|
||||
$response->setETag($etag);
|
||||
}
|
||||
|
||||
if ($response->isNotModified($request)) {
|
||||
$event->setController(function () use ($response) {
|
||||
return $response;
|
||||
});
|
||||
$event->stopPropagation();
|
||||
} else {
|
||||
if ($etag) {
|
||||
$this->etags[$request] = $etag;
|
||||
}
|
||||
if ($lastModifiedDate) {
|
||||
$this->lastModifiedDates[$request] = $lastModifiedDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the response to apply HTTP cache headers when needed.
|
||||
*/
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (!$configuration = $request->attributes->get('_cache')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$response = $event->getResponse();
|
||||
|
||||
// http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-12#section-3.1
|
||||
if (!in_array($response->getStatusCode(), array(200, 203, 300, 301, 302, 304, 404, 410))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $age = $configuration->getSMaxAge()) {
|
||||
if (!is_numeric($age)) {
|
||||
$now = microtime(true);
|
||||
|
||||
$age = ceil(strtotime($configuration->getSMaxAge(), $now) - $now);
|
||||
}
|
||||
|
||||
$response->setSharedMaxAge($age);
|
||||
}
|
||||
|
||||
if (null !== $age = $configuration->getMaxAge()) {
|
||||
if (!is_numeric($age)) {
|
||||
$now = microtime(true);
|
||||
|
||||
$age = ceil(strtotime($configuration->getMaxAge(), $now) - $now);
|
||||
}
|
||||
|
||||
$response->setMaxAge($age);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
if ($configuration->isPrivate()) {
|
||||
$response->setPrivate();
|
||||
}
|
||||
|
||||
if (isset($this->lastModifiedDates[$request])) {
|
||||
$response->setLastModified($this->lastModifiedDates[$request]);
|
||||
|
||||
unset($this->lastModifiedDates[$request]);
|
||||
}
|
||||
|
||||
if (isset($this->etags[$request])) {
|
||||
$response->setETag($this->etags[$request]);
|
||||
|
||||
unset($this->etags[$request]);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::CONTROLLER => 'onKernelController',
|
||||
KernelEvents::RESPONSE => 'onKernelResponse',
|
||||
);
|
||||
}
|
||||
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
||||
}
|
||||
$this->expressionLanguage = new ExpressionLanguage();
|
||||
}
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* The ParamConverterListener handles the ParamConverter annotation.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ParamConverterListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var ParamConverterManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
protected $autoConvert;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isParameterTypeSupported;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ParamConverterManager $manager A ParamConverterManager instance
|
||||
* @param bool $autoConvert Auto convert non-configured objects
|
||||
*/
|
||||
public function __construct(ParamConverterManager $manager, $autoConvert = true)
|
||||
{
|
||||
$this->manager = $manager;
|
||||
$this->autoConvert = $autoConvert;
|
||||
$this->isParameterTypeSupported = method_exists('ReflectionParameter', 'getType');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
} elseif (is_object($controller) && is_callable($controller, '__invoke')) {
|
||||
$r = new \ReflectionMethod($controller, '__invoke');
|
||||
} else {
|
||||
$r = new \ReflectionFunction($controller);
|
||||
}
|
||||
|
||||
// automatically apply conversion for non-configured objects
|
||||
if ($this->autoConvert) {
|
||||
$configurations = $this->autoConfigure($r, $request, $configurations);
|
||||
}
|
||||
|
||||
$this->manager->apply($request, $configurations);
|
||||
}
|
||||
|
||||
private function autoConfigure(\ReflectionFunctionAbstract $r, Request $request, $configurations)
|
||||
{
|
||||
foreach ($r->getParameters() as $param) {
|
||||
if ($param->getClass() && $param->getClass()->isInstance($request)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $param->getName();
|
||||
$class = $param->getClass();
|
||||
$hasType = $this->isParameterTypeSupported && $param->hasType();
|
||||
|
||||
if ($class || $hasType) {
|
||||
if (!isset($configurations[$name])) {
|
||||
$configuration = new ParamConverter(array());
|
||||
$configuration->setName($name);
|
||||
|
||||
$configurations[$name] = $configuration;
|
||||
}
|
||||
|
||||
if ($class && null === $configurations[$name]->getClass()) {
|
||||
$configurations[$name]->setClass($class->getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($configurations[$name])) {
|
||||
$configurations[$name]->setIsOptional($param->isOptional() || $param->isDefaultValueAvailable() || $hasType && $param->getType()->allowsNull());
|
||||
}
|
||||
}
|
||||
|
||||
return $configurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscribed events.
|
||||
*
|
||||
* @return array Subscribed events
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::CONTROLLER => 'onKernelController',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
|
||||
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Converts PSR-7 Response to HttpFoundation Response using the bridge.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PsrResponseListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var HttpFoundationFactoryInterface
|
||||
*/
|
||||
private $httpFoundationFactory;
|
||||
|
||||
public function __construct(HttpFoundationFactoryInterface $httpFoundationFactory)
|
||||
{
|
||||
$this->httpFoundationFactory = $httpFoundationFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the conversion if applicable and update the response of the event.
|
||||
*
|
||||
* @param GetResponseForControllerResultEvent $event
|
||||
*/
|
||||
public function onKernelView(GetResponseForControllerResultEvent $event)
|
||||
{
|
||||
$controllerResult = $event->getControllerResult();
|
||||
|
||||
if (!$controllerResult instanceof ResponseInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event->setResponse($this->httpFoundationFactory->createResponse($controllerResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::VIEW => 'onKernelView',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage;
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Security\Core\SecurityContextInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
|
||||
|
||||
/**
|
||||
* SecurityListener handles security restrictions on controllers.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SecurityListener implements EventSubscriberInterface
|
||||
{
|
||||
private $tokenStorage;
|
||||
private $authChecker;
|
||||
private $language;
|
||||
private $trustResolver;
|
||||
private $roleHierarchy;
|
||||
|
||||
public function __construct(SecurityContextInterface $securityContext = null, ExpressionLanguage $language = null, AuthenticationTrustResolverInterface $trustResolver = null, RoleHierarchyInterface $roleHierarchy = null, TokenStorageInterface $tokenStorage = null, AuthorizationCheckerInterface $authChecker = null)
|
||||
{
|
||||
$this->tokenStorage = $tokenStorage ?: $securityContext;
|
||||
$this->authChecker = $authChecker ?: $securityContext;
|
||||
$this->language = $language;
|
||||
$this->trustResolver = $trustResolver;
|
||||
$this->roleHierarchy = $roleHierarchy;
|
||||
}
|
||||
|
||||
public function onKernelController(FilterControllerEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
if (!$configuration = $request->attributes->get('_security')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $this->tokenStorage || null === $this->trustResolver) {
|
||||
throw new \LogicException('To use the @Security tag, you need to install the Symfony Security bundle.');
|
||||
}
|
||||
|
||||
if (null === $this->tokenStorage->getToken()) {
|
||||
throw new \LogicException('To use the @Security tag, your controller needs to be behind a firewall.');
|
||||
}
|
||||
|
||||
if (null === $this->language) {
|
||||
throw new \LogicException('To use the @Security tag, you need to use the Security component 2.4 or newer and install the ExpressionLanguage component.');
|
||||
}
|
||||
|
||||
if (!$this->language->evaluate($configuration->getExpression(), $this->getVariables($request))) {
|
||||
throw new AccessDeniedException(sprintf('Expression "%s" denied access.', $configuration->getExpression()));
|
||||
}
|
||||
}
|
||||
|
||||
// code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter
|
||||
private function getVariables(Request $request)
|
||||
{
|
||||
$token = $this->tokenStorage->getToken();
|
||||
|
||||
if (null !== $this->roleHierarchy) {
|
||||
$roles = $this->roleHierarchy->getReachableRoles($token->getRoles());
|
||||
} else {
|
||||
$roles = $token->getRoles();
|
||||
}
|
||||
|
||||
$variables = array(
|
||||
'token' => $token,
|
||||
'user' => $token->getUser(),
|
||||
'object' => $request,
|
||||
'subject' => $request,
|
||||
'request' => $request,
|
||||
'roles' => array_map(function ($role) { return $role->getRole(); }, $roles),
|
||||
'trust_resolver' => $this->trustResolver,
|
||||
// needed for the is_granted expression function
|
||||
'auth_checker' => $this->authChecker,
|
||||
);
|
||||
|
||||
// controller variables should also be accessible
|
||||
return array_merge($request->attributes->all(), $variables);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(KernelEvents::CONTROLLER => 'onKernelController');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
|
||||
|
||||
/**
|
||||
* Handles the Template annotation for actions.
|
||||
*
|
||||
* Depends on pre-processing of the ControllerListener.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TemplateListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var 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)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
$template = $request->attributes->get('_template');
|
||||
|
||||
if (!$template instanceof Template) {
|
||||
return;
|
||||
}
|
||||
|
||||
$template->setOwner($controller = $event->getController());
|
||||
|
||||
// when no template has been given, try to resolve it based on the controller
|
||||
if (null === $template->getTemplate()) {
|
||||
$guesser = $this->container->get('sensio_framework_extra.view.guesser');
|
||||
$template->setTemplate($guesser->guessTemplateName($controller, $request, $template->getEngine()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the template and initializes a new response object with the
|
||||
* rendered template content.
|
||||
*
|
||||
* @param GetResponseForControllerResultEvent $event
|
||||
*/
|
||||
public function onKernelView(GetResponseForControllerResultEvent $event)
|
||||
{
|
||||
/* @var Template $template */
|
||||
$request = $event->getRequest();
|
||||
$template = $request->attributes->get('_template');
|
||||
|
||||
if (!$template instanceof Template) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parameters = $event->getControllerResult();
|
||||
$owner = $template->getOwner();
|
||||
list($controller, $action) = $owner;
|
||||
|
||||
// when the annotation declares no default vars and the action returns
|
||||
// null, all action method arguments are used as default vars
|
||||
if (null === $parameters) {
|
||||
$parameters = $this->resolveDefaultParameters($request, $template, $controller, $action);
|
||||
}
|
||||
|
||||
// attempt to render the actual response
|
||||
$templating = $this->container->get('templating');
|
||||
|
||||
if ($template->isStreamable()) {
|
||||
$callback = function () use ($templating, $template, $parameters) {
|
||||
return $templating->stream($template->getTemplate(), $parameters);
|
||||
};
|
||||
|
||||
$event->setResponse(new StreamedResponse($callback));
|
||||
} else {
|
||||
$event->setResponse($templating->renderResponse($template->getTemplate(), $parameters));
|
||||
}
|
||||
|
||||
// make sure the owner (controller+dependencies) is not cached or stored elsewhere
|
||||
$template->setOwner(array());
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::CONTROLLER => array('onKernelController', -128),
|
||||
KernelEvents::VIEW => 'onKernelView',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Template $template
|
||||
* @param object $controller
|
||||
* @param string $action
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function resolveDefaultParameters(Request $request, Template $template, $controller, $action)
|
||||
{
|
||||
$parameters = array();
|
||||
$arguments = $template->getVars();
|
||||
|
||||
if (0 === count($arguments)) {
|
||||
$r = new \ReflectionObject($controller);
|
||||
|
||||
$arguments = array();
|
||||
foreach ($r->getMethod($action)->getParameters() as $param) {
|
||||
$arguments[] = $param;
|
||||
}
|
||||
}
|
||||
|
||||
// fetch the arguments of @Template.vars or everything if desired
|
||||
// and assign them to the designated template
|
||||
foreach ($arguments as $argument) {
|
||||
if ($argument instanceof \ReflectionParameter) {
|
||||
$parameters[$name = $argument->getName()] = !$request->attributes->has($name) && $argument->isDefaultValueAvailable() ? $argument->getDefaultValue() : $request->attributes->get($name);
|
||||
} else {
|
||||
$parameters[$argument] = $request->attributes->get($argument);
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
}
|
||||
19
trunk/_vendor/sensio/framework-extra-bundle/LICENSE
Normal file
19
trunk/_vendor/sensio/framework-extra-bundle/LICENSE
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2010-2017 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.
|
||||
12
trunk/_vendor/sensio/framework-extra-bundle/README.md
Normal file
12
trunk/_vendor/sensio/framework-extra-bundle/README.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
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).
|
||||
|
||||
As of v3.0.0 of the bundle, the release cycle is de-synchronized from the
|
||||
framework's. It means you can just require "sensio/framework-extra-bundle":
|
||||
"~3.0" in your composer.json and Composer will automatically pick the latest
|
||||
version of the bundle that works with your current version of Symfony. The
|
||||
minimum version of Symfony for this workflow is 2.3.
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Convert DateTime instances from request attribute variable.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class DateTimeParamConverter implements ParamConverterInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws NotFoundHttpException When invalid date given
|
||||
*/
|
||||
public function apply(Request $request, ParamConverter $configuration)
|
||||
{
|
||||
$param = $configuration->getName();
|
||||
|
||||
if (!$request->attributes->has($param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = $configuration->getOptions();
|
||||
$value = $request->attributes->get($param);
|
||||
|
||||
if (!$value && $configuration->isOptional()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($options['format'])) {
|
||||
$date = DateTime::createFromFormat($options['format'], $value);
|
||||
|
||||
if (!$date) {
|
||||
throw new NotFoundHttpException(sprintf('Invalid date given for parameter "%s".', $param));
|
||||
}
|
||||
} else {
|
||||
if (false === strtotime($value)) {
|
||||
throw new NotFoundHttpException(sprintf('Invalid date given for parameter "%s".', $param));
|
||||
}
|
||||
|
||||
$date = new DateTime($value);
|
||||
}
|
||||
|
||||
$request->attributes->set($param, $date);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(ParamConverter $configuration)
|
||||
{
|
||||
if (null === $configuration->getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'DateTime' === $configuration->getClass();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
<?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\Request\ParamConverter;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use Doctrine\ORM\NoResultException;
|
||||
|
||||
/**
|
||||
* DoctrineParamConverter.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class DoctrineParamConverter implements ParamConverterInterface
|
||||
{
|
||||
/**
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
public function __construct(ManagerRegistry $registry = null)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \LogicException When unable to guess how to get a Doctrine instance from the request information
|
||||
* @throws NotFoundHttpException When object not found
|
||||
*/
|
||||
public function apply(Request $request, ParamConverter $configuration)
|
||||
{
|
||||
$name = $configuration->getName();
|
||||
$class = $configuration->getClass();
|
||||
$options = $this->getOptions($configuration);
|
||||
|
||||
if (null === $request->attributes->get($name, false)) {
|
||||
$configuration->setIsOptional(true);
|
||||
}
|
||||
|
||||
// find by identifier?
|
||||
if (false === $object = $this->find($class, $request, $options, $name)) {
|
||||
// find by criteria
|
||||
if (false === $object = $this->findOneBy($class, $request, $options)) {
|
||||
if ($configuration->isOptional()) {
|
||||
$object = null;
|
||||
} else {
|
||||
throw new \LogicException(sprintf('Unable to guess how to get a Doctrine instance from the request information for parameter "%s".', $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $object && false === $configuration->isOptional()) {
|
||||
throw new NotFoundHttpException(sprintf('%s object not found.', $class));
|
||||
}
|
||||
|
||||
$request->attributes->set($name, $object);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function find($class, Request $request, $options, $name)
|
||||
{
|
||||
if ($options['mapping'] || $options['exclude']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = $this->getIdentifier($request, $options, $name);
|
||||
|
||||
if (false === $id || null === $id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($options['repository_method'])) {
|
||||
$method = $options['repository_method'];
|
||||
} else {
|
||||
$method = 'find';
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->getManager($options['entity_manager'], $class)->getRepository($class)->$method($id);
|
||||
} catch (NoResultException $e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getIdentifier(Request $request, $options, $name)
|
||||
{
|
||||
if (isset($options['id'])) {
|
||||
if (!is_array($options['id'])) {
|
||||
$name = $options['id'];
|
||||
} elseif (is_array($options['id'])) {
|
||||
$id = array();
|
||||
foreach ($options['id'] as $field) {
|
||||
$id[$field] = $request->attributes->get($field);
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->attributes->has($name)) {
|
||||
return $request->attributes->get($name);
|
||||
}
|
||||
|
||||
if ($request->attributes->has('id') && !isset($options['id'])) {
|
||||
return $request->attributes->get('id');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function findOneBy($class, Request $request, $options)
|
||||
{
|
||||
if (!$options['mapping']) {
|
||||
$keys = $request->attributes->keys();
|
||||
$options['mapping'] = $keys ? array_combine($keys, $keys) : array();
|
||||
}
|
||||
|
||||
foreach ($options['exclude'] as $exclude) {
|
||||
unset($options['mapping'][$exclude]);
|
||||
}
|
||||
|
||||
if (!$options['mapping']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if a specific id has been defined in the options and there is no corresponding attribute
|
||||
// return false in order to avoid a fallback to the id which might be of another object
|
||||
if (isset($options['id']) && null === $request->attributes->get($options['id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$criteria = array();
|
||||
$em = $this->getManager($options['entity_manager'], $class);
|
||||
$metadata = $em->getClassMetadata($class);
|
||||
|
||||
$mapMethodSignature = isset($options['repository_method'])
|
||||
&& isset($options['map_method_signature'])
|
||||
&& $options['map_method_signature'] === true;
|
||||
|
||||
foreach ($options['mapping'] as $attribute => $field) {
|
||||
if ($metadata->hasField($field)
|
||||
|| ($metadata->hasAssociation($field) && $metadata->isSingleValuedAssociation($field))
|
||||
|| $mapMethodSignature) {
|
||||
$criteria[$field] = $request->attributes->get($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['strip_null']) {
|
||||
$criteria = array_filter($criteria, function ($value) { return !is_null($value); });
|
||||
}
|
||||
|
||||
if (!$criteria) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($options['repository_method'])) {
|
||||
$repositoryMethod = $options['repository_method'];
|
||||
} else {
|
||||
$repositoryMethod = 'findOneBy';
|
||||
}
|
||||
|
||||
try {
|
||||
if ($mapMethodSignature) {
|
||||
return $this->findDataByMapMethodSignature($em, $class, $repositoryMethod, $criteria);
|
||||
}
|
||||
|
||||
return $em->getRepository($class)->$repositoryMethod($criteria);
|
||||
} catch (NoResultException $e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private function findDataByMapMethodSignature($em, $class, $repositoryMethod, $criteria)
|
||||
{
|
||||
$arguments = array();
|
||||
$repository = $em->getRepository($class);
|
||||
$ref = new \ReflectionMethod($repository, $repositoryMethod);
|
||||
foreach ($ref->getParameters() as $parameter) {
|
||||
if (array_key_exists($parameter->name, $criteria)) {
|
||||
$arguments[] = $criteria[$parameter->name];
|
||||
} elseif ($parameter->isDefaultValueAvailable()) {
|
||||
$arguments[] = $parameter->getDefaultValue();
|
||||
} else {
|
||||
throw new \InvalidArgumentException(sprintf('Repository method "%s::%s" requires that you provide a value for the "$%s" argument.', get_class($repository), $repositoryMethod, $parameter->name));
|
||||
}
|
||||
}
|
||||
|
||||
return $ref->invokeArgs($repository, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(ParamConverter $configuration)
|
||||
{
|
||||
// if there is no manager, this means that only Doctrine DBAL is configured
|
||||
if (null === $this->registry || !count($this->registry->getManagers())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null === $configuration->getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = $this->getOptions($configuration);
|
||||
|
||||
// Doctrine Entity?
|
||||
$em = $this->getManager($options['entity_manager'], $configuration->getClass());
|
||||
if (null === $em) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !$em->getMetadataFactory()->isTransient($configuration->getClass());
|
||||
}
|
||||
|
||||
protected function getOptions(ParamConverter $configuration)
|
||||
{
|
||||
return array_replace(array(
|
||||
'entity_manager' => null,
|
||||
'exclude' => array(),
|
||||
'mapping' => array(),
|
||||
'strip_null' => false,
|
||||
), $configuration->getOptions());
|
||||
}
|
||||
|
||||
private function getManager($name, $class)
|
||||
{
|
||||
if (null === $name) {
|
||||
return $this->registry->getManagerForClass($class);
|
||||
}
|
||||
|
||||
return $this->registry->getManager($name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?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\Request\ParamConverter;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Converts request parameters to objects and stores them as request
|
||||
* attributes, so they can be injected as controller method arguments.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ParamConverterInterface
|
||||
{
|
||||
/**
|
||||
* Stores the object in the request.
|
||||
*
|
||||
* @param Request $request The request
|
||||
* @param ParamConverter $configuration Contains the name, class and options of the object
|
||||
*
|
||||
* @return bool True if the object has been successfully set, else false
|
||||
*/
|
||||
public function apply(Request $request, ParamConverter $configuration);
|
||||
|
||||
/**
|
||||
* Checks if the object is supported.
|
||||
*
|
||||
* @param ParamConverter $configuration Should be an instance of ParamConverter
|
||||
*
|
||||
* @return bool True if the object is supported, else false
|
||||
*/
|
||||
public function supports(ParamConverter $configuration);
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
<?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\Request\ParamConverter;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Managers converters.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Henrik Bjornskov <henrik@bjrnskov.dk>
|
||||
*/
|
||||
class ParamConverterManager
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $converters = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $namedConverters = 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) {
|
||||
$this->applyConverter($request, $configuration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply converter on request based on the given configuration.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param ConfigurationInterface $configuration
|
||||
*/
|
||||
protected function applyConverter(Request $request, ConfigurationInterface $configuration)
|
||||
{
|
||||
$value = $request->attributes->get($configuration->getName());
|
||||
$className = $configuration->getClass();
|
||||
|
||||
// If the value is already an instance of the class we are trying to convert it into
|
||||
// we should continue as no conversion is required
|
||||
if (is_object($value) && $value instanceof $className) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($converterName = $configuration->getConverter()) {
|
||||
if (!isset($this->namedConverters[$converterName])) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
"No converter named '%s' found for conversion of parameter '%s'.",
|
||||
$converterName, $configuration->getName()
|
||||
));
|
||||
}
|
||||
|
||||
$converter = $this->namedConverters[$converterName];
|
||||
|
||||
if (!$converter->supports($configuration)) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
"Converter '%s' does not support conversion of parameter '%s'.",
|
||||
$converterName, $configuration->getName()
|
||||
));
|
||||
}
|
||||
|
||||
$converter->apply($request, $configuration);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->all() as $converter) {
|
||||
if ($converter->supports($configuration)) {
|
||||
if ($converter->apply($request, $configuration)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parameter converter.
|
||||
*
|
||||
* Converters match either explicitly via $name or by iteration over all
|
||||
* converters with a $priority. If you pass a $priority = null then the
|
||||
* added converter will not be part of the iteration chain and can only
|
||||
* be invoked explicitly.
|
||||
*
|
||||
* @param ParamConverterInterface $converter A ParamConverterInterface instance
|
||||
* @param int $priority The priority (between -10 and 10).
|
||||
* @param string $name Name of the converter.
|
||||
*/
|
||||
public function add(ParamConverterInterface $converter, $priority = 0, $name = null)
|
||||
{
|
||||
if ($priority !== null) {
|
||||
if (!isset($this->converters[$priority])) {
|
||||
$this->converters[$priority] = array();
|
||||
}
|
||||
|
||||
$this->converters[$priority][] = $converter;
|
||||
}
|
||||
|
||||
if (null !== $name) {
|
||||
$this->namedConverters[$name] = $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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?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\Request\ParamConverter;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Converts HttpFoundation Request to PSR-7 ServerRequest using the bridge.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PsrServerRequestParamConverter implements ParamConverterInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $supportedTypes = array(
|
||||
'Psr\Http\Message\ServerRequestInterface' => true,
|
||||
'Psr\Http\Message\RequestInterface' => true,
|
||||
'Psr\Http\Message\MessageInterface' => true,
|
||||
);
|
||||
/**
|
||||
* @var HttpMessageFactoryInterface
|
||||
*/
|
||||
private $httpMessageFactory;
|
||||
|
||||
public function __construct(HttpMessageFactoryInterface $httpMessageFactory)
|
||||
{
|
||||
$this->httpMessageFactory = $httpMessageFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply(Request $request, ParamConverter $configuration)
|
||||
{
|
||||
$request->attributes->set($configuration->getName(), $this->httpMessageFactory->createRequest($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(ParamConverter $configuration)
|
||||
{
|
||||
if (null === $configuration->getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset(self::$supportedTypes[$configuration->getClass()]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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_subscriber" />
|
||||
<argument type="service" id="annotation_reader" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
@ -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\HttpCacheListener">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?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>
|
||||
<parameter key="sensio_framework_extra.converter.datetime.class">Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DateTimeParamConverter</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="sensio_framework_extra.converter.listener" class="%sensio_framework_extra.converter.listener.class%">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service" id="sensio_framework_extra.converter.manager" />
|
||||
<argument>true</argument>
|
||||
</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" converter="doctrine.orm" />
|
||||
<argument type="service" id="doctrine" on-invalid="ignore" />
|
||||
</service>
|
||||
|
||||
<service id="sensio_framework_extra.converter.datetime" class="%sensio_framework_extra.converter.datetime.class%">
|
||||
<tag name="request.param_converter" converter="datetime" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?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.psr7.http_message_factory" class="Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory" public="false" />
|
||||
<service id="sensio_framework_extra.psr7.http_foundation_factory" class="Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory" public="false" />
|
||||
|
||||
<service id="sensio_framework_extra.psr7.converter.server_request" class="Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\PsrServerRequestParamConverter">
|
||||
<argument type="service" id="sensio_framework_extra.psr7.http_message_factory" />
|
||||
|
||||
<tag name="request.param_converter" converter="psr.server_request" />
|
||||
</service>
|
||||
|
||||
<service id="sensio_framework_extra.psr7.listener.response" class="Sensio\Bundle\FrameworkExtraBundle\EventListener\PsrResponseListener">
|
||||
<argument type="service" id="sensio_framework_extra.psr7.http_foundation_factory" />
|
||||
|
||||
<tag name="kernel.event_subscriber" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
@ -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_class" class="%sensio_framework_extra.routing.loader.annot_class.class%" public="false">
|
||||
<tag name="routing.loader" />
|
||||
<argument type="service" id="annotation_reader" />
|
||||
</service>
|
||||
|
||||
<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>
|
||||
</services>
|
||||
</container>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?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.security.listener" class="Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener">
|
||||
<argument type="service" id="security.context" on-invalid="null" />
|
||||
<argument type="service" id="sensio_framework_extra.security.expression_language" on-invalid="null" />
|
||||
<argument type="service" id="security.authentication.trust_resolver" on-invalid="null" />
|
||||
<argument type="service" id="security.role_hierarchy" on-invalid="null" />
|
||||
<argument type="service" id="security.token_storage" on-invalid="null" />
|
||||
<argument type="service" id="security.authorization_checker" on-invalid="null" />
|
||||
<tag name="kernel.event_subscriber" />
|
||||
</service>
|
||||
|
||||
<service id="sensio_framework_extra.security.expression_language.default" class="Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage" public="false" />
|
||||
</services>
|
||||
</container>
|
||||
|
|
@ -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%" public="true">
|
||||
<argument type="service" id="kernel" />
|
||||
</service>
|
||||
</services>
|
||||
|
||||
</container>
|
||||
|
|
@ -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.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_subscriber" />
|
||||
<argument type="service" id="service_container" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
@Cache
|
||||
======
|
||||
|
||||
The ``@Cache`` annotation makes it easy to define HTTP caching headers for
|
||||
expiration and validation.
|
||||
|
||||
HTTP Expiration Strategies
|
||||
--------------------------
|
||||
|
||||
The ``@Cache`` annotation makes it easy to define HTTP caching::
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
|
||||
/**
|
||||
* @Cache(expires="tomorrow", public=true)
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
}
|
||||
|
||||
You can also use the annotation on a class to define caching for all actions
|
||||
of a controller::
|
||||
|
||||
/**
|
||||
* @Cache(expires="tomorrow", public=true)
|
||||
*/
|
||||
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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
The ``expires`` attribute takes any valid date understood by the PHP
|
||||
``strtotime()`` function.
|
||||
|
||||
HTTP Validation Strategies
|
||||
--------------------------
|
||||
|
||||
The ``lastModified`` and ``ETag`` attributes manage the HTTP validation cache
|
||||
headers. ``lastModified`` adds a ``Last-Modified`` header to Responses and
|
||||
``ETag`` adds an ``ETag`` header.
|
||||
|
||||
Both automatically trigger the logic to return a 304 response when the
|
||||
response is not modified (in this case, the controller is **not** called)::
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
|
||||
/**
|
||||
* @Cache(lastModified="post.getUpdatedAt()", ETag="'Post' ~ post.getId() ~ post.getUpdatedAt().getTimestamp()")
|
||||
*/
|
||||
public function indexAction(Post $post)
|
||||
{
|
||||
// your code
|
||||
// won't be called in case of a 304
|
||||
}
|
||||
|
||||
It's roughly doing the same as the following code::
|
||||
|
||||
public function myAction(Request $request, Post $post)
|
||||
{
|
||||
$response = new Response();
|
||||
$response->setLastModified($post->getUpdatedAt());
|
||||
if ($response->isNotModified($request)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// your code
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
The ETag HTTP header value is the result of the expression hashed with the
|
||||
``sha256`` algorithm.
|
||||
|
||||
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()``
|
||||
``@Cache(public=true)`` ``$response->setPublic()``
|
||||
``@Cache(lastModified="post.getUpdatedAt()")`` ``$response->setLastModified()``
|
||||
``@Cache(ETag="post.getId() ~ post.getUpdatedAt().getTimestamp()")`` ``$response->setETag()``
|
||||
======================================================================= ================================
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
@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 happen 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 other request attributes, 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)
|
||||
{
|
||||
}
|
||||
|
||||
.. tip::
|
||||
|
||||
You can disable the auto-conversion of type-hinted method arguments feature
|
||||
by setting the ``auto_convert`` flag to ``false``:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# app/config/config.yml
|
||||
sensio_framework_extra:
|
||||
request:
|
||||
converters: true
|
||||
auto_convert: false
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<sensio-framework-extra:config>
|
||||
<request converters="true" auto-convert="true" />
|
||||
</sensio-framework-extra:config>
|
||||
|
||||
To detect which converter is run on a parameter the following process is run:
|
||||
|
||||
* If an explicit converter choice was made with
|
||||
``@ParamConverter(converter="name")`` the converter with the given name is
|
||||
chosen.
|
||||
|
||||
* Otherwise all registered parameter converters are iterated by priority. The
|
||||
``supports()`` method is invoked to check if a param converter can convert
|
||||
the request into the required parameter. If it returns ``true`` the param
|
||||
converter is invoked.
|
||||
|
||||
Built-in Converters
|
||||
-------------------
|
||||
|
||||
The bundle has two built-in converters, the Doctrine one and a DateTime
|
||||
converter.
|
||||
|
||||
Doctrine Converter
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Converter Name: ``doctrine.orm``
|
||||
|
||||
The Doctrine Converter attempts to convert request attributes to Doctrine
|
||||
entities fetched from the database. Two different approaches are possible:
|
||||
|
||||
- Fetch object by primary key.
|
||||
- Fetch object by one or several fields which contain unique values in the
|
||||
database.
|
||||
|
||||
The following algorithm determines which operation will be performed.
|
||||
|
||||
- If an ``{id}`` parameter is present in the route, find object by primary key.
|
||||
- If an option ``'id'`` is configured and matches route parameters, find object by primary key.
|
||||
- If the previous rules do not apply, attempt to find one entity by matching
|
||||
route parameters to entity fields. You can control this process by
|
||||
configuring ``exclude`` parameters or a attribute to field name ``mapping``.
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
If the placeholder does not have the same name as the primary key, pass the ``id``
|
||||
option::
|
||||
|
||||
/**
|
||||
* @Route("/blog/{post_id}")
|
||||
* @ParamConverter("post", class="SensioBlogBundle:Post", options={"id" = "post_id"})
|
||||
*/
|
||||
public function showAction(Post $post)
|
||||
{
|
||||
}
|
||||
|
||||
.. tip::
|
||||
|
||||
The ``id`` option specifies which placeholder from the route gets passed to the repository
|
||||
method used. If no repository method is specified, ``find()`` is used by default.
|
||||
|
||||
This also allows you to have multiple converters in one action::
|
||||
|
||||
/**
|
||||
* @Route("/blog/{id}/comments/{comment_id}")
|
||||
* @ParamConverter("comment", class="SensioBlogBundle:Comment", options={"id" = "comment_id"})
|
||||
*/
|
||||
public function showAction(Post $post, Comment $comment)
|
||||
{
|
||||
}
|
||||
|
||||
In the example above, the ``$post`` parameter is handled automatically, but ``$comment`` is
|
||||
configured with the annotation since they can not both follow the default convention.
|
||||
|
||||
If you want to match an entity using multiple fields use the ``mapping`` hash
|
||||
option: the key is route placeholder name and the value is the Doctrine
|
||||
field name::
|
||||
|
||||
/**
|
||||
* @Route("/blog/{date}/{slug}/comments/{comment_slug}")
|
||||
* @ParamConverter("post", options={"mapping": {"date": "date", "slug": "slug"}})
|
||||
* @ParamConverter("comment", options={"mapping": {"comment_slug": "slug"}})
|
||||
*/
|
||||
public function showAction(Post $post, Comment $comment)
|
||||
{
|
||||
}
|
||||
|
||||
If you are matching an entity using several fields, but you want to exclude a
|
||||
route parameter from being part of the criteria::
|
||||
|
||||
/**
|
||||
* @Route("/blog/{date}/{slug}")
|
||||
* @ParamConverter("post", options={"exclude": {"date"}})
|
||||
*/
|
||||
public function showAction(Post $post, \DateTime $date)
|
||||
{
|
||||
}
|
||||
|
||||
If you want to specify the repository method to use to find the entity (for example,
|
||||
to add joins to the query), you can add the ``repository_method`` option::
|
||||
|
||||
/**
|
||||
* @Route("/blog/{id}")
|
||||
* @ParamConverter("post", class="SensioBlogBundle:Post", options={"repository_method" = "findWithJoins"})
|
||||
*/
|
||||
public function showAction(Post $post)
|
||||
{
|
||||
}
|
||||
|
||||
The specified repository method will be called with the criteria in an ``array``
|
||||
as parameter. This is a good fit with Doctrine's ``findBy`` and ``findOneBy``
|
||||
methods.
|
||||
|
||||
There are cases where you want to you use your own repository method and you
|
||||
want to map the criteria to the method signature. This is possible when you set
|
||||
the ``map_method_signature`` option to true. The default is false::
|
||||
|
||||
/**
|
||||
* @Route("/user/{first_name}/{last_name}")
|
||||
* @ParamConverter("user", class="AcmeBlogBundle:User", options={
|
||||
* "repository_method" = "findByFullName",
|
||||
* "mapping": {"first_name": "firstName", "last_name": "lastName"},
|
||||
* "map_method_signature" = true
|
||||
* })
|
||||
*/
|
||||
public function showAction(User $user)
|
||||
{
|
||||
}
|
||||
|
||||
class UserRepository
|
||||
{
|
||||
public function findByFullName($firstName, $lastName)
|
||||
{
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
.. tip::
|
||||
|
||||
When ``map_method_signature`` is ``true``, the ``firstName`` and
|
||||
``lastName`` parameters do not have to be Doctrine fields.
|
||||
|
||||
DateTime Converter
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Converter Name: ``datetime``
|
||||
|
||||
The datetime converter converts any route or request attribute into a datetime
|
||||
instance::
|
||||
|
||||
/**
|
||||
* @Route("/blog/archive/{start}/{end}")
|
||||
*/
|
||||
public function archiveAction(\DateTime $start, \DateTime $end)
|
||||
{
|
||||
}
|
||||
|
||||
By default any date format that can be parsed by the ``DateTime`` constructor
|
||||
is accepted. You can be stricter with input given through the options::
|
||||
|
||||
/**
|
||||
* @Route("/blog/archive/{start}/{end}")
|
||||
* @ParamConverter("start", options={"format": "Y-m-d"})
|
||||
* @ParamConverter("end", options={"format": "Y-m-d"})
|
||||
*/
|
||||
public function archiveAction(\DateTime $start, \DateTime $end)
|
||||
{
|
||||
}
|
||||
|
||||
Creating a Converter
|
||||
--------------------
|
||||
|
||||
All converters must implement the ``ParamConverterInterface``::
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
interface ParamConverterInterface
|
||||
{
|
||||
function apply(Request $request, ParamConverter $configuration);
|
||||
|
||||
function supports(ParamConverter $configuration);
|
||||
}
|
||||
|
||||
The ``supports()`` method must return ``true`` when it is able to convert the
|
||||
given configuration (a ``ParamConverter`` instance).
|
||||
|
||||
The ``ParamConverter`` instance has three pieces of 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()``.
|
||||
|
||||
To register your converter service, you must add a tag to your service:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# app/config/config.yml
|
||||
services:
|
||||
my_converter:
|
||||
class: MyBundle\Request\ParamConverter\MyConverter
|
||||
tags:
|
||||
- { name: request.param_converter, priority: -2, converter: my_converter }
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<service id="my_converter" class="MyBundle\Request\ParamConverter\MyConverter">
|
||||
<tag name="request.param_converter" priority="-2" converter="my_converter" />
|
||||
</service>
|
||||
|
||||
You can register a converter by priority, by name (attribute "converter"), or
|
||||
both. If you don't specify a priority or a name, the converter will be added to
|
||||
the converter stack with a priority of ``0``. To explicitly disable the
|
||||
registration by priority you have to set ``priority="false"`` in your tag
|
||||
definition.
|
||||
|
||||
.. tip::
|
||||
|
||||
If you would like to inject services or additional arguments into a custom
|
||||
param converter, the priority shouldn't be higher than ``1``. Otherwise, the
|
||||
service wouldn't be loaded.
|
||||
|
||||
.. tip::
|
||||
|
||||
Use the ``DoctrineParamConverter`` class as a template for your own converters.
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
@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:
|
||||
path: /
|
||||
defaults: { _controller: SensioBlogBundle:Post:index }
|
||||
|
||||
Like any route pattern, you can define placeholders, requirements, and default
|
||||
values::
|
||||
|
||||
/**
|
||||
* @Route("/{id}", requirements={"id" = "\d+"}, defaults={"id" = 1})
|
||||
*/
|
||||
public function showAction($id)
|
||||
{
|
||||
}
|
||||
|
||||
You can also define the default value for a placeholder with
|
||||
the PHP default value::
|
||||
|
||||
/**
|
||||
* @Route("/{id}", requirements={"id" = "\d+"})
|
||||
*/
|
||||
public function showAction($id = 1)
|
||||
{
|
||||
}
|
||||
|
||||
You can also match more than one URL by defining additional ``@Route``
|
||||
annotations::
|
||||
|
||||
/**
|
||||
* @Route("/", defaults={"id" = 1})
|
||||
* @Route("/{id}")
|
||||
*/
|
||||
public function showAction($id)
|
||||
{
|
||||
}
|
||||
|
||||
.. _frameworkextra-annotations-routing-activation:
|
||||
|
||||
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 (note that you cannot have more than one ``@Route`` annotation on a
|
||||
class)::
|
||||
|
||||
/**
|
||||
* @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
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. tip::
|
||||
|
||||
You can also omit the ``service`` option if your service ID is your controller
|
||||
fully-qualified class name (FQCN).
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
@Security
|
||||
=========
|
||||
|
||||
.. caution::
|
||||
|
||||
The ``@Security`` annotation only works as of Symfony 2.4.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The ``@Security`` annotation restricts access on controllers::
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
|
||||
class PostController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Security("has_role('ROLE_ADMIN')")
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
The expression can use all functions that you can use in the ``access_control``
|
||||
section of the security bundle configuration, with the addition of the
|
||||
``is_granted()`` function.
|
||||
|
||||
The expression has access to the following variables:
|
||||
|
||||
* ``token``: The current security token;
|
||||
* ``user``: The current user object;
|
||||
* ``request``: The request instance;
|
||||
* ``roles``: The user roles;
|
||||
* and all request attributes.
|
||||
|
||||
The ``is_granted()`` function allows you to restrict access based on variables
|
||||
passed to the controller::
|
||||
|
||||
/**
|
||||
* @Security("is_granted('POST_SHOW', post)")
|
||||
*/
|
||||
public function showAction(Post $post)
|
||||
{
|
||||
}
|
||||
|
||||
Here is another example, making use of multiple functions in the expression::
|
||||
|
||||
/**
|
||||
* @Security("is_granted('POST_SHOW', post) and has_role('ROLE_ADMIN')")
|
||||
*/
|
||||
public function showAction(Post $post)
|
||||
{
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Defining a ``Security`` annotation has the same effect as defining an
|
||||
access control rule, but it is more efficient as the check is only done
|
||||
when this specific route is accessed. To create new access control
|
||||
rules, please refer to `the Security Voters page`_.
|
||||
|
||||
.. tip::
|
||||
|
||||
You can also add a ``@Security`` annotation on a controller class.
|
||||
|
||||
.. _`the Security Voters page`: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
@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.
|
||||
|
||||
.. note::
|
||||
|
||||
If you want to stream your template, you can make it with the following configuration::
|
||||
|
||||
/**
|
||||
* @Template(isStreamable=true)
|
||||
*/
|
||||
public function showAction($id)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
.. 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);
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
If you are using PHP as a templating system, you need to make it
|
||||
explicit::
|
||||
|
||||
/**
|
||||
* @Template(engine="php")
|
||||
*/
|
||||
public function showAction($id)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
SensioFrameworkExtraBundle
|
||||
==========================
|
||||
|
||||
The default Symfony ``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
|
||||
------------
|
||||
|
||||
Before using this bundle in your project, add it to your ``composer.json`` file:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ composer require sensio/framework-extra-bundle
|
||||
|
||||
Then, like for any other bundle, include it in your Kernel class::
|
||||
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = array(
|
||||
// ...
|
||||
|
||||
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
|
||||
);
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
.. _release-cycle-note:
|
||||
|
||||
.. note::
|
||||
|
||||
Since SensioFrameworkExtraBundle 3.0 its release cycle is out of sync
|
||||
with Symfony's release cycle. This means that you can simply require
|
||||
``sensio/framework-extra-bundle: ~3.0`` in your ``composer.json`` file
|
||||
and Composer will automatically pick the latest bundle version for you.
|
||||
You have to use Symfony 2.3 or later for this workflow. Before Symfony
|
||||
2.3, the required version of the SensioFrameworkExtraBundle should be
|
||||
the same as your Symfony version.
|
||||
|
||||
If you plan to use or create annotations for controllers, make sure to update
|
||||
your ``autoload.php`` by adding the following line::
|
||||
|
||||
Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
|
||||
|
||||
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, auto_convert: true }
|
||||
view: { annotations: true }
|
||||
cache: { annotations: true }
|
||||
security: { annotations: true }
|
||||
psr_message: { enabled: false } # Defaults to true if the PSR-7 bridge is installed
|
||||
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<!-- xmlns:sensio-framework-extra="http://symfony.com/schema/dic/symfony_extra" -->
|
||||
<sensio-framework-extra:config>
|
||||
<router annotations="true" />
|
||||
<request converters="true" auto_convert="true" />
|
||||
<view annotations="true" />
|
||||
<cache annotations="true" />
|
||||
<security annotations="true" />
|
||||
<psr-message enabled="false" /> <!-- Defaults to true if the PSR-7 bridge is installed -->
|
||||
</sensio-framework-extra:config>
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// load the profiler
|
||||
$container->loadFromExtension('sensio_framework_extra', array(
|
||||
'router' => array('annotations' => true),
|
||||
'request' => array('converters' => true, 'auto_convert' => true),
|
||||
'view' => array('annotations' => true),
|
||||
'cache' => array('annotations' => true),
|
||||
'security' => array('annotations' => true),
|
||||
'psr_message' => array('enabled' => false), // Defaults to true if the PSR-7 bridge is installed
|
||||
));
|
||||
|
||||
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 Symfony 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
|
||||
annotations/security
|
||||
|
||||
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;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
|
||||
/**
|
||||
* @Route("/blog")
|
||||
* @Cache(expires="tomorrow")
|
||||
*/
|
||||
class AnnotController
|
||||
{
|
||||
/**
|
||||
* @Route("/")
|
||||
* @Template
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
$posts = ...;
|
||||
|
||||
return array('posts' => $posts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}")
|
||||
* @Method("GET")
|
||||
* @ParamConverter("post", class="SensioBlogBundle:Post")
|
||||
* @Template("SensioBlogBundle:Annot:show.html.twig", vars={"post"})
|
||||
* @Cache(smaxage="15", lastmodified="post.getUpdatedAt()", etag="'Post' ~ post.getId() ~ post.getUpdatedAt()")
|
||||
* @Security("has_role('ROLE_ADMIN') and is_granted('POST_SHOW', post)")
|
||||
*/
|
||||
public function showAction(Post $post)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
As the ``showAction`` method follows some conventions, you can omit some
|
||||
annotations::
|
||||
|
||||
/**
|
||||
* @Route("/{id}")
|
||||
* @Cache(smaxage="15", lastModified="post.getUpdatedAt()", ETag="'Post' ~ post.getId() ~ post.getUpdatedAt()")
|
||||
* @Security("has_role('ROLE_ADMIN') and is_granted('POST_SHOW', post)")
|
||||
*/
|
||||
public function showAction(Post $post)
|
||||
{
|
||||
}
|
||||
|
||||
The routes need to be imported to be active as any other routing resources, for
|
||||
example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# app/config/routing.yml
|
||||
|
||||
# import routes from a controller directory
|
||||
annot:
|
||||
resource: "@AnnotRoutingBundle/Controller"
|
||||
type: annotation
|
||||
|
||||
see :ref:`Annotated Routes Activation<frameworkextra-annotations-routing-activation>` for more details.
|
||||
|
||||
PSR-7 support
|
||||
-------------
|
||||
|
||||
SensioFrameworkExtraBundle provides support for HTTP messages interfaces defined
|
||||
in `PSR-7`_. It allows to inject instances of ``Psr\Http\Message\ServerRequestInterface``
|
||||
and to return instances of ``Psr\Http\Message\ResponseInterface`` in controllers.
|
||||
|
||||
To enable this feature, `the HttpFoundation to PSR-7 bridge`_ and `Zend Diactoros`_ must be installed:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ composer require symfony/psr-http-message-bridge zendframework/zend-diactoros
|
||||
|
||||
Then, PSR-7 messages can be used directly in controllers like in the following code
|
||||
snippet::
|
||||
|
||||
namespace AppBundle\Controller;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response;
|
||||
|
||||
class DefaultController
|
||||
{
|
||||
public function indexAction(ServerRequestInterface $request)
|
||||
{
|
||||
// Interact with the PSR-7 request
|
||||
|
||||
$response = new Response();
|
||||
// Interact with the PSR-7 response
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
Note that internally, Symfony always use :class:`Symfony\\Component\\HttpFoundation\\Request`
|
||||
and :class:`Symfony\\Component\\HttpFoundation\\Response` instances.
|
||||
|
||||
.. _`SensioFrameworkExtraBundle`: https://github.com/sensiolabs/SensioFrameworkExtraBundle
|
||||
.. _`PSR-7`: http://www.php-fig.org/psr/psr-7/
|
||||
.. _`the HttpFoundation to PSR-7 bridge`: https://github.com/symfony/psr-http-message-bridge
|
||||
.. _`Zend Diactoros`: https://github.com/zendframework/zend-diactoros
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?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\Routing;
|
||||
|
||||
use Symfony\Component\Routing\Loader\AnnotationClassLoader;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route as FrameworkExtraBundleRoute;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
||||
|
||||
/**
|
||||
* 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 HTTP 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
|
||||
* @param mixed $annot The annotation class instance
|
||||
*
|
||||
* @throws \LogicException When the service option is specified on a method
|
||||
*/
|
||||
protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot)
|
||||
{
|
||||
// controller
|
||||
$classAnnot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass);
|
||||
if ($classAnnot instanceof FrameworkExtraBundleRoute && $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->setMethods(implode('|', $configuration->getMethods()));
|
||||
} elseif ($configuration instanceof FrameworkExtraBundleRoute && $configuration->getService()) {
|
||||
throw new \LogicException('The service option can only be specified at class level.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getGlobals(\ReflectionClass $class)
|
||||
{
|
||||
$globals = parent::getGlobals($class);
|
||||
|
||||
foreach ($this->reader->getClassAnnotations($class) as $configuration) {
|
||||
if ($configuration instanceof Method) {
|
||||
$globals['methods'] = array_merge($globals['methods'], $configuration->getMethods());
|
||||
}
|
||||
}
|
||||
|
||||
return $globals;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 The default route name
|
||||
*/
|
||||
protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method)
|
||||
{
|
||||
$routeName = parent::getDefaultRouteName($class, $method);
|
||||
|
||||
return preg_replace(array(
|
||||
'/(bundle|controller)_/',
|
||||
'/action(_\d+)?$/',
|
||||
'/__/',
|
||||
), array(
|
||||
'_',
|
||||
'\\1',
|
||||
'_',
|
||||
), $routeName);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?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\Security;
|
||||
|
||||
use Symfony\Component\Security\Core\Authorization\ExpressionLanguage as BaseExpressionLanguage;
|
||||
|
||||
/**
|
||||
* Adds some function to the default Symfony Security ExpressionLanguage.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ExpressionLanguage extends BaseExpressionLanguage
|
||||
{
|
||||
protected function registerFunctions()
|
||||
{
|
||||
parent::registerFunctions();
|
||||
|
||||
$this->register('is_granted', function ($attributes, $object = 'null') {
|
||||
return sprintf('$auth_checker->isGranted(%s, %s)', $attributes, $object);
|
||||
}, function (array $variables, $attributes, $object = null) {
|
||||
return $variables['auth_checker']->isGranted($attributes, $object);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler\AddParamConverterPass;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler\LegacyPass;
|
||||
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
class SensioFrameworkExtraBundle extends Bundle
|
||||
{
|
||||
public function build(ContainerBuilder $container)
|
||||
{
|
||||
parent::build($container);
|
||||
|
||||
$container->addCompilerPass(new AddParamConverterPass());
|
||||
$container->addCompilerPass(new LegacyPass());
|
||||
$container->addCompilerPass(new AddExpressionLanguageProvidersPass());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
<?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\Templating;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
|
||||
/**
|
||||
* The TemplateGuesser class handles the guessing of template name based on controller.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TemplateGuesser
|
||||
{
|
||||
/**
|
||||
* @var KernelInterface
|
||||
*/
|
||||
protected $kernel;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $controllerPatterns;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param KernelInterface $kernel A KernelInterface instance
|
||||
* @param string[] $controllerPatterns Regexps extracting the controller name from its FQN.
|
||||
*/
|
||||
public function __construct(KernelInterface $kernel, array $controllerPatterns = array())
|
||||
{
|
||||
$controllerPatterns[] = '/Controller\\\(.+)Controller$/';
|
||||
|
||||
$this->kernel = $kernel;
|
||||
$this->controllerPatterns = $controllerPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses and returns the template name to render based on the controller
|
||||
* and action names.
|
||||
*
|
||||
* @param callable $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')
|
||||
{
|
||||
if (is_object($controller) && method_exists($controller, '__invoke')) {
|
||||
$controller = array($controller, '__invoke');
|
||||
} elseif (!is_array($controller)) {
|
||||
throw new \InvalidArgumentException(sprintf('First argument of %s must be an array callable or an object defining the magic method __invoke. "%s" given.', __METHOD__, gettype($controller)));
|
||||
}
|
||||
|
||||
$className = class_exists('Doctrine\Common\Util\ClassUtils') ? ClassUtils::getClass($controller[0]) : get_class($controller[0]);
|
||||
|
||||
$matchController = null;
|
||||
foreach ($this->controllerPatterns as $pattern) {
|
||||
if (preg_match($pattern, $className, $tempMatch)) {
|
||||
$matchController = $tempMatch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null === $matchController) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (its FQN must match one of the following regexps: "%s")', get_class($controller[0]), implode('", "', $this->controllerPatterns)));
|
||||
}
|
||||
|
||||
if ($controller[1] === '__invoke') {
|
||||
$matchAction = $matchController;
|
||||
$matchController = null;
|
||||
} elseif (!preg_match('/^(.+)Action$/', $controller[1], $matchAction)) {
|
||||
$matchAction = array(null, $controller[1]);
|
||||
}
|
||||
|
||||
$bundle = $this->getBundleForClass($className);
|
||||
|
||||
if ($bundle) {
|
||||
while ($bundleName = $bundle->getName()) {
|
||||
if (null === $parentBundleName = $bundle->getParent()) {
|
||||
$bundleName = $bundle->getName();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$bundles = $this->kernel->getBundle($parentBundleName, false);
|
||||
$bundle = array_pop($bundles);
|
||||
}
|
||||
} else {
|
||||
$bundleName = null;
|
||||
}
|
||||
|
||||
return new TemplateReference($bundleName, $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
|
||||
*
|
||||
* @return Bundle|null $bundle A Bundle instance
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?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\Tests\Configuration;
|
||||
|
||||
class ConfigurationAnnotationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testUndefinedSetterThrowsException()
|
||||
{
|
||||
$this->getMockForAbstractClass('Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationAnnotation', array(
|
||||
array(
|
||||
'doesNotExists' => true,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Configuration;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
|
||||
/**
|
||||
* @author Iltar van der Berg <ivanderberg@hostnet.nl>
|
||||
*/
|
||||
class RouteTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSetServiceWithoutPath()
|
||||
{
|
||||
$route = new Route(array());
|
||||
$this->assertNull($route->getPath());
|
||||
$this->assertNull($route->getService());
|
||||
|
||||
$route->setService('app.test');
|
||||
|
||||
$this->assertSame('', $route->getPath());
|
||||
$this->assertSame('app.test', $route->getService());
|
||||
}
|
||||
|
||||
public function testSetServiceWithPath()
|
||||
{
|
||||
$route = new Route(array());
|
||||
$this->assertNull($route->getPath());
|
||||
$this->assertNull($route->getService());
|
||||
|
||||
$route->setPath('/test/');
|
||||
$route->setService('app.test');
|
||||
|
||||
$this->assertSame('/test/', $route->getPath());
|
||||
$this->assertSame('app.test', $route->getService());
|
||||
}
|
||||
|
||||
public function testSettersViaConstruct()
|
||||
{
|
||||
$route = new Route(array('service' => 'app.test'));
|
||||
$this->assertSame('', $route->getPath());
|
||||
$this->assertSame('app.test', $route->getService());
|
||||
|
||||
$route = new Route(array('service' => 'app.test', 'path' => '/test/'));
|
||||
$this->assertSame('/test/', $route->getPath());
|
||||
$this->assertSame('app.test', $route->getService());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<?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\Tests\DependencyInjection;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class AddExpressionLanguageProvidersPassTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var AddExpressionLanguageProvidersPass
|
||||
*/
|
||||
private $pass;
|
||||
|
||||
/**
|
||||
* @var ContainerBuilder
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @var Definition
|
||||
*/
|
||||
private $expressionLangDefinition;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->pass = new AddExpressionLanguageProvidersPass();
|
||||
$this->container = new ContainerBuilder();
|
||||
$this->expressionLangDefinition = new Definition();
|
||||
$this->container->setDefinition('sensio_framework_extra.security.expression_language.default', $this->expressionLangDefinition);
|
||||
}
|
||||
|
||||
public function testProcessNoOpNoExpressionLang()
|
||||
{
|
||||
$this->container->removeDefinition('sensio_framework_extra.security.expression_language.default');
|
||||
$this->pass->process($this->container);
|
||||
}
|
||||
|
||||
public function testProcessNoOpNoTaggedServices()
|
||||
{
|
||||
$this->pass->process($this->container);
|
||||
$this->assertCount(0, $this->expressionLangDefinition->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testProcessAddsTaggedServices()
|
||||
{
|
||||
$provider = new Definition();
|
||||
$provider->setTags(array(
|
||||
'security.expression_language_provider' => array(
|
||||
array(),
|
||||
),
|
||||
));
|
||||
|
||||
$this->container->setDefinition('provider', $provider);
|
||||
|
||||
$this->pass->process($this->container);
|
||||
|
||||
$methodCalls = $this->expressionLangDefinition->getMethodCalls();
|
||||
$this->assertCount(1, $methodCalls);
|
||||
$this->assertEquals(array('registerProvider', array(new Reference('provider'))), $methodCalls[0]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
<?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\Tests\DependencyInjection;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler\AddParamConverterPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class AddParamConverterPassTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var AddParamConverterPass
|
||||
*/
|
||||
private $pass;
|
||||
|
||||
/**
|
||||
* @var ContainerBuilder
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @var Definition
|
||||
*/
|
||||
private $managerDefinition;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->pass = new AddParamConverterPass();
|
||||
$this->container = new ContainerBuilder();
|
||||
$this->managerDefinition = new Definition();
|
||||
$this->container->setDefinition('sensio_framework_extra.converter.manager', $this->managerDefinition);
|
||||
}
|
||||
|
||||
public function testProcessNoOpNoManager()
|
||||
{
|
||||
$this->container->removeDefinition('sensio_framework_extra.converter.manager');
|
||||
$this->pass->process($this->container);
|
||||
}
|
||||
|
||||
public function testProcessNoOpNoTaggedServices()
|
||||
{
|
||||
$this->pass->process($this->container);
|
||||
$this->assertCount(0, $this->managerDefinition->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testProcessAddsTaggedServices()
|
||||
{
|
||||
$paramConverter1 = new Definition();
|
||||
$paramConverter1->setTags(array(
|
||||
'request.param_converter' => array(
|
||||
array(
|
||||
'priority' => 'false',
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$paramConverter2 = new Definition();
|
||||
$paramConverter2->setTags(array(
|
||||
'request.param_converter' => array(
|
||||
array(
|
||||
'converter' => 'foo',
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$paramConverter3 = new Definition();
|
||||
$paramConverter3->setTags(array(
|
||||
'request.param_converter' => array(
|
||||
array(
|
||||
'priority' => 5,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$this->container->setDefinition('param_converter_one', $paramConverter1);
|
||||
$this->container->setDefinition('param_converter_two', $paramConverter2);
|
||||
$this->container->setDefinition('param_converter_three', $paramConverter3);
|
||||
|
||||
$this->pass->process($this->container);
|
||||
|
||||
$methodCalls = $this->managerDefinition->getMethodCalls();
|
||||
$this->assertCount(3, $methodCalls);
|
||||
$this->assertEquals(array('add', array(new Reference('param_converter_one'), 0, null)), $methodCalls[0]);
|
||||
$this->assertEquals(array('add', array(new Reference('param_converter_two'), 0, 'foo')), $methodCalls[1]);
|
||||
$this->assertEquals(array('add', array(new Reference('param_converter_three'), 5, null)), $methodCalls[2]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
<?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\Tests\DependencyInjection;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\SensioFrameworkExtraExtension;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler\LegacyPass;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
|
||||
class SensioFrameworkExtraExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testLegacySecurityListener()
|
||||
{
|
||||
if (interface_exists('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../../Resources/config'));
|
||||
$loader->load('security.xml');
|
||||
$r = new \ReflectionClass('Symfony\Bundle\SecurityBundle\SecurityBundle');
|
||||
$loader = new XmlFileLoader($container, new FileLocator(dirname($r->getFileName()).'/Resources/config'));
|
||||
$loader->load('security.xml');
|
||||
$this->registerLegacyPass($container);
|
||||
$container->compile();
|
||||
|
||||
$securityContext = $container->getDefinition('sensio_framework_extra.security.listener')->getArgument(0);
|
||||
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $securityContext);
|
||||
}
|
||||
|
||||
public function testSecurityListener()
|
||||
{
|
||||
if (!interface_exists('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../../Resources/config'));
|
||||
$loader->load('security.xml');
|
||||
$r = new \ReflectionClass('Symfony\Bundle\SecurityBundle\SecurityBundle');
|
||||
$loader = new XmlFileLoader($container, new FileLocator(dirname($r->getFileName()).'/Resources/config'));
|
||||
$loader->load('security.xml');
|
||||
$this->registerLegacyPass($container);
|
||||
$container->compile();
|
||||
|
||||
$this->assertNull($container->getDefinition('sensio_framework_extra.security.listener')->getArgument(0));
|
||||
}
|
||||
|
||||
public function testDefaultExpressionLanguageConfig()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$extension = new SensioFrameworkExtraExtension();
|
||||
$extension->load(array(), $container);
|
||||
|
||||
$this->assertAlias($container, 'sensio_framework_extra.security.expression_language.default', 'sensio_framework_extra.security.expression_language');
|
||||
}
|
||||
|
||||
public function testOverrideExpressionLanguageConfig()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$extension = new SensioFrameworkExtraExtension();
|
||||
$config = array(
|
||||
'security' => array(
|
||||
'expression_language' => 'acme.security.expression_language',
|
||||
),
|
||||
);
|
||||
|
||||
$container->setDefinition('acme.security.expression_language', new Definition());
|
||||
|
||||
$extension->load(array($config), $container);
|
||||
|
||||
$this->assertAlias($container, 'acme.security.expression_language', 'sensio_framework_extra.security.expression_language');
|
||||
}
|
||||
|
||||
public function testTemplatingControllerPatterns()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$extension = new SensioFrameworkExtraExtension();
|
||||
$config = array(
|
||||
'templating' => array(
|
||||
'controller_patterns' => $patterns = array('/foo/', '/bar/', '/foobar/'),
|
||||
),
|
||||
);
|
||||
|
||||
$extension->load(array($config), $container);
|
||||
|
||||
$this->assertEquals($patterns, $container->getDefinition('sensio_framework_extra.view.guesser')->getArgument(1));
|
||||
}
|
||||
|
||||
private function assertAlias(ContainerBuilder $container, $value, $key)
|
||||
{
|
||||
$this->assertEquals($value, (string) $container->getAlias($key), sprintf('%s alias is correct', $key));
|
||||
}
|
||||
|
||||
private function registerLegacyPass(ContainerBuilder $container)
|
||||
{
|
||||
$passConfig = $container->getCompiler()->getPassConfig();
|
||||
$passConfig->setAfterRemovingPasses(array());
|
||||
$passConfig->setBeforeOptimizationPasses(array());
|
||||
$passConfig->setBeforeRemovingPasses(array());
|
||||
$passConfig->setOptimizationPasses(array());
|
||||
$passConfig->setRemovingPasses(array());
|
||||
$container->addCompilerPass(new LegacyPass());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<?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\Tests\EventListener;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerCacheAtClass;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerCacheAtClassAndMethod;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerCacheAtMethod;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerMultipleCacheAtClass;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerMultipleCacheAtMethod;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerParamConverterAtClassAndMethod;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
class ControllerListenerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
$this->listener = new ControllerListener(new AnnotationReader());
|
||||
$this->request = $this->createRequest();
|
||||
|
||||
// trigger the autoloading of the @Cache annotation
|
||||
class_exists('Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache');
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
$this->listener = null;
|
||||
$this->request = null;
|
||||
}
|
||||
|
||||
public function testCacheAnnotationAtMethod()
|
||||
{
|
||||
$controller = new FooControllerCacheAtMethod();
|
||||
|
||||
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
|
||||
$this->listener->onKernelController($this->event);
|
||||
|
||||
$this->assertNotNull($this->getReadedCache());
|
||||
$this->assertEquals(FooControllerCacheAtMethod::METHOD_SMAXAGE, $this->getReadedCache()->getSMaxAge());
|
||||
}
|
||||
|
||||
public function testCacheAnnotationAtClass()
|
||||
{
|
||||
$controller = new FooControllerCacheAtClass();
|
||||
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
|
||||
$this->listener->onKernelController($this->event);
|
||||
|
||||
$this->assertNotNull($this->getReadedCache());
|
||||
$this->assertEquals(FooControllerCacheAtClass::CLASS_SMAXAGE, $this->getReadedCache()->getSMaxAge());
|
||||
}
|
||||
|
||||
public function testCacheAnnotationAtClassAndMethod()
|
||||
{
|
||||
$controller = new FooControllerCacheAtClassAndMethod();
|
||||
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
|
||||
$this->listener->onKernelController($this->event);
|
||||
|
||||
$this->assertNotNull($this->getReadedCache());
|
||||
$this->assertEquals(FooControllerCacheAtClassAndMethod::METHOD_SMAXAGE, $this->getReadedCache()->getSMaxAge());
|
||||
|
||||
$this->event = $this->getFilterControllerEvent(array($controller, 'bar2Action'), $this->request);
|
||||
$this->listener->onKernelController($this->event);
|
||||
|
||||
$this->assertNotNull($this->getReadedCache());
|
||||
$this->assertEquals(FooControllerCacheAtClassAndMethod::CLASS_SMAXAGE, $this->getReadedCache()->getSMaxAge());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Multiple "cache" annotations are not allowed
|
||||
*/
|
||||
public function testMultipleAnnotationsOnClassThrowsExceptionUnlessConfigurationAllowsArray()
|
||||
{
|
||||
$controller = new FooControllerMultipleCacheAtClass();
|
||||
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
|
||||
$this->listener->onKernelController($this->event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Multiple "cache" annotations are not allowed
|
||||
*/
|
||||
public function testMultipleAnnotationsOnMethodThrowsExceptionUnlessConfigurationAllowsArray()
|
||||
{
|
||||
$controller = new FooControllerMultipleCacheAtMethod();
|
||||
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
|
||||
$this->listener->onKernelController($this->event);
|
||||
}
|
||||
|
||||
public function testMultipleParamConverterAnnotationsOnMethod()
|
||||
{
|
||||
$paramConverter = new \Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter(array());
|
||||
$controller = new FooControllerParamConverterAtClassAndMethod();
|
||||
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
|
||||
$this->listener->onKernelController($this->event);
|
||||
|
||||
$annotations = $this->request->attributes->get('_converters');
|
||||
$this->assertNotNull($annotations);
|
||||
$this->assertArrayHasKey(0, $annotations);
|
||||
$this->assertInstanceOf('Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter', $annotations[0]);
|
||||
$this->assertEquals('test', $annotations[0]->getName());
|
||||
|
||||
$this->assertArrayHasKey(1, $annotations);
|
||||
$this->assertInstanceOf('Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter', $annotations[1]);
|
||||
$this->assertEquals('test2', $annotations[1]->getName());
|
||||
|
||||
$this->assertEquals(2, count($annotations));
|
||||
}
|
||||
|
||||
protected function createRequest(Cache $cache = null)
|
||||
{
|
||||
return new Request(array(), array(), array(
|
||||
'_cache' => $cache,
|
||||
));
|
||||
}
|
||||
|
||||
protected function getFilterControllerEvent($controller, Request $request)
|
||||
{
|
||||
$mockKernel = $this->getMockForAbstractClass('Symfony\Component\HttpKernel\Kernel', array('', ''));
|
||||
|
||||
return new FilterControllerEvent($mockKernel, $controller, $request, HttpKernelInterface::MASTER_REQUEST);
|
||||
}
|
||||
|
||||
protected function getReadedCache()
|
||||
{
|
||||
return $this->request->attributes->get('_cache');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
|
||||
/**
|
||||
* @Cache(smaxage="20")
|
||||
*/
|
||||
class FooControllerCacheAtClass
|
||||
{
|
||||
const CLASS_SMAXAGE = 20;
|
||||
|
||||
public function barAction()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
|
||||
/**
|
||||
* @Cache(smaxage="20")
|
||||
*/
|
||||
class FooControllerCacheAtClassAndMethod
|
||||
{
|
||||
const CLASS_SMAXAGE = 20;
|
||||
const METHOD_SMAXAGE = 25;
|
||||
|
||||
/**
|
||||
* @Cache(smaxage="25")
|
||||
*/
|
||||
public function barAction()
|
||||
{
|
||||
}
|
||||
|
||||
public function bar2Action()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
|
||||
class FooControllerCacheAtMethod
|
||||
{
|
||||
const METHOD_SMAXAGE = 15;
|
||||
|
||||
/**
|
||||
* @Cache(smaxage="15")
|
||||
*/
|
||||
public function barAction()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
|
||||
/**
|
||||
* @Cache()
|
||||
* @Cache()
|
||||
*/
|
||||
class FooControllerMultipleCacheAtClass
|
||||
{
|
||||
public function barAction()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
|
||||
class FooControllerMultipleCacheAtMethod
|
||||
{
|
||||
/**
|
||||
* @Cache()
|
||||
* @Cache()
|
||||
*/
|
||||
public function barAction()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
|
||||
|
||||
class FooControllerNullableParameter
|
||||
{
|
||||
public function requiredParamAction(\DateTime $param)
|
||||
{
|
||||
}
|
||||
|
||||
public function defaultParamAction(\DateTime $param = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function nullableParamAction(?\DateTime $param)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
|
||||
/**
|
||||
* @ParamConverter("test")
|
||||
*/
|
||||
class FooControllerParamConverterAtClassAndMethod
|
||||
{
|
||||
/**
|
||||
* @ParamConverter("test2")
|
||||
*/
|
||||
public function barAction($test, $test2)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
<?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\Tests\EventListener;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\EventListener\HttpCacheListener;
|
||||
|
||||
class HttpCacheListenerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
$this->listener = new HttpCacheListener();
|
||||
$this->response = new Response();
|
||||
$this->cache = new Cache(array());
|
||||
$this->request = $this->createRequest($this->cache);
|
||||
$this->event = $this->createEventMock($this->request, $this->response);
|
||||
}
|
||||
|
||||
public function testWontReassignResponseWhenResponseIsUnsuccessful()
|
||||
{
|
||||
$this->event
|
||||
->expects($this->never())
|
||||
->method('setResponse')
|
||||
;
|
||||
|
||||
$this->response->setStatusCode(500);
|
||||
|
||||
$this->assertInternalType('null', $this->listener->onKernelResponse($this->event));
|
||||
}
|
||||
|
||||
public function testWontReassignResponseWhenNoConfigurationIsPresent()
|
||||
{
|
||||
$this->event
|
||||
->expects($this->never())
|
||||
->method('setResponse')
|
||||
;
|
||||
|
||||
$this->request->attributes->remove('_cache');
|
||||
|
||||
$this->assertInternalType('null', $this->listener->onKernelResponse($this->event));
|
||||
}
|
||||
|
||||
public function testResponseIsPublicIfConfigurationIsPublicTrue()
|
||||
{
|
||||
$request = $this->createRequest(new Cache(array(
|
||||
'public' => true,
|
||||
)));
|
||||
|
||||
$this->listener->onKernelResponse($this->createEventMock($request, $this->response));
|
||||
|
||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('public'));
|
||||
$this->assertFalse($this->response->headers->hasCacheControlDirective('private'));
|
||||
}
|
||||
|
||||
public function testResponseIsPrivateIfConfigurationIsPublicFalse()
|
||||
{
|
||||
$request = $this->createRequest(new Cache(array(
|
||||
'public' => false,
|
||||
)));
|
||||
|
||||
$this->listener->onKernelResponse($this->createEventMock($request, $this->response));
|
||||
|
||||
$this->assertFalse($this->response->headers->hasCacheControlDirective('public'));
|
||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('private'));
|
||||
}
|
||||
|
||||
public function testResponseVary()
|
||||
{
|
||||
$vary = array('foobar');
|
||||
$request = $this->createRequest(new Cache(array('vary' => $vary)));
|
||||
|
||||
$this->listener->onKernelResponse($this->createEventMock($request, $this->response));
|
||||
$this->assertTrue($this->response->hasVary());
|
||||
$result = $this->response->getVary();
|
||||
$this->assertEquals($vary, $result);
|
||||
}
|
||||
|
||||
public function testResponseVaryWhenVaryNotSet()
|
||||
{
|
||||
$request = $this->createRequest(new Cache(array()));
|
||||
$vary = array('foobar');
|
||||
$this->response->setVary($vary);
|
||||
|
||||
$this->listener->onKernelResponse($this->createEventMock($request, $this->response));
|
||||
$this->assertTrue($this->response->hasVary());
|
||||
$result = $this->response->getVary();
|
||||
$this->assertFalse(empty($result), 'Existing vary headers should not be removed');
|
||||
$this->assertEquals($vary, $result, 'Vary header should not be changed');
|
||||
}
|
||||
|
||||
public function testResponseIsPrivateIfConfigurationIsPublicNotSet()
|
||||
{
|
||||
$request = $this->createRequest(new Cache(array()));
|
||||
|
||||
$this->listener->onKernelResponse($this->createEventMock($request, $this->response));
|
||||
|
||||
$this->assertFalse($this->response->headers->hasCacheControlDirective('public'));
|
||||
}
|
||||
|
||||
public function testConfigurationAttributesAreSetOnResponse()
|
||||
{
|
||||
$this->assertInternalType('null', $this->response->getMaxAge());
|
||||
$this->assertInternalType('null', $this->response->getExpires());
|
||||
$this->assertFalse($this->response->headers->hasCacheControlDirective('s-maxage'));
|
||||
|
||||
$this->request->attributes->set('_cache', new Cache(array(
|
||||
'expires' => 'tomorrow',
|
||||
'smaxage' => '15',
|
||||
'maxage' => '15',
|
||||
)));
|
||||
|
||||
$this->listener->onKernelResponse($this->event);
|
||||
|
||||
$this->assertEquals('15', $this->response->getMaxAge());
|
||||
$this->assertEquals('15', $this->response->headers->getCacheControlDirective('s-maxage'));
|
||||
$this->assertInstanceOf('DateTime', $this->response->getExpires());
|
||||
}
|
||||
|
||||
public function testCacheMaxAgeSupportsStrtotimeFormat()
|
||||
{
|
||||
$this->request->attributes->set('_cache', new Cache(array(
|
||||
'smaxage' => '1 day',
|
||||
'maxage' => '1 day',
|
||||
)));
|
||||
|
||||
$this->listener->onKernelResponse($this->event);
|
||||
|
||||
$this->assertEquals(60 * 60 * 24, $this->response->headers->getCacheControlDirective('s-maxage'));
|
||||
$this->assertEquals(60 * 60 * 24, $this->response->getMaxAge());
|
||||
}
|
||||
|
||||
public function testLastModifiedNotModifiedResponse()
|
||||
{
|
||||
$request = $this->createRequest(new Cache(array('lastModified' => 'test.getDate()')));
|
||||
$request->attributes->set('test', new TestEntity());
|
||||
$request->headers->add(array('If-Modified-Since' => 'Fri, 23 Aug 2013 00:00:00 GMT'));
|
||||
|
||||
$listener = new HttpCacheListener();
|
||||
$controllerEvent = new FilterControllerEvent($this->getKernel(), function () { return new Response(500); }, $request, null);
|
||||
|
||||
$listener->onKernelController($controllerEvent);
|
||||
$response = call_user_func($controllerEvent->getController());
|
||||
|
||||
$this->assertEquals(304, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testLastModifiedHeader()
|
||||
{
|
||||
$request = $this->createRequest(new Cache(array('lastModified' => 'test.getDate()')));
|
||||
$request->attributes->set('test', new TestEntity());
|
||||
$response = new Response();
|
||||
|
||||
$listener = new HttpCacheListener();
|
||||
$controllerEvent = new FilterControllerEvent($this->getKernel(), function () { return new Response(); }, $request, null);
|
||||
$listener->onKernelController($controllerEvent);
|
||||
|
||||
$responseEvent = new FilterResponseEvent($this->getKernel(), $request, null, call_user_func($controllerEvent->getController()));
|
||||
$listener->onKernelResponse($responseEvent);
|
||||
|
||||
$response = $responseEvent->getResponse();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertTrue($response->headers->has('Last-Modified'));
|
||||
$this->assertEquals('Fri, 23 Aug 2013 00:00:00 GMT', $response->headers->get('Last-Modified'));
|
||||
}
|
||||
|
||||
public function testETagNotModifiedResponse()
|
||||
{
|
||||
$request = $this->createRequest(new Cache(array('etag' => 'test.getId()')));
|
||||
$request->attributes->set('test', $entity = new TestEntity());
|
||||
$request->headers->add(array('If-None-Match' => sprintf('"%s"', hash('sha256', $entity->getId()))));
|
||||
|
||||
$listener = new HttpCacheListener();
|
||||
$controllerEvent = new FilterControllerEvent($this->getKernel(), function () { return new Response(500); }, $request, null);
|
||||
|
||||
$listener->onKernelController($controllerEvent);
|
||||
$response = call_user_func($controllerEvent->getController());
|
||||
|
||||
$this->assertEquals(304, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testETagHeader()
|
||||
{
|
||||
$request = $this->createRequest(new Cache(array('ETag' => 'test.getId()')));
|
||||
$request->attributes->set('test', $entity = new TestEntity());
|
||||
$response = new Response();
|
||||
|
||||
$listener = new HttpCacheListener();
|
||||
$controllerEvent = new FilterControllerEvent($this->getKernel(), function () { return new Response(); }, $request, null);
|
||||
$listener->onKernelController($controllerEvent);
|
||||
|
||||
$responseEvent = new FilterResponseEvent($this->getKernel(), $request, null, call_user_func($controllerEvent->getController()));
|
||||
$listener->onKernelResponse($responseEvent);
|
||||
|
||||
$response = $responseEvent->getResponse();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertTrue($response->headers->has('ETag'));
|
||||
$this->assertContains(hash('sha256', $entity->getId()), $response->headers->get('ETag'));
|
||||
}
|
||||
|
||||
private function createRequest(Cache $cache = null)
|
||||
{
|
||||
return new Request(array(), array(), array(
|
||||
'_cache' => $cache,
|
||||
));
|
||||
}
|
||||
|
||||
private function createEventMock(Request $request, Response $response)
|
||||
{
|
||||
$event = $this
|
||||
->getMockBuilder('Symfony\Component\HttpKernel\Event\FilterResponseEvent')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$event
|
||||
->expects($this->any())
|
||||
->method('getRequest')
|
||||
->will($this->returnValue($request))
|
||||
;
|
||||
|
||||
$event
|
||||
->expects($this->any())
|
||||
->method('getResponse')
|
||||
->will($this->returnValue($response))
|
||||
;
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
private function getKernel()
|
||||
{
|
||||
return $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
|
||||
}
|
||||
}
|
||||
|
||||
class TestEntity
|
||||
{
|
||||
public function getDate()
|
||||
{
|
||||
return new \DateTime('Fri, 23 Aug 2013 00:00:00 GMT');
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return '12345';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
<?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\Tests\EventListener;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerNullableParameter;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
|
||||
class ParamConverterListenerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getControllerWithNoArgsFixtures
|
||||
*/
|
||||
public function testRequestIsSkipped($controllerCallable)
|
||||
{
|
||||
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
|
||||
$request = new Request();
|
||||
|
||||
$listener = new ParamConverterListener($this->getParamConverterManager($request, array()));
|
||||
$event = new FilterControllerEvent($kernel, $controllerCallable, $request, null);
|
||||
|
||||
$listener->onKernelController($event);
|
||||
}
|
||||
|
||||
public function getControllerWithNoArgsFixtures()
|
||||
{
|
||||
return array(
|
||||
array(array(new TestController(), 'noArgAction')),
|
||||
array(new InvokableNoArgController()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getControllerWithArgsFixtures
|
||||
*/
|
||||
public function testAutoConvert($controllerCallable)
|
||||
{
|
||||
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
|
||||
$request = new Request(array(), array(), array('date' => '2014-03-14 09:00:00'));
|
||||
|
||||
$converter = new ParamConverter(array('name' => 'date', 'class' => 'DateTime'));
|
||||
|
||||
$listener = new ParamConverterListener($this->getParamConverterManager($request, array('date' => $converter)));
|
||||
$event = new FilterControllerEvent($kernel, $controllerCallable, $request, null);
|
||||
|
||||
$listener->onKernelController($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider settingOptionalParamProvider
|
||||
* @requires PHP 7.1
|
||||
*/
|
||||
public function testSettingOptionalParam($function, $isOptional)
|
||||
{
|
||||
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
|
||||
$request = new Request();
|
||||
|
||||
$converter = new ParamConverter(array('name' => 'param', 'class' => 'DateTime'));
|
||||
$converter->setIsOptional($isOptional);
|
||||
|
||||
$listener = new ParamConverterListener($this->getParamConverterManager($request, array('param' => $converter)), true);
|
||||
$event = new FilterControllerEvent(
|
||||
$kernel,
|
||||
array(
|
||||
new FooControllerNullableParameter(),
|
||||
$function,
|
||||
),
|
||||
$request,
|
||||
null
|
||||
);
|
||||
|
||||
$listener->onKernelController($event);
|
||||
}
|
||||
|
||||
public function settingOptionalParamProvider()
|
||||
{
|
||||
return array(
|
||||
array('requiredParamAction', false),
|
||||
array('defaultParamAction', true),
|
||||
array('nullableParamAction', true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getControllerWithArgsFixtures
|
||||
*/
|
||||
public function testNoAutoConvert($controllerCallable)
|
||||
{
|
||||
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
|
||||
$request = new Request(array(), array(), array('date' => '2014-03-14 09:00:00'));
|
||||
|
||||
$listener = new ParamConverterListener($this->getParamConverterManager($request, array()), false);
|
||||
$event = new FilterControllerEvent($kernel, $controllerCallable, $request, null);
|
||||
|
||||
$listener->onKernelController($event);
|
||||
}
|
||||
|
||||
public function getControllerWithArgsFixtures()
|
||||
{
|
||||
return array(
|
||||
array(array(new TestController(), 'dateAction')),
|
||||
array(new InvokableController()),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getParamConverterManager(Request $request, $configurations)
|
||||
{
|
||||
$manager = $this->getMockBuilder('Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager')->getMock();
|
||||
$manager
|
||||
->expects($this->once())
|
||||
->method('apply')
|
||||
->with($this->equalTo($request), $this->equalTo($configurations))
|
||||
;
|
||||
|
||||
return $manager;
|
||||
}
|
||||
}
|
||||
|
||||
class TestController
|
||||
{
|
||||
public function noArgAction(Request $request)
|
||||
{
|
||||
}
|
||||
|
||||
public function dateAction(\DateTime $date)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class InvokableNoArgController
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class InvokableController
|
||||
{
|
||||
public function __invoke(\DateTime $date)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?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\Tests\EventListener;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\EventListener\PsrResponseListener;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
|
||||
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Response;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
* @requires PHP 5.4
|
||||
*/
|
||||
class PsrResponseListenerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testConvertsControllerResult()
|
||||
{
|
||||
$listener = new PsrResponseListener(new HttpFoundationFactory());
|
||||
$event = $this->createEventMock(new Response());
|
||||
$event->expects($this->once())->method('setResponse')->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Response'));
|
||||
$listener->onKernelView($event);
|
||||
}
|
||||
|
||||
public function testDoesNotConvertControllerResult()
|
||||
{
|
||||
$listener = new PsrResponseListener(new HttpFoundationFactory());
|
||||
$event = $this->createEventMock(array());
|
||||
$event->expects($this->never())->method('setResponse');
|
||||
|
||||
$listener->onKernelView($event);
|
||||
|
||||
$event = $this->createEventMock(null);
|
||||
$event->expects($this->never())->method('setResponse');
|
||||
|
||||
$listener->onKernelView($event);
|
||||
}
|
||||
|
||||
private function createEventMock($controllerResult)
|
||||
{
|
||||
$event = $this
|
||||
->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
$event
|
||||
->expects($this->any())
|
||||
->method('getControllerResult')
|
||||
->will($this->returnValue($controllerResult))
|
||||
;
|
||||
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<?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\Tests\EventListener;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
class SecurityListenerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
|
||||
* @group legacy
|
||||
*/
|
||||
public function testLegacyAccessDenied()
|
||||
{
|
||||
if (!interface_exists('Symfony\Component\Security\Core\SecurityContextInterface')) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
|
||||
|
||||
$token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
|
||||
$token->expects($this->once())->method('getRoles')->will($this->returnValue(array()));
|
||||
|
||||
$securityContext = $this->getMockBuilder('Symfony\Component\Security\Core\SecurityContextInterface')->getMock();
|
||||
$securityContext->expects($this->once())->method('isGranted')->will($this->throwException(new AccessDeniedException()));
|
||||
$securityContext->expects($this->exactly(2))->method('getToken')->will($this->returnValue($token));
|
||||
|
||||
$trustResolver = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface')->getMock();
|
||||
|
||||
$language = new ExpressionLanguage();
|
||||
|
||||
$listener = new SecurityListener($securityContext, $language, $trustResolver);
|
||||
$request = $this->createRequest(new Security(array('expression' => 'has_role("ROLE_ADMIN") or is_granted("FOO")')));
|
||||
|
||||
$event = new FilterControllerEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), function () { return new Response(); }, $request, null);
|
||||
|
||||
$listener->onKernelController($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
|
||||
*/
|
||||
public function testAccessDenied()
|
||||
{
|
||||
if (!interface_exists('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
$token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
|
||||
$token->expects($this->once())->method('getRoles')->will($this->returnValue(array()));
|
||||
|
||||
$tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock();
|
||||
$tokenStorage->expects($this->exactly(2))->method('getToken')->will($this->returnValue($token));
|
||||
|
||||
$authChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock();
|
||||
$authChecker->expects($this->once())->method('isGranted')->will($this->throwException(new AccessDeniedException()));
|
||||
|
||||
$trustResolver = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface')->getMock();
|
||||
|
||||
$language = new ExpressionLanguage();
|
||||
|
||||
$listener = new SecurityListener(null, $language, $trustResolver, null, $tokenStorage, $authChecker);
|
||||
$request = $this->createRequest(new Security(array('expression' => 'has_role("ROLE_ADMIN") or is_granted("FOO")')));
|
||||
|
||||
$event = new FilterControllerEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), function () { return new Response(); }, $request, null);
|
||||
|
||||
$listener->onKernelController($event);
|
||||
}
|
||||
|
||||
private function createRequest(Security $security = null)
|
||||
{
|
||||
return new Request(array(), array(), array(
|
||||
'_security' => $security,
|
||||
));
|
||||
}
|
||||
|
||||
private function getKernel()
|
||||
{
|
||||
return $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Tests\Fixtures\ActionArgumentsBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class ActionArgumentsBundle extends Bundle
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Fixtures\ActionArgumentsBundle\Controller;
|
||||
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @Route("/nullable-arguments")
|
||||
*/
|
||||
class NullableArgumentsController
|
||||
{
|
||||
/**
|
||||
* @Route("/invoke/")
|
||||
*/
|
||||
public function __invoke(RequestInterface $request, MessageInterface $message, ServerRequestInterface $serverRequest)
|
||||
{
|
||||
return new Response('<html><body>ok</body></html>');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/with-default")
|
||||
*/
|
||||
public function withDefaultAction(string $d = null)
|
||||
{
|
||||
return new Response(null === $d ? 'yes' : 'no');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/without-default")
|
||||
*/
|
||||
public function withoutDefaultAction(string $d)
|
||||
{
|
||||
return new Response(null === $d ? 'yes' : 'no');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/nullable")
|
||||
*/
|
||||
public function nullableAction(?string $d)
|
||||
{
|
||||
return new Response(null === $d ? 'yes' : 'no');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Tests\Fixtures\FooBundle\Controller;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
|
||||
|
||||
/**
|
||||
* @Route(service="test.invokable_class_level.predefined")
|
||||
* @Template("FooBundle:Invokable:predefined.html.twig")
|
||||
*/
|
||||
class InvokableClassLevelController
|
||||
{
|
||||
/**
|
||||
* @Route("/invokable/class-level/service/")
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
return array(
|
||||
'foo' => 'bar',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Tests\Fixtures\FooBundle\Controller;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
|
||||
class InvokableContainerController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Route("/invokable/variable/container/{variable}/")
|
||||
* @Template()
|
||||
*/
|
||||
public function variableAction($variable)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/invokable/another-variable/container/{variable}/")
|
||||
* @Template("FooBundle:InvokableContainer:variable.html.twig")
|
||||
*/
|
||||
public function anotherVariableAction($variable)
|
||||
{
|
||||
return array(
|
||||
'variable' => $variable,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/invokable/variable/container/{variable}/{another_variable}/")
|
||||
* @Template("FooBundle:InvokableContainer:another_variable.html.twig")
|
||||
*/
|
||||
public function doubleVariableAction($variable, $another_variable)
|
||||
{
|
||||
return array(
|
||||
'variable' => $variable,
|
||||
'another_variable' => $another_variable,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/invokable/predefined/container/")
|
||||
* @Template("FooBundle:Invokable:predefined.html.twig")
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
return array(
|
||||
'foo' => 'bar',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Tests\Fixtures\FooBundle\Controller;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
|
||||
|
||||
/**
|
||||
* @Route(service="test.invokable.predefined")
|
||||
*/
|
||||
class InvokableController
|
||||
{
|
||||
/**
|
||||
* @Route("/invokable/predefined/service/")
|
||||
* @Template("FooBundle:Invokable:predefined.html.twig")
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
return array(
|
||||
'foo' => 'bar',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Tests\Fixtures\FooBundle\Controller;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
|
||||
/**
|
||||
* @Template("FooBundle:Invokable:predefined.html.twig")
|
||||
*/
|
||||
class MultipleActionsClassLevelTemplateController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Route("/multi/one-template/1/")
|
||||
*/
|
||||
public function firstAction()
|
||||
{
|
||||
return array(
|
||||
'foo' => 'bar',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/multi/one-template/2/")
|
||||
* @Route("/multi/one-template/3/")
|
||||
*/
|
||||
public function secondAction()
|
||||
{
|
||||
return array(
|
||||
'foo' => 'bar',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/multi/one-template/4/")
|
||||
* @Template("FooBundle::overwritten.html.twig")
|
||||
*/
|
||||
public function overwriteAction()
|
||||
{
|
||||
return array(
|
||||
'foo' => 'foo bar baz',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Fixtures\FooBundle\Controller;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @Route("/optional-arguments")
|
||||
*/
|
||||
class OptionalArgumentsController
|
||||
{
|
||||
/**
|
||||
* @Route("/with-default-followed-by-mandatory", defaults={"e" = null})
|
||||
* @ParamConverter(name="d", class="Tests\Fixtures\FooBundle\Entity\Foo")
|
||||
*/
|
||||
public function withDefaultFollowedByMandatory($d = null, $e)
|
||||
{
|
||||
return new Response(null === $d ? 'yes' : 'no');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Tests\Fixtures\FooBundle\Controller;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @Route(service="test.simple.multiple")
|
||||
*/
|
||||
class SimpleController
|
||||
{
|
||||
/**
|
||||
* @Route("/simple/multiple/", defaults={"a": "a", "b": "b"})
|
||||
* @Template()
|
||||
*/
|
||||
public function someAction($a, $b, $c = 'c')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/simple/multiple/{a}/{b}/")
|
||||
* @Template("FooBundle:Simple:some.html.twig")
|
||||
*/
|
||||
public function someMoreAction($a, $b, $c = 'c')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/simple/multiple-with-vars/", defaults={"a": "a", "b": "b"})
|
||||
* @Template(vars={"a", "b"})
|
||||
*/
|
||||
public function anotherAction($a, $b, $c = 'c')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/no-listener/")
|
||||
*/
|
||||
public function noListenerAction()
|
||||
{
|
||||
return new Response('<html><body>I did not get rendered via twig</body></html>');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/streamed/")
|
||||
* @Template(isStreamable=true)
|
||||
*/
|
||||
public function streamedAction()
|
||||
{
|
||||
return array(
|
||||
'foo' => 'foo',
|
||||
'bar' => 'bar',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Fixtures\FooBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Mapping\Entity
|
||||
*/
|
||||
class Foo
|
||||
{
|
||||
/**
|
||||
* @Mapping\Column(type="integer")
|
||||
* @Mapping\Id
|
||||
* @Mapping\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Tests\Fixtures\FooBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class FooBundle extends Bundle
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<html><body>{{ foo }}</body></html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<html><body>{{ variable }},{{ another_variable }}</body></html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<html><body>{{ variable }}</body></html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<html><body>{{ a }}, {{ b }}</body></html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<html><body>{{ a }}, {{ b }}, {{ c }}</body></html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<html><body>{{ foo }}, {{ bar }}</body></html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<html><body>{{ foo }}</body></html>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
/**
|
||||
* Used for functional tests.
|
||||
*/
|
||||
class TestKernel extends Kernel
|
||||
{
|
||||
public function registerBundles()
|
||||
{
|
||||
return array(
|
||||
new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
|
||||
new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
|
||||
new \Symfony\Bundle\TwigBundle\TwigBundle(),
|
||||
new \Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
|
||||
new \Tests\Fixtures\FooBundle\FooBundle(),
|
||||
new \Tests\Fixtures\ActionArgumentsBundle\ActionArgumentsBundle(),
|
||||
);
|
||||
}
|
||||
|
||||
public function registerContainerConfiguration(LoaderInterface $loader)
|
||||
{
|
||||
$loader->load(__DIR__.'/config/config.yml');
|
||||
|
||||
if (PHP_VERSION_ID >= 70100) {
|
||||
$loader->load(__DIR__.'/config/nullable_type/config.yml');
|
||||
}
|
||||
}
|
||||
|
||||
public function getCacheDir()
|
||||
{
|
||||
return $this->rootDir.'/cache/'.$this->environment;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Tests\Fixtures\TestKernel', 'TestKernel');
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
framework:
|
||||
test: true
|
||||
secret: test
|
||||
templating:
|
||||
engine: [twig, php]
|
||||
router:
|
||||
resource: "%kernel.root_dir%/config/routing.yml"
|
||||
doctrine:
|
||||
dbal:
|
||||
driver: pdo_sqlite
|
||||
path: "%kernel.root_dir%/data/db.sqlite"
|
||||
|
||||
orm:
|
||||
auto_mapping: true
|
||||
|
||||
services:
|
||||
test.invokable.predefined:
|
||||
class: Tests\Fixtures\FooBundle\Controller\InvokableController
|
||||
|
||||
test.invokable_class_level.predefined:
|
||||
class: Tests\Fixtures\FooBundle\Controller\InvokableClassLevelController
|
||||
|
||||
test.simple.multiple:
|
||||
class: Tests\Fixtures\FooBundle\Controller\SimpleController
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
framework:
|
||||
router:
|
||||
resource: '%kernel.root_dir%/config/nullable_type/routing.yml'
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
foo_bundle:
|
||||
resource: "@FooBundle/Controller"
|
||||
type: annotation
|
||||
|
||||
action_arguments_bundle:
|
||||
resource: "@ActionArgumentsBundle/Controller"
|
||||
type: annotation
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
foo_bundle:
|
||||
resource: "@FooBundle/Controller"
|
||||
type: annotation
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
/**
|
||||
* @requires PHP 7.1
|
||||
*/
|
||||
class NullableAnnotationTest extends WebTestCase
|
||||
{
|
||||
public function testMissingRequiredArgumentWillResultWithError()
|
||||
{
|
||||
$client = self::createClient();
|
||||
$client->request('GET', '/nullable-arguments/without-default');
|
||||
|
||||
$this->assertSame(500, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
public function testArgumentWithDefaultIsOptional()
|
||||
{
|
||||
$client = self::createClient();
|
||||
$crawler = $client->request('GET', '/nullable-arguments/with-default');
|
||||
|
||||
$this->assertSame('yes', $crawler->text());
|
||||
}
|
||||
|
||||
public function testNullableArgumentIsOptional()
|
||||
{
|
||||
$client = self::createClient();
|
||||
$crawler = $client->request('GET', '/nullable-arguments/nullable');
|
||||
|
||||
$this->assertSame('yes', $crawler->text());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
class OptionalArgumentsTest extends WebTestCase
|
||||
{
|
||||
public function testArgumentWithDefaultFollowedByMandatoryIsOptional()
|
||||
{
|
||||
$client = self::createClient();
|
||||
$crawler = $client->request('GET', '/optional-arguments/with-default-followed-by-mandatory');
|
||||
|
||||
$this->assertSame('yes', $crawler->text());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
class TemplateAnnotationTest extends WebTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider urlProvider
|
||||
*/
|
||||
public function testController($url, $checkHtml)
|
||||
{
|
||||
$client = self::createClient();
|
||||
$crawler = $client->request('GET', $url);
|
||||
|
||||
$this->assertEquals($checkHtml, $crawler->filterXPath('//body')->html());
|
||||
}
|
||||
|
||||
public static function urlProvider()
|
||||
{
|
||||
return array(
|
||||
array('/multi/one-template/1/', 'bar'),
|
||||
array('/multi/one-template/2/', 'bar'),
|
||||
array('/multi/one-template/3/', 'bar'),
|
||||
array('/multi/one-template/4/', 'foo bar baz'),
|
||||
array('/invokable/predefined/service/', 'bar'),
|
||||
array('/invokable/class-level/service/', 'bar'),
|
||||
array('/simple/multiple/', 'a, b, c'),
|
||||
array('/simple/multiple/henk/bar/', 'henk, bar, c'),
|
||||
array('/simple/multiple-with-vars/', 'a, b'),
|
||||
array('/invokable/predefined/container/', 'bar'),
|
||||
array('/invokable/variable/container/the-var/', 'the-var'),
|
||||
array('/invokable/another-variable/container/another-var/', 'another-var'),
|
||||
array('/invokable/variable/container/the-var/another-var/', 'the-var,another-var'),
|
||||
array('/no-listener/', 'I did not get rendered via twig'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testStreamedControllerResponse()
|
||||
{
|
||||
$uri = '/streamed/';
|
||||
|
||||
ob_start();
|
||||
$client = self::createClient();
|
||||
$client->request('GET', $uri);
|
||||
|
||||
$crawler = new Crawler(null, $uri);
|
||||
$crawler->addContent(ob_get_clean());
|
||||
|
||||
$this->assertEquals('foo, bar', $crawler->filterXPath('//body')->html());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
<?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\Tests\Request\ParamConverter;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DateTimeParamConverter;
|
||||
|
||||
class DateTimeParamConverterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $converter;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->converter = new DateTimeParamConverter();
|
||||
}
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$config = $this->createConfiguration('DateTime');
|
||||
$this->assertTrue($this->converter->supports($config));
|
||||
|
||||
$config = $this->createConfiguration(__CLASS__);
|
||||
$this->assertFalse($this->converter->supports($config));
|
||||
|
||||
$config = $this->createConfiguration();
|
||||
$this->assertFalse($this->converter->supports($config));
|
||||
}
|
||||
|
||||
public function testApply()
|
||||
{
|
||||
$request = new Request(array(), array(), array('start' => '2012-07-21 00:00:00'));
|
||||
$config = $this->createConfiguration('DateTime', 'start');
|
||||
|
||||
$this->converter->apply($request, $config);
|
||||
|
||||
$this->assertInstanceOf('DateTime', $request->attributes->get('start'));
|
||||
$this->assertEquals('2012-07-21', $request->attributes->get('start')->format('Y-m-d'));
|
||||
}
|
||||
|
||||
public function testApplyInvalidDate404Exception()
|
||||
{
|
||||
$request = new Request(array(), array(), array('start' => 'Invalid DateTime Format'));
|
||||
$config = $this->createConfiguration('DateTime', 'start');
|
||||
|
||||
$this->setExpectedException('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', 'Invalid date given for parameter "start".');
|
||||
$this->converter->apply($request, $config);
|
||||
}
|
||||
|
||||
public function testApplyWithFormatInvalidDate404Exception()
|
||||
{
|
||||
$request = new Request(array(), array(), array('start' => '2012-07-21'));
|
||||
$config = $this->createConfiguration('DateTime', 'start');
|
||||
$config->expects($this->any())->method('getOptions')->will($this->returnValue(array('format' => 'd.m.Y')));
|
||||
|
||||
$this->setExpectedException('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', 'Invalid date given for parameter "start".');
|
||||
$this->converter->apply($request, $config);
|
||||
}
|
||||
|
||||
public function testApplyOptionalWithEmptyAttribute()
|
||||
{
|
||||
$request = new Request(array(), array(), array('start' => null));
|
||||
$config = $this->createConfiguration('DateTime', 'start');
|
||||
$config->expects($this->once())
|
||||
->method('isOptional')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$this->assertFalse($this->converter->apply($request, $config));
|
||||
$this->assertNull($request->attributes->get('start'));
|
||||
}
|
||||
|
||||
public function createConfiguration($class = null, $name = null)
|
||||
{
|
||||
$config = $this
|
||||
->getMockBuilder('Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter')
|
||||
->setMethods(array('getClass', 'getAliasName', 'getOptions', 'getName', 'allowArray', 'isOptional'))
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
if ($name !== null) {
|
||||
$config->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($name));
|
||||
}
|
||||
if ($class !== null) {
|
||||
$config->expects($this->any())
|
||||
->method('getClass')
|
||||
->will($this->returnValue($class));
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,468 @@
|
|||
<?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\Tests\Request\ParamConverter;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter;
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
|
||||
class DoctrineParamConverterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
private $registry;
|
||||
|
||||
/**
|
||||
* @var DoctrineParamConverter
|
||||
*/
|
||||
private $converter;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock();
|
||||
$this->converter = new DoctrineParamConverter($this->registry);
|
||||
}
|
||||
|
||||
public function createConfiguration($class = null, array $options = null, $name = 'arg', $isOptional = false)
|
||||
{
|
||||
$methods = array('getClass', 'getAliasName', 'getOptions', 'getName', 'allowArray');
|
||||
if (null !== $isOptional) {
|
||||
$methods[] = 'isOptional';
|
||||
}
|
||||
$config = $this
|
||||
->getMockBuilder('Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter')
|
||||
->setMethods($methods)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
if ($options !== null) {
|
||||
$config->expects($this->once())
|
||||
->method('getOptions')
|
||||
->will($this->returnValue($options));
|
||||
}
|
||||
if ($class !== null) {
|
||||
$config->expects($this->any())
|
||||
->method('getClass')
|
||||
->will($this->returnValue($class));
|
||||
}
|
||||
$config->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($name));
|
||||
if (null !== $isOptional) {
|
||||
$config->expects($this->any())
|
||||
->method('isOptional')
|
||||
->will($this->returnValue($isOptional));
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function testApplyWithNoIdAndData()
|
||||
{
|
||||
$request = new Request();
|
||||
$config = $this->createConfiguration(null, array());
|
||||
$objectManager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
|
||||
$this->setExpectedException('LogicException');
|
||||
$this->converter->apply($request, $config);
|
||||
}
|
||||
|
||||
public function testApplyWithNoIdAndDataOptional()
|
||||
{
|
||||
$request = new Request();
|
||||
$config = $this->createConfiguration(null, array(), 'arg', true);
|
||||
$objectManager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
|
||||
$ret = $this->converter->apply($request, $config);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
$this->assertNull($request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
public function testApplyWithStripNulls()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('arg', null);
|
||||
$config = $this->createConfiguration('stdClass', array('mapping' => array('arg' => 'arg'), 'strip_null' => true), 'arg', true);
|
||||
|
||||
$classMetadata = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')->getMock();
|
||||
$manager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$manager->expects($this->once())
|
||||
->method('getClassMetadata')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($classMetadata));
|
||||
|
||||
$manager->expects($this->never())
|
||||
->method('getRepository');
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($manager));
|
||||
|
||||
$classMetadata->expects($this->once())
|
||||
->method('hasField')
|
||||
->with($this->equalTo('arg'))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$this->converter->apply($request, $config);
|
||||
|
||||
$this->assertNull($request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider idsProvider
|
||||
*/
|
||||
public function testApplyWithId($id)
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('id', $id);
|
||||
|
||||
$config = $this->createConfiguration('stdClass', array('id' => 'id'), 'arg');
|
||||
|
||||
$manager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$objectRepository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository')->getMock();
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($manager));
|
||||
|
||||
$manager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($objectRepository));
|
||||
|
||||
$objectRepository->expects($this->once())
|
||||
->method('find')
|
||||
->with($this->equalTo($id))
|
||||
->will($this->returnValue($object = new \stdClass()));
|
||||
|
||||
$ret = $this->converter->apply($request, $config);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
$this->assertSame($object, $request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
public function testUsedProperIdentifier()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('id', 1);
|
||||
$request->attributes->set('entity_id', null);
|
||||
$request->attributes->set('arg', null);
|
||||
|
||||
$config = $this->createConfiguration('stdClass', array('id' => 'entity_id'), 'arg', null);
|
||||
|
||||
$ret = $this->converter->apply($request, $config);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
$this->assertNull($request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
public function idsProvider()
|
||||
{
|
||||
return array(
|
||||
array(1),
|
||||
array(0),
|
||||
array('foo'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testApplyGuessOptional()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('arg', null);
|
||||
|
||||
$config = $this->createConfiguration('stdClass', array(), 'arg', null);
|
||||
|
||||
$classMetadata = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')->getMock();
|
||||
$manager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$manager->expects($this->once())
|
||||
->method('getClassMetadata')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($classMetadata));
|
||||
|
||||
$objectRepository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository')->getMock();
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($manager));
|
||||
|
||||
$manager->expects($this->never())->method('getRepository');
|
||||
|
||||
$objectRepository->expects($this->never())->method('find');
|
||||
$objectRepository->expects($this->never())->method('findOneBy');
|
||||
|
||||
$ret = $this->converter->apply($request, $config);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
$this->assertNull($request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
public function testApplyWithMappingAndExclude()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('foo', 1);
|
||||
$request->attributes->set('bar', 2);
|
||||
|
||||
$config = $this->createConfiguration(
|
||||
'stdClass',
|
||||
array('mapping' => array('foo' => 'Foo'), 'exclude' => array('bar')),
|
||||
'arg'
|
||||
);
|
||||
|
||||
$manager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$metadata = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')->getMock();
|
||||
$repository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository')->getMock();
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($manager));
|
||||
|
||||
$manager->expects($this->once())
|
||||
->method('getClassMetadata')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($metadata));
|
||||
$manager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($repository));
|
||||
|
||||
$metadata->expects($this->once())
|
||||
->method('hasField')
|
||||
->with($this->equalTo('Foo'))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$repository->expects($this->once())
|
||||
->method('findOneBy')
|
||||
->with($this->equalTo(array('Foo' => 1)))
|
||||
->will($this->returnValue($object = new \stdClass()));
|
||||
|
||||
$ret = $this->converter->apply($request, $config);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
$this->assertSame($object, $request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
public function testApplyWithRepositoryMethod()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('id', 1);
|
||||
|
||||
$config = $this->createConfiguration(
|
||||
'stdClass',
|
||||
array('repository_method' => 'getClassName'),
|
||||
'arg'
|
||||
);
|
||||
|
||||
$objectRepository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository')->getMock();
|
||||
$manager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$manager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($objectRepository));
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->will($this->returnValue($manager));
|
||||
|
||||
$objectRepository->expects($this->once())
|
||||
->method('getClassName')
|
||||
->will($this->returnValue($className = 'ObjectRepository'));
|
||||
|
||||
$ret = $this->converter->apply($request, $config);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
$this->assertSame($className, $request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
public function testApplyWithRepositoryMethodAndMapping()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('id', 1);
|
||||
|
||||
$config = $this->createConfiguration(
|
||||
'stdClass',
|
||||
array('repository_method' => 'getClassName', 'mapping' => array('foo' => 'Foo')),
|
||||
'arg'
|
||||
);
|
||||
|
||||
$objectManager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$objectRepository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository')->getMock();
|
||||
$metadata = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')->getMock();
|
||||
|
||||
$objectManager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($objectRepository));
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->will($this->returnValue($objectManager));
|
||||
|
||||
$metadata->expects($this->once())
|
||||
->method('hasField')
|
||||
->with($this->equalTo('Foo'))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$objectManager->expects($this->once())
|
||||
->method('getClassMetadata')
|
||||
->will($this->returnValue($metadata));
|
||||
$objectManager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($objectRepository));
|
||||
|
||||
$objectRepository->expects($this->once())
|
||||
->method('getClassName')
|
||||
->will($this->returnValue($className = 'ObjectRepository'));
|
||||
|
||||
$ret = $this->converter->apply($request, $config);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
$this->assertSame($className, $request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
public function testApplyWithRepositoryMethodAndMapMethodSignature()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('first_name', 'Fabien');
|
||||
$request->attributes->set('last_name', 'Potencier');
|
||||
|
||||
$config = $this->createConfiguration(
|
||||
'stdClass',
|
||||
array(
|
||||
'repository_method' => 'findByFullName',
|
||||
'mapping' => array('first_name' => 'firstName', 'last_name' => 'lastName'),
|
||||
'map_method_signature' => true,
|
||||
),
|
||||
'arg'
|
||||
);
|
||||
|
||||
$objectManager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$objectRepository = new TestUserRepository();
|
||||
$metadata = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')->getMock();
|
||||
|
||||
$objectManager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($objectRepository));
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->will($this->returnValue($objectManager));
|
||||
|
||||
$objectManager->expects($this->once())
|
||||
->method('getClassMetadata')
|
||||
->will($this->returnValue($metadata));
|
||||
|
||||
$ret = $this->converter->apply($request, $config);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
$this->assertSame('Fabien Potencier', $request->attributes->get('arg'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Repository method "Sensio\Bundle\FrameworkExtraBundle\Tests\Request\ParamConverter\TestUserRepository::findByFullName" requires that you provide a value for the "$lastName" argument.
|
||||
*/
|
||||
public function testApplyWithRepositoryMethodAndMapMethodSignatureException()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('first_name', 'Fabien');
|
||||
$request->attributes->set('last_name', 'Potencier');
|
||||
|
||||
$config = $this->createConfiguration(
|
||||
'stdClass',
|
||||
array(
|
||||
'repository_method' => 'findByFullName',
|
||||
'mapping' => array('first_name' => 'firstName', 'last_name' => 'lastNameXxx'),
|
||||
'map_method_signature' => true,
|
||||
),
|
||||
'arg'
|
||||
);
|
||||
|
||||
$objectManager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$objectRepository = new TestUserRepository();
|
||||
$metadata = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')->getMock();
|
||||
|
||||
$objectManager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($objectRepository));
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->will($this->returnValue($objectManager));
|
||||
|
||||
$objectManager->expects($this->once())
|
||||
->method('getClassMetadata')
|
||||
->will($this->returnValue($metadata));
|
||||
|
||||
$this->converter->apply($request, $config);
|
||||
}
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$config = $this->createConfiguration('stdClass', array());
|
||||
$metadataFactory = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadataFactory')->getMock();
|
||||
$metadataFactory->expects($this->once())
|
||||
->method('isTransient')
|
||||
->with($this->equalTo('stdClass'))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$objectManager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$objectManager->expects($this->once())
|
||||
->method('getMetadataFactory')
|
||||
->will($this->returnValue($metadataFactory));
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagers')
|
||||
->will($this->returnValue(array($objectManager)));
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagerForClass')
|
||||
->with('stdClass')
|
||||
->will($this->returnValue($objectManager));
|
||||
|
||||
$ret = $this->converter->supports($config);
|
||||
|
||||
$this->assertTrue($ret, 'Should be supported');
|
||||
}
|
||||
|
||||
public function testSupportsWithConfiguredEntityManager()
|
||||
{
|
||||
$config = $this->createConfiguration('stdClass', array('entity_manager' => 'foo'));
|
||||
$metadataFactory = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadataFactory')->getMock();
|
||||
$metadataFactory->expects($this->once())
|
||||
->method('isTransient')
|
||||
->with($this->equalTo('stdClass'))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$objectManager = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
|
||||
$objectManager->expects($this->once())
|
||||
->method('getMetadataFactory')
|
||||
->will($this->returnValue($metadataFactory));
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManagers')
|
||||
->will($this->returnValue(array($objectManager)));
|
||||
|
||||
$this->registry->expects($this->once())
|
||||
->method('getManager')
|
||||
->with('foo')
|
||||
->will($this->returnValue($objectManager));
|
||||
|
||||
$ret = $this->converter->supports($config);
|
||||
|
||||
$this->assertTrue($ret, 'Should be supported');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
<?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\Tests\Request\ParamConverter;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class ParamConverterManagerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testPriorities()
|
||||
{
|
||||
$manager = new ParamConverterManager();
|
||||
$this->assertEquals(array(), $manager->all());
|
||||
|
||||
$high = $this->createParamConverterMock();
|
||||
$low = $this->createParamConverterMock();
|
||||
|
||||
$manager->add($low);
|
||||
$manager->add($high, 10);
|
||||
|
||||
$this->assertEquals(array($high, $low), $manager->all());
|
||||
}
|
||||
|
||||
public function testApply()
|
||||
{
|
||||
$supported = $this->createParamConverterMock();
|
||||
$supported
|
||||
->expects($this->once())
|
||||
->method('supports')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
$supported
|
||||
->expects($this->once())
|
||||
->method('apply')
|
||||
->will($this->returnValue(false))
|
||||
;
|
||||
|
||||
$invalid = $this->createParamConverterMock();
|
||||
$invalid
|
||||
->expects($this->once())
|
||||
->method('supports')
|
||||
->will($this->returnValue(false))
|
||||
;
|
||||
$invalid
|
||||
->expects($this->never())
|
||||
->method('apply')
|
||||
;
|
||||
|
||||
$configurations = array(
|
||||
new Configuration\ParamConverter(array(
|
||||
'name' => 'var',
|
||||
)),
|
||||
);
|
||||
|
||||
$manager = new ParamConverterManager();
|
||||
$manager->add($supported);
|
||||
$manager->add($invalid);
|
||||
$manager->apply(new Request(), $configurations);
|
||||
}
|
||||
|
||||
public function testApplyNamedConverter()
|
||||
{
|
||||
$converter = $this->createParamConverterMock();
|
||||
$converter
|
||||
->expects($this->any())
|
||||
->method('supports')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$converter
|
||||
->expects($this->any())
|
||||
->method('apply')
|
||||
;
|
||||
|
||||
$request = new Request();
|
||||
$request->attributes->set('param', '1234');
|
||||
|
||||
$configuration = new Configuration\ParamConverter(array(
|
||||
'name' => 'param',
|
||||
'class' => 'stdClass',
|
||||
'converter' => 'test',
|
||||
));
|
||||
|
||||
$manager = new ParamConverterManager();
|
||||
$manager->add($converter, 0, 'test');
|
||||
$manager->apply($request, array($configuration));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage Converter 'test' does not support conversion of parameter 'param'.
|
||||
*/
|
||||
public function testApplyNamedConverterNotSupportsParameter()
|
||||
{
|
||||
$converter = $this->createParamConverterMock();
|
||||
$converter
|
||||
->expects($this->any())
|
||||
->method('supports')
|
||||
->will($this->returnValue(false))
|
||||
;
|
||||
|
||||
$request = new Request();
|
||||
$request->attributes->set('param', '1234');
|
||||
|
||||
$configuration = new Configuration\ParamConverter(array(
|
||||
'name' => 'param',
|
||||
'class' => 'stdClass',
|
||||
'converter' => 'test',
|
||||
));
|
||||
|
||||
$manager = new ParamConverterManager();
|
||||
$manager->add($converter, 0, 'test');
|
||||
$manager->apply($request, array($configuration));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage No converter named 'test' found for conversion of parameter 'param'.
|
||||
*/
|
||||
public function testApplyNamedConverterNoConverter()
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('param', '1234');
|
||||
|
||||
$configuration = new Configuration\ParamConverter(array(
|
||||
'name' => 'param',
|
||||
'class' => 'stdClass',
|
||||
'converter' => 'test',
|
||||
));
|
||||
|
||||
$manager = new ParamConverterManager();
|
||||
$manager->apply($request, array($configuration));
|
||||
}
|
||||
|
||||
public function testApplyNotCalledOnAlreadyConvertedObjects()
|
||||
{
|
||||
$converter = $this->createParamConverterMock();
|
||||
$converter
|
||||
->expects($this->never())
|
||||
->method('supports')
|
||||
;
|
||||
|
||||
$converter
|
||||
->expects($this->never())
|
||||
->method('apply')
|
||||
;
|
||||
|
||||
$request = new Request();
|
||||
$request->attributes->set('converted', new \stdClass());
|
||||
|
||||
$configuration = new Configuration\ParamConverter(array(
|
||||
'name' => 'converted',
|
||||
'class' => 'stdClass',
|
||||
));
|
||||
|
||||
$manager = new ParamConverterManager();
|
||||
$manager->add($converter);
|
||||
$manager->apply($request, array($configuration));
|
||||
}
|
||||
|
||||
protected function createParamConverterMock()
|
||||
{
|
||||
return $this->getMockBuilder('Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface')->getMock();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Request\ParamConverter;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\PsrServerRequestParamConverter;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
* @requires PHP 5.4
|
||||
*/
|
||||
class PsrServerRequestParamConverterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSupports()
|
||||
{
|
||||
$converter = new PsrServerRequestParamConverter(new DiactorosFactory());
|
||||
$config = $this->createConfiguration('Psr\Http\Message\ServerRequestInterface');
|
||||
$this->assertTrue($converter->supports($config));
|
||||
|
||||
$config = $this->createConfiguration('Psr\Http\Message\RequestInterface');
|
||||
$this->assertTrue($converter->supports($config));
|
||||
|
||||
$config = $this->createConfiguration('Psr\Http\Message\MessageInterface');
|
||||
$this->assertTrue($converter->supports($config));
|
||||
|
||||
$config = $this->createConfiguration(__CLASS__);
|
||||
$this->assertFalse($converter->supports($config));
|
||||
|
||||
$config = $this->createConfiguration();
|
||||
$this->assertFalse($converter->supports($config));
|
||||
}
|
||||
|
||||
public function testApply()
|
||||
{
|
||||
$converter = new PsrServerRequestParamConverter(new DiactorosFactory());
|
||||
$request = new Request(
|
||||
array('foo' => 'bar'),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array('HTTP_HOST' => 'dunglas.fr')
|
||||
);
|
||||
$config = $this->createConfiguration('Psr\Http\Message\ServerRequestInterface', 'request');
|
||||
|
||||
$converter->apply($request, $config);
|
||||
|
||||
$this->assertInstanceOf('Psr\Http\Message\ServerRequestInterface', $request->attributes->get('request'));
|
||||
$this->assertEquals('bar', $request->query->get('foo'));
|
||||
}
|
||||
|
||||
private function createConfiguration($class = null, $name = null)
|
||||
{
|
||||
$config = $this
|
||||
->getMockBuilder('Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter')
|
||||
->setMethods(array('getClass', 'getAliasName', 'getOptions', 'getName', 'allowArray', 'isOptional'))
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
|
||||
if (null !== $name) {
|
||||
$config->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($name));
|
||||
}
|
||||
if (null !== $class) {
|
||||
$config->expects($this->any())
|
||||
->method('getClass')
|
||||
->will($this->returnValue($class));
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?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\Tests\Request\ParamConverter;
|
||||
|
||||
/*
|
||||
* This class is a helper Repository for DoctrineParamConverter's map_method_signature functionality
|
||||
*/
|
||||
class TestUserRepository
|
||||
{
|
||||
public function findByFullName($firstName, $lastName)
|
||||
{
|
||||
return $firstName.' '.$lastName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<?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\Tests\Routing;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader;
|
||||
|
||||
class AnnotatedRouteControllerLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testServiceOptionIsAllowedOnClass()
|
||||
{
|
||||
$route = $this->getMockBuilder('Symfony\Component\Routing\Route')
|
||||
->setMethods(array('setDefault'))
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$route
|
||||
->expects($this->once())
|
||||
->method('setDefault')
|
||||
->with('_controller', 'service:testServiceOptionIsAllowedOnClass')
|
||||
;
|
||||
|
||||
$annotation = new Route(array());
|
||||
$annotation->setService('service');
|
||||
|
||||
$reader = $this->getMockBuilder('Doctrine\Common\Annotations\Reader')
|
||||
->setMethods(array('getClassAnnotation', 'getMethodAnnotations'))
|
||||
->disableOriginalConstructor()
|
||||
->getMockForAbstractClass()
|
||||
;
|
||||
|
||||
$reader
|
||||
->expects($this->once())
|
||||
->method('getClassAnnotation')
|
||||
->will($this->returnValue($annotation))
|
||||
;
|
||||
|
||||
$reader
|
||||
->expects($this->once())
|
||||
->method('getMethodAnnotations')
|
||||
->will($this->returnValue(array()))
|
||||
;
|
||||
|
||||
$loader = $this->getMockBuilder('Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader')
|
||||
->setConstructorArgs(array($reader))
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$r = new \ReflectionMethod($loader, 'configureRoute');
|
||||
$r->setAccessible(true);
|
||||
|
||||
$r->invoke(
|
||||
$loader,
|
||||
$route,
|
||||
new \ReflectionClass($this),
|
||||
new \ReflectionMethod($this, 'testServiceOptionIsAllowedOnClass'),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage The service option can only be specified at class level.
|
||||
*/
|
||||
public function testServiceOptionIsNotAllowedOnMethod()
|
||||
{
|
||||
$route = $this->getMockBuilder('Symfony\Component\Routing\Route')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$reader = $this->getMockBuilder('Doctrine\Common\Annotations\Reader')
|
||||
->setMethods(array('getClassAnnotation', 'getMethodAnnotations'))
|
||||
->disableOriginalConstructor()
|
||||
->getMockForAbstractClass()
|
||||
;
|
||||
|
||||
$annotation = new Route(array());
|
||||
$annotation->setService('service');
|
||||
|
||||
$reader
|
||||
->expects($this->once())
|
||||
->method('getClassAnnotation')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$reader
|
||||
->expects($this->once())
|
||||
->method('getMethodAnnotations')
|
||||
->will($this->returnValue(array($annotation)))
|
||||
;
|
||||
|
||||
$loader = $this->getMockBuilder('Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader')
|
||||
->setConstructorArgs(array($reader))
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$r = new \ReflectionMethod($loader, 'configureRoute');
|
||||
$r->setAccessible(true);
|
||||
|
||||
$r->invoke(
|
||||
$loader,
|
||||
$route,
|
||||
new \ReflectionClass($this),
|
||||
new \ReflectionMethod($this, 'testServiceOptionIsNotAllowedOnMethod'),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public function testLoad()
|
||||
{
|
||||
$loader = new AnnotatedRouteControllerLoader(new AnnotationReader());
|
||||
AnnotationRegistry::registerLoader('class_exists');
|
||||
|
||||
$rc = $loader->load('Sensio\Bundle\FrameworkExtraBundle\Tests\Routing\Fixtures\FoobarController');
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $rc);
|
||||
$this->assertCount(2, $rc);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Routing\Route', $rc->get('index'));
|
||||
// depending on the Symfony version, it can return GET or an empty array (on 2.3)
|
||||
// which has the same behavior anyway
|
||||
$methods = $rc->get('index')->getMethods();
|
||||
$this->assertTrue(empty($methods) || array('GET') == $methods);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Routing\Route', $rc->get('new'));
|
||||
$this->assertEquals(array('POST'), $rc->get('new')->getMethods());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Routing\Fixtures;
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
||||
|
||||
/**
|
||||
* @Route("/base")
|
||||
* @Method("GET")
|
||||
*/
|
||||
class FoobarController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="index")
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/new", name="new")
|
||||
* @Method("POST")
|
||||
*/
|
||||
public function newAction()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Templating\Fixture\BarBundle\Controller;
|
||||
|
||||
class BarController
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Templating\Fixture\Controller;
|
||||
|
||||
class OutOfBundleController
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Templating\Fixture\FooBarBundle\Controller;
|
||||
|
||||
class FooBarController
|
||||
{
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue