Merge branch '2.3' into 2.5

* 2.3:
  added missing files
  [TwigBundle] added a test
  Indicate which file was being parsed if an exception is thrown while running translation:debug
  [ClassLoader] Cast $useIncludePath property to boolean
  [HttpFoundation] Minor spelling fix in PHPDocs
  improve error message for multiple documents
  [Session] remove invalid workaround in session regenerate
  [Kernel] ensure session is saved before sending response
  [Routing] serialize the compiled route to speed things up
  [Validator] Fixed Regex::getHtmlPattern() to work with complex and negated patterns
  [DependencyInjection] use inheritdoc for loaders
  [Config] fix filelocator with empty name
  [Form] fix form handling with unconventional request methods like OPTIONS
  CSRF warning docs on Request::enableHttpMethodParameterOverride()

Conflicts:
	src/Symfony/Component/Routing/Route.php
This commit is contained in:
Fabien Potencier 2014-11-03 04:54:42 +01:00
commit 14c417a391
40 changed files with 465 additions and 293 deletions

View File

@ -0,0 +1 @@
{% syntax error

View File

@ -70,4 +70,17 @@ class TwigExtractorTest extends \PHPUnit_Framework_TestCase
array('{{ "new key" | transchoice(domain="domain", count=1) }}', array('new key' => 'domain')),
);
}
/**
* @expectedException \Twig_Error
* @expectedExceptionMessage Unclosed "block" in "extractor/syntax_error.twig" at line 1
*/
public function testExtractSyntaxError()
{
$twig = new \Twig_Environment(new \Twig_Loader_Array(array()));
$twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface')));
$extractor = new TwigExtractor($twig);
$extractor->extract(__DIR__.'/../Fixtures', new MessageCatalogue('en'));
}
}

View File

@ -58,7 +58,13 @@ class TwigExtractor implements ExtractorInterface
$finder = new Finder();
$files = $finder->files()->name('*.twig')->sortByName()->in($directory);
foreach ($files as $file) {
$this->extractTemplate(file_get_contents($file->getPathname()), $catalogue);
try {
$this->extractTemplate(file_get_contents($file->getPathname()), $catalogue);
} catch (\Twig_Error $e) {
$e->setTemplateFile($file->getRelativePathname());
throw $e;
}
}
}

View File

@ -62,6 +62,10 @@
<argument type="service" id="service_container" />
</service>
<service id="session.save_listener" class="Symfony\Component\HttpKernel\EventListener\SaveSessionListener">
<tag name="kernel.event_subscriber" />
</service>
<!-- for BC -->
<service id="session.storage.filesystem" alias="session.storage.mock_file" />
</services>

View File

@ -107,7 +107,7 @@ class ClassLoader
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
$this->useIncludePath = (bool) $useIncludePath;
}
/**

View File

@ -74,7 +74,7 @@ class UniversalClassLoader
*/
public function useIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
$this->useIncludePath = (bool) $useIncludePath;
}
/**

View File

@ -31,18 +31,14 @@ class FileLocator implements FileLocatorInterface
}
/**
* Returns a full path for a given file name.
*
* @param mixed $name The file name to locate
* @param string $currentPath The current path
* @param bool $first Whether to return the first occurrence or an array of filenames
*
* @return string|array The full path to the file|An array of file paths
*
* @throws \InvalidArgumentException When file is not found
* {@inheritdoc}
*/
public function locate($name, $currentPath = null, $first = true)
{
if ('' == $name) {
throw new \InvalidArgumentException('An empty file name is not valid to be located.');
}
if ($this->isAbsolutePath($name)) {
if (!file_exists($name)) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $name));
@ -84,10 +80,10 @@ class FileLocator implements FileLocatorInterface
*/
private function isAbsolutePath($file)
{
if ($file[0] == '/' || $file[0] == '\\'
if ($file[0] === '/' || $file[0] === '\\'
|| (strlen($file) > 3 && ctype_alpha($file[0])
&& $file[1] == ':'
&& ($file[2] == '\\' || $file[2] == '/')
&& $file[1] === ':'
&& ($file[2] === '\\' || $file[2] === '/')
)
|| null !== parse_url($file, PHP_URL_SCHEME)
) {

View File

@ -19,11 +19,11 @@ interface FileLocatorInterface
/**
* Returns a full path for a given file name.
*
* @param mixed $name The file name to locate
* @param string $currentPath The current path
* @param bool $first Whether to return the first occurrence or an array of filenames
* @param string $name The file name to locate
* @param string|null $currentPath The current path
* @param bool $first Whether to return the first occurrence or an array of filenames
*
* @return string|array The full path to the file|An array of file paths
* @return string|array The full path to the file or an array of file paths
*
* @throws \InvalidArgumentException When file is not found
*/

View File

@ -34,14 +34,7 @@ class DelegatingLoader extends Loader
}
/**
* Loads a resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return mixed
*
* @throws FileLoaderLoadException if no loader is found.
* {@inheritdoc}
*/
public function load($resource, $type = null)
{

View File

@ -22,8 +22,14 @@ use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceExceptio
*/
abstract class FileLoader extends Loader
{
/**
* @var array
*/
protected static $loading = array();
/**
* @var FileLocatorInterface
*/
protected $locator;
private $currentDir;
@ -38,11 +44,21 @@ abstract class FileLoader extends Loader
$this->locator = $locator;
}
/**
* Sets the current directory.
*
* @param string $dir
*/
public function setCurrentDir($dir)
{
$this->currentDir = $dir;
}
/**
* Returns the file locator used by this loader.
*
* @return FileLocatorInterface
*/
public function getLocator()
{
return $this->locator;
@ -51,10 +67,10 @@ abstract class FileLoader extends Loader
/**
* Imports a resource.
*
* @param mixed $resource A Resource
* @param string $type The resource type
* @param bool $ignoreErrors Whether to ignore import errors or not
* @param string $sourceResource The original resource importing the new resource
* @param mixed $resource A Resource
* @param string|null $type The resource type or null if unknown
* @param bool $ignoreErrors Whether to ignore import errors or not
* @param string|null $sourceResource The original resource importing the new resource
*
* @return mixed
*

View File

@ -23,9 +23,7 @@ abstract class Loader implements LoaderInterface
protected $resolver;
/**
* Gets the loader resolver.
*
* @return LoaderResolverInterface A LoaderResolverInterface instance
* {@inheritdoc}
*/
public function getResolver()
{
@ -33,9 +31,7 @@ abstract class Loader implements LoaderInterface
}
/**
* Sets the loader resolver.
*
* @param LoaderResolverInterface $resolver A LoaderResolverInterface instance
* {@inheritdoc}
*/
public function setResolver(LoaderResolverInterface $resolver)
{
@ -45,8 +41,8 @@ abstract class Loader implements LoaderInterface
/**
* Imports a resource.
*
* @param mixed $resource A Resource
* @param string $type The resource type
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return mixed
*/
@ -58,12 +54,12 @@ abstract class Loader implements LoaderInterface
/**
* Finds a loader able to load an imported resource.
*
* @param mixed $resource A Resource
* @param string $type The resource type
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return LoaderInterface A LoaderInterface instance
*
* @throws FileLoaderLoadException if no loader is found
* @throws FileLoaderLoadException If no loader is found
*/
public function resolve($resource, $type = null)
{

View File

@ -21,18 +21,20 @@ interface LoaderInterface
/**
* Loads a resource.
*
* @param mixed $resource The resource
* @param string $type The resource type
* @param mixed $resource The resource
* @param string|null $type The resource type or null if unknown
*
* @throws \Exception If something went wrong
*/
public function load($resource, $type = null);
/**
* Returns true if this class supports the given resource.
* Returns whether this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return bool true if this class supports the given resource, false otherwise
* @return bool True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null);

View File

@ -39,12 +39,7 @@ class LoaderResolver implements LoaderResolverInterface
}
/**
* Returns a loader able to load the resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return LoaderInterface|false A LoaderInterface instance
* {@inheritdoc}
*/
public function resolve($resource, $type = null)
{

View File

@ -21,10 +21,10 @@ interface LoaderResolverInterface
/**
* Returns a loader able to load the resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
* @param mixed $resource A resource
* @param string|null $type The resource type or null if unknown
*
* @return LoaderInterface A LoaderInterface instance
* @return LoaderInterface|false The loader or false if none is able to load the resource
*/
public function resolve($resource, $type = null);
}

View File

@ -24,8 +24,8 @@ class DirectoryResource implements ResourceInterface, \Serializable
/**
* Constructor.
*
* @param string $resource The file path to the resource
* @param string $pattern A pattern to restrict monitored files
* @param string $resource The file path to the resource
* @param string|null $pattern A pattern to restrict monitored files
*/
public function __construct($resource, $pattern = null)
{
@ -34,9 +34,7 @@ class DirectoryResource implements ResourceInterface, \Serializable
}
/**
* Returns a string representation of the Resource.
*
* @return string A string representation of the Resource
* {@inheritdoc}
*/
public function __toString()
{
@ -44,26 +42,25 @@ class DirectoryResource implements ResourceInterface, \Serializable
}
/**
* Returns the resource tied to this Resource.
*
* @return mixed The resource
* {@inheritdoc}
*/
public function getResource()
{
return $this->resource;
}
/**
* Returns the pattern to restrict monitored files
*
* @return string|null
*/
public function getPattern()
{
return $this->pattern;
}
/**
* Returns true if the resource has not been updated since the given timestamp.
*
* @param int $timestamp The last time the resource was loaded
*
* @return bool true if the resource has not been updated, false otherwise
* {@inheritdoc}
*/
public function isFresh($timestamp)
{

View File

@ -20,6 +20,9 @@ namespace Symfony\Component\Config\Resource;
*/
class FileResource implements ResourceInterface, \Serializable
{
/**
* @var string|false
*/
private $resource;
/**
@ -33,9 +36,7 @@ class FileResource implements ResourceInterface, \Serializable
}
/**
* Returns a string representation of the Resource.
*
* @return string A string representation of the Resource
* {@inheritdoc}
*/
public function __toString()
{
@ -43,9 +44,7 @@ class FileResource implements ResourceInterface, \Serializable
}
/**
* Returns the resource tied to this Resource.
*
* @return mixed The resource
* {@inheritdoc}
*/
public function getResource()
{
@ -53,15 +52,11 @@ class FileResource implements ResourceInterface, \Serializable
}
/**
* Returns true if the resource has not been updated since the given timestamp.
*
* @param int $timestamp The last time the resource was loaded
*
* @return bool true if the resource has not been updated, false otherwise
* {@inheritdoc}
*/
public function isFresh($timestamp)
{
if (!file_exists($this->resource)) {
if (false === $this->resource || !file_exists($this->resource)) {
return false;
}

View File

@ -28,14 +28,14 @@ interface ResourceInterface
/**
* Returns true if the resource has not been updated since the given timestamp.
*
* @param int $timestamp The last time the resource was loaded
* @param int $timestamp The last time the resource was loaded
*
* @return bool true if the resource has not been updated, false otherwise
* @return bool True if the resource has not been updated, false otherwise
*/
public function isFresh($timestamp);
/**
* Returns the resource tied to this Resource.
* Returns the tied resource.
*
* @return mixed The resource
*/

View File

@ -87,6 +87,7 @@ class FileLocatorTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The file "foobar.xml" does not exist
*/
public function testLocateThrowsAnExceptionIfTheFileDoesNotExists()
{
@ -104,4 +105,15 @@ class FileLocatorTest extends \PHPUnit_Framework_TestCase
$loader->locate(__DIR__.'/Fixtures/foobar.xml', __DIR__);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage An empty file name is not valid to be located.
*/
public function testLocateEmpty()
{
$loader = new FileLocator(array(__DIR__.'/Fixtures'));
$loader->locate(null, __DIR__);
}
}

View File

@ -36,23 +36,15 @@ class ClosureLoader extends Loader
}
/**
* Loads a Closure.
*
* @param \Closure $closure The resource
* @param string $type The resource type
* {@inheritdoc}
*/
public function load($closure, $type = null)
public function load($resource, $type = null)
{
call_user_func($closure, $this->container);
call_user_func($resource, $this->container);
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{

View File

@ -27,8 +27,8 @@ abstract class FileLoader extends BaseFileLoader
/**
* Constructor.
*
* @param ContainerBuilder $container A ContainerBuilder instance
* @param FileLocatorInterface $locator A FileLocator instance
* @param ContainerBuilder $container A ContainerBuilder instance
* @param FileLocatorInterface $locator A FileLocator instance
*/
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator)
{

View File

@ -22,22 +22,17 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
class IniFileLoader extends FileLoader
{
/**
* Loads a resource.
*
* @param mixed $file The resource
* @param string $type The resource type
*
* @throws InvalidArgumentException When ini file is not valid
* {@inheritdoc}
*/
public function load($file, $type = null)
public function load($resource, $type = null)
{
$path = $this->locator->locate($file);
$path = $this->locator->locate($resource);
$this->container->addResource(new FileResource($path));
$result = parse_ini_file($path, true);
if (false === $result || array() === $result) {
throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $file));
throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $resource));
}
if (isset($result['parameters']) && is_array($result['parameters'])) {
@ -48,12 +43,7 @@ class IniFileLoader extends FileLoader
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{

View File

@ -24,18 +24,15 @@ use Symfony\Component\Config\Resource\FileResource;
class PhpFileLoader extends FileLoader
{
/**
* Loads a PHP file.
*
* @param mixed $file The resource
* @param string $type The resource type
* {@inheritdoc}
*/
public function load($file, $type = null)
public function load($resource, $type = null)
{
// the container and loader variables are exposed to the included file below
$container = $this->container;
$loader = $this;
$path = $this->locator->locate($file);
$path = $this->locator->locate($resource);
$this->setCurrentDir(dirname($path));
$this->container->addResource(new FileResource($path));
@ -43,12 +40,7 @@ class PhpFileLoader extends FileLoader
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{

View File

@ -32,14 +32,11 @@ class XmlFileLoader extends FileLoader
const NS = 'http://symfony.com/schema/dic/services';
/**
* Loads an XML file.
*
* @param mixed $file The resource
* @param string $type The resource type
* {@inheritdoc}
*/
public function load($file, $type = null)
public function load($resource, $type = null)
{
$path = $this->locator->locate($file);
$path = $this->locator->locate($resource);
$xml = $this->parseFileToDOM($path);
@ -62,12 +59,7 @@ class XmlFileLoader extends FileLoader
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{

View File

@ -33,14 +33,11 @@ class YamlFileLoader extends FileLoader
private $yamlParser;
/**
* Loads a Yaml file.
*
* @param mixed $file The resource
* @param string $type The resource type
* {@inheritdoc}
*/
public function load($file, $type = null)
public function load($resource, $type = null)
{
$path = $this->locator->locate($file);
$path = $this->locator->locate($resource);
$content = $this->loadFile($path);
@ -57,7 +54,7 @@ class YamlFileLoader extends FileLoader
// parameters
if (isset($content['parameters'])) {
if (!is_array($content['parameters'])) {
throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $file));
throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $resource));
}
foreach ($content['parameters'] as $key => $value) {
@ -69,16 +66,11 @@ class YamlFileLoader extends FileLoader
$this->loadFromExtensions($content);
// services
$this->parseDefinitions($content, $file);
$this->parseDefinitions($content, $resource);
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{

View File

@ -13,7 +13,6 @@ namespace Symfony\Component\Form\Extension\HttpFoundation\EventListener;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
@ -49,12 +48,19 @@ class BindRequestListener implements EventSubscriberInterface
$name = $form->getConfig()->getName();
$default = $form->getConfig()->getCompound() ? array() : null;
// Store the bound data in case of a post request
// For request methods that must not have a request body we fetch data
// from the query string. Otherwise we look for data in the request body.
switch ($request->getMethod()) {
case 'POST':
case 'PUT':
case 'DELETE':
case 'PATCH':
case 'GET':
case 'HEAD':
case 'TRACE':
$data = '' === $name
? $request->query->all()
: $request->query->get($name, $default);
break;
default:
if ('' === $name) {
// Form bound without name
$params = $request->request->all();
@ -71,19 +77,6 @@ class BindRequestListener implements EventSubscriberInterface
}
break;
case 'GET':
$data = '' === $name
? $request->query->all()
: $request->query->get($name, $default);
break;
default:
throw new LogicException(sprintf(
'The request method "%s" is not supported',
$request->getMethod()
));
}
$event->setData($data);

View File

@ -55,7 +55,9 @@ class HttpFoundationRequestHandler implements RequestHandlerInterface
return;
}
if ('GET' === $method) {
// For request methods that must not have a request body we fetch data
// from the query string. Otherwise we look for data in the request body.
if ('GET' === $method || 'HEAD' === $method || 'TRACE' === $method) {
if ('' === $name) {
$data = $request->query->all();
} else {

View File

@ -63,7 +63,9 @@ class NativeRequestHandler implements RequestHandlerInterface
return;
}
if ('GET' === $method) {
// For request methods that must not have a request body we fetch data
// from the query string. Otherwise we look for data in the request body.
if ('GET' === $method || 'HEAD' === $method || 'TRACE' === $method) {
if ('' === $name) {
$data = $_GET;
} else {

View File

@ -496,7 +496,7 @@ class Request
* Overrides the PHP global variables according to this request instance.
*
* It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
* $_FILES is never override, see rfc1867
* $_FILES is never overridden, see rfc1867
*
* @api
*/
@ -671,6 +671,9 @@ class Request
*
* Be warned that enabling this feature might lead to CSRF issues in your code.
* Check that you are using CSRF tokens when required.
* If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered
* and used to send a "PUT" or "DELETE" request via the _method request parameter.
* If these methods are not protected against CSRF, this presents a possible vulnerability.
*
* The HTTP method can only be overridden when the real HTTP method is POST.
*/
@ -1612,7 +1615,7 @@ class Request
/**
* Returns true if the request is a XMLHttpRequest.
*
* It works if your JavaScript library set an X-Requested-With HTTP header.
* It works if your JavaScript library sets an X-Requested-With HTTP header.
* It is known to work with common JavaScript frameworks:
* @link http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
*

View File

@ -206,23 +206,7 @@ class NativeSessionStorage implements SessionStorageInterface
$this->metadataBag->stampNew();
}
$ret = session_regenerate_id($destroy);
// workaround for https://bugs.php.net/bug.php?id=61470 as suggested by David Grudl
if ('files' === $this->getSaveHandler()->getSaveHandlerName()) {
session_write_close();
if (isset($_SESSION)) {
$backup = $_SESSION;
session_start();
$_SESSION = $backup;
} else {
session_start();
}
$this->loadSession();
}
return $ret;
return session_regenerate_id($destroy);
}
/**

View File

@ -88,6 +88,14 @@ interface SessionStorageInterface
* Note regenerate+destroy should not clear the session data in memory
* only delete the session data from persistent storage.
*
* Care: When regenerating the session ID no locking is involved in PHPs
* session design. See https://bugs.php.net/bug.php?id=61470 for a discussion.
* So you must make sure the regenerated session is saved BEFORE sending the
* headers with the new ID. Symfonys HttpKernel offers a listener for this.
* See Symfony\Component\HttpKernel\EventListener\SaveSessionListener.
* Otherwise session data could get lost again for concurrent requests with the
* new ID. One result could be that you get logged out after just logging in.
*
* @param bool $destroy Destroy session when regenerating?
* @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
* will leave the system settings unchanged, 0 sets the cookie

View File

@ -47,7 +47,7 @@ class FileLocator extends BaseFileLocator
*/
public function locate($file, $currentPath = null, $first = true)
{
if ('@' === $file[0]) {
if (isset($file[0]) && '@' === $file[0]) {
return $this->kernel->locateResource($file, $this->path, $first);
}

View File

@ -0,0 +1,67 @@
<?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 Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Saves the session, in case it is still open, before sending the response/headers.
*
* This ensures several things in case the developer did not save the session explicitly:
*
* * If a session save handler without locking is used, it ensures the data is available
* on the next request, e.g. after a redirect. PHPs auto-save at script end via
* session_register_shutdown is executed after fastcgi_finish_request. So in this case
* the data could be missing the next request because it might not be saved the moment
* the new request is processed.
* * A locking save handler (e.g. the native 'files') circumvents concurrency problems like
* the one above. But by saving the session before long-running things in the terminate event,
* we ensure the session is not blocked longer than needed.
* * When regenerating the session ID no locking is involved in PHPs session design. See
* https://bugs.php.net/bug.php?id=61470 for a discussion. So in this case, the session must
* be saved anyway before sending the headers with the new session ID. Otherwise session
* data could get lost again for concurrent requests with the new ID. One result could be
* that you get logged out after just logging in.
*
* This listener should be executed as one of the last listeners, so that previous listeners
* can still operate on the open session. This prevents the overhead of restarting it.
* Listeners after closing the session can still work with the session as usual because
* Symfonys session implementation starts the session on demand. So writing to it after
* it is saved will just restart it.
*
* @author Tobias Schultze <http://tobion.de>
*/
class SaveSessionListener implements EventSubscriberInterface
{
public function onKernelResponse(FilterResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$session = $event->getRequest()->getSession();
if ($session && $session->isStarted()) {
$session->save();
}
}
public static function getSubscribedEvents()
{
return array(
// low priority but higher than StreamedResponseListener
KernelEvents::RESPONSE => array(array('onKernelResponse', -1000)),
);
}
}

View File

@ -16,7 +16,7 @@ namespace Symfony\Component\Routing;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CompiledRoute
class CompiledRoute implements \Serializable
{
private $variables;
private $tokens;
@ -51,6 +51,39 @@ class CompiledRoute
$this->variables = $variables;
}
/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize(array(
'vars' => $this->variables,
'path_prefix' => $this->staticPrefix,
'path_regex' => $this->regex,
'path_tokens' => $this->tokens,
'path_vars' => $this->pathVariables,
'host_regex' => $this->hostRegex,
'host_tokens' => $this->hostTokens,
'host_vars' => $this->hostVariables,
));
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->variables = $data['vars'];
$this->staticPrefix = $data['path_prefix'];
$this->regex = $data['path_regex'];
$this->tokens = $data['path_tokens'];
$this->pathVariables = $data['path_vars'];
$this->hostRegex = $data['host_regex'];
$this->hostTokens = $data['host_tokens'];
$this->hostVariables = $data['host_vars'];
}
/**
* Returns the static prefix.
*

View File

@ -99,6 +99,9 @@ class Route implements \Serializable
$this->setCondition($condition);
}
/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize(array(
@ -110,12 +113,16 @@ class Route implements \Serializable
'schemes' => $this->schemes,
'methods' => $this->methods,
'condition' => $this->condition,
'compiled' => $this->compiled,
));
}
public function unserialize($data)
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$data = unserialize($data);
$data = unserialize($serialized);
$this->path = $data['path'];
$this->host = $data['host'];
$this->defaults = $data['defaults'];
@ -123,7 +130,13 @@ class Route implements \Serializable
$this->options = $data['options'];
$this->schemes = $data['schemes'];
$this->methods = $data['methods'];
<<<<<<< HEAD
$this->condition = $data['condition'];
=======
if (isset($data['compiled'])) {
$this->compiled = $data['compiled'];
}
>>>>>>> 2.3
}
/**

View File

@ -229,7 +229,7 @@ class RouteTest extends \PHPUnit_Framework_TestCase
public function testSerialize()
{
$route = new Route('/{foo}', array('foo' => 'default'), array('foo' => '\d+'));
$route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+'));
$serialized = serialize($route);
$unserialized = unserialize($serialized);
@ -237,4 +237,39 @@ class RouteTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($route, $unserialized);
$this->assertNotSame($route, $unserialized);
}
/**
* Tests that the compiled version is also serialized to prevent the overhead
* of compiling it again after unserialize.
*/
public function testSerializeWhenCompiled()
{
$route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+'));
$route->setHost('{locale}.example.net');
$route->compile();
$serialized = serialize($route);
$unserialized = unserialize($serialized);
$this->assertEquals($route, $unserialized);
$this->assertNotSame($route, $unserialized);
}
/**
* Tests that the serialized representation of a route in one symfony version
* also works in later symfony versions, i.e. the unserialized route is in the
* same state as another, semantically equivalent, route.
*/
public function testSerializedRepresentationKeepsWorking()
{
$serialized = 'C:31:"Symfony\Component\Routing\Route":933:{a:8:{s:4:"path";s:13:"/prefix/{foo}";s:4:"host";s:20:"{locale}.example.net";s:8:"defaults";a:1:{s:3:"foo";s:7:"default";}s:12:"requirements";a:1:{s:3:"foo";s:3:"\d+";}s:7:"options";a:1:{s:14:"compiler_class";s:39:"Symfony\Component\Routing\RouteCompiler";}s:7:"schemes";a:0:{}s:7:"methods";a:0:{}s:8:"compiled";C:39:"Symfony\Component\Routing\CompiledRoute":568:{a:8:{s:4:"vars";a:2:{i:0;s:6:"locale";i:1;s:3:"foo";}s:11:"path_prefix";s:7:"/prefix";s:10:"path_regex";s:30:"#^/prefix(?:/(?P<foo>\d+))?$#s";s:11:"path_tokens";a:2:{i:0;a:4:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:3:"foo";}i:1;a:2:{i:0;s:4:"text";i:1;s:7:"/prefix";}}s:9:"path_vars";a:1:{i:0;s:3:"foo";}s:10:"host_regex";s:38:"#^(?P<locale>[^\.]++)\.example\.net$#s";s:11:"host_tokens";a:2:{i:0;a:2:{i:0;s:4:"text";i:1;s:12:".example.net";}i:1;a:4:{i:0;s:8:"variable";i:1;s:0:"";i:2;s:7:"[^\.]++";i:3;s:6:"locale";}}s:9:"host_vars";a:1:{i:0;s:6:"locale";}}}}}';
$unserialized = unserialize($serialized);
$route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+'));
$route->setHost('{locale}.example.net');
$route->compile();
$this->assertEquals($route, $unserialized);
$this->assertNotSame($route, $unserialized);
}
}

View File

@ -45,7 +45,14 @@ class Regex extends Constraint
}
/**
* Returns htmlPattern if exists or pattern is convertible.
* Converts the htmlPattern to a suitable format for HTML5 pattern.
* Example: /^[a-z]+$/ would be converted to [a-z]+
* However, if options are specified, it cannot be converted
*
* Pattern is also ignored if match=false since the pattern should
* then be reversed before application.
*
* @link http://dev.w3.org/html5/spec/single-page.html#the-pattern-attribute
*
* @return string|null
*/
@ -58,40 +65,34 @@ class Regex extends Constraint
: $this->htmlPattern;
}
return $this->getNonDelimitedPattern();
}
/**
* Converts the htmlPattern to a suitable format for HTML5 pattern.
* Example: /^[a-z]+$/ would be converted to [a-z]+
* However, if options are specified, it cannot be converted
*
* Pattern is also ignored if match=false since the pattern should
* then be reversed before application.
*
* @todo reverse pattern in case match=false as per issue #5307
*
* @link http://dev.w3.org/html5/spec/single-page.html#the-pattern-attribute
*
* @return string|null
*/
private function getNonDelimitedPattern()
{
// If match = false, pattern should not be added to HTML5 validation
if (!$this->match) {
// Quit if delimiters not at very beginning/end (e.g. when options are passed)
if ($this->pattern[0] !== $this->pattern[strlen($this->pattern) - 1]) {
return;
}
if (preg_match('/^(.)(\^?)(.*?)(\$?)\1$/', $this->pattern, $matches)) {
$delimiter = $matches[1];
$start = empty($matches[2]) ? '.*' : '';
$pattern = $matches[3];
$end = empty($matches[4]) ? '.*' : '';
$delimiter = $this->pattern[0];
// Unescape the delimiter in pattern
$pattern = str_replace('\\'.$delimiter, $delimiter, $pattern);
// Unescape the delimiter
$pattern = str_replace('\\'.$delimiter, $delimiter, substr($this->pattern, 1, -1));
return $start.$pattern.$end;
// If the pattern is inverted, we can simply wrap it in
// ((?!pattern).)*
if (!$this->match) {
return '((?!'.$pattern.').)*';
}
// If the pattern contains an or statement, wrap the pattern in
// .*(pattern).* and quit. Otherwise we'd need to parse the pattern
if (false !== strpos($pattern, '|')) {
return '.*('.$pattern.').*';
}
// Trim leading ^, otherwise prepend .*
$pattern = '^' === $pattern[0] ? substr($pattern, 1) : '.*'.$pattern;
// Trim trailing $, otherwise append .*
$pattern = '$' === $pattern[strlen($pattern) - 1] ? substr($pattern, 0, -1) : $pattern.'.*';
return $pattern;
}
}

View File

@ -0,0 +1,87 @@
<?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 Constraints;
use Symfony\Component\Validator\Constraints\Regex;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RegexTest extends \PHPUnit_Framework_TestCase
{
public function testConstraintGetDefaultOption()
{
$constraint = new Regex('/^[0-9]+$/');
$this->assertSame('/^[0-9]+$/', $constraint->pattern);
}
public function provideHtmlPatterns()
{
return array(
// HTML5 wraps the pattern in ^(?:pattern)$
array('/^[0-9]+$/', '[0-9]+'),
array('/[0-9]+$/', '.*[0-9]+'),
array('/^[0-9]+/', '[0-9]+.*'),
array('/[0-9]+/', '.*[0-9]+.*'),
// We need a smart way to allow matching of patterns that contain
// ^ and $ at various sub-clauses of an or-clause
// .*(pattern).* seems to work correctly
array('/[0-9]$|[a-z]+/', '.*([0-9]$|[a-z]+).*'),
array('/[0-9]$|^[a-z]+/', '.*([0-9]$|^[a-z]+).*'),
array('/^[0-9]|[a-z]+$/', '.*(^[0-9]|[a-z]+$).*'),
// Unescape escaped delimiters
array('/^[0-9]+\/$/', '[0-9]+/'),
array('#^[0-9]+\#$#', '[0-9]+#'),
// Cannot be converted
array('/^[0-9]+$/i', null),
// Inverse matches are simple, just wrap in
// ((?!pattern).)*
array('/^[0-9]+$/', '((?!^[0-9]+$).)*', false),
array('/[0-9]+$/', '((?![0-9]+$).)*', false),
array('/^[0-9]+/', '((?!^[0-9]+).)*', false),
array('/[0-9]+/', '((?![0-9]+).)*', false),
array('/[0-9]$|[a-z]+/', '((?![0-9]$|[a-z]+).)*', false),
array('/[0-9]$|^[a-z]+/', '((?![0-9]$|^[a-z]+).)*', false),
array('/^[0-9]|[a-z]+$/', '((?!^[0-9]|[a-z]+$).)*', false),
array('/^[0-9]+\/$/', '((?!^[0-9]+/$).)*', false),
array('#^[0-9]+\#$#', '((?!^[0-9]+#$).)*', false),
array('/^[0-9]+$/i', null, false),
);
}
/**
* @dataProvider provideHtmlPatterns
*/
public function testGetHtmlPattern($pattern, $htmlPattern, $match = true)
{
$constraint = new Regex(array(
'pattern' => $pattern,
'match' => $match,
));
$this->assertSame($pattern, $constraint->pattern);
$this->assertSame($htmlPattern, $constraint->getHtmlPattern());
}
public function testGetCustomHtmlPattern()
{
$constraint = new Regex(array(
'pattern' => '((?![0-9]$|[a-z]+).)*',
'htmlPattern' => 'foobar',
));
$this->assertSame('((?![0-9]$|[a-z]+).)*', $constraint->pattern);
$this->assertSame('foobar', $constraint->getHtmlPattern());
}
}

View File

@ -94,70 +94,4 @@ class RegexValidatorTest extends AbstractConstraintValidatorTest
array('090foo'),
);
}
public function testConstraintGetDefaultOption()
{
$constraint = new Regex(array(
'pattern' => '/^[0-9]+$/',
));
$this->assertEquals('pattern', $constraint->getDefaultOption());
}
public function testHtmlPatternEscaping()
{
$constraint = new Regex(array(
'pattern' => '/^[0-9]+\/$/',
));
$this->assertEquals('[0-9]+/', $constraint->getHtmlPattern());
$constraint = new Regex(array(
'pattern' => '#^[0-9]+\#$#',
));
$this->assertEquals('[0-9]+#', $constraint->getHtmlPattern());
}
public function testHtmlPattern()
{
// Specified htmlPattern
$constraint = new Regex(array(
'pattern' => '/^[a-z]+$/i',
'htmlPattern' => '[a-zA-Z]+',
));
$this->assertEquals('[a-zA-Z]+', $constraint->getHtmlPattern());
// Disabled htmlPattern
$constraint = new Regex(array(
'pattern' => '/^[a-z]+$/i',
'htmlPattern' => false,
));
$this->assertNull($constraint->getHtmlPattern());
// Cannot be converted
$constraint = new Regex(array(
'pattern' => '/^[a-z]+$/i',
));
$this->assertNull($constraint->getHtmlPattern());
// Automatically converted
$constraint = new Regex(array(
'pattern' => '/^[a-z]+$/',
));
$this->assertEquals('[a-z]+', $constraint->getHtmlPattern());
// Automatically converted, adds .*
$constraint = new Regex(array(
'pattern' => '/[a-z]+/',
));
$this->assertEquals('.*[a-z]+.*', $constraint->getHtmlPattern());
// Dropped because of match=false
$constraint = new Regex(array(
'pattern' => '/[a-z]+/',
'match' => false,
));
$this->assertNull($constraint->getHtmlPattern());
}
}

View File

@ -228,6 +228,11 @@ class Parser
}
}
} else {
// multiple documents are not supported
if ('---' === $this->currentLine) {
throw new ParseException('Multiple documents are not supported.');
}
// 1-liner optionally followed by newline
$lineCount = count($this->lines);
if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) {

View File

@ -482,6 +482,27 @@ EOF;
$this->parser->parse($yaml);
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage Multiple documents are not supported.
*/
public function testMultipleDocumentsNotSupportedException()
{
Yaml::parse(<<<EOL
# Ranking of 1998 home runs
---
- Mark McGwire
- Sammy Sosa
- Ken Griffey
# Team ranking
---
- Chicago Cubs
- St Louis Cardinals
EOL
);
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/