Merge branch '3.4'

* 3.4: (23 commits)
  Don't display the Symfony debug toolbar when printing the page
  [Routing] also add matched params for redirect due to trailing slash
  do not wire namespaces for the ArrayAdapter
  check _controller attribute is a string before parsing it
  [Cache] Added test for ApcuAdapter when using in CLI
  [Validator] sync upgrade file with latest code changes
  allow to configure custom formats in XML configs
  [HttpKernel] fix DumpDataCollector tests
  [FrameworkBundle] fix changelog
  [WebProfilerBundle] Cleanup profiler leftover
  [Routing] Add matched and default parameters to redirect responses
  [DotEnv] Fix variable substitution
  require the XML PHP extension
  Fix phpdoc for serializer normalizers exceptions
  Fixed absolute url generation for query strings and hash urls
  bumped Symfony version to 2.8.25
  updated VERSION for 2.8.24
  updated CHANGELOG for 2.8.24
  bumped Symfony version to 2.7.32
  [Filesystem] Dont copy perms when origin is remote
  ...
This commit is contained in:
Nicolas Grekas 2017-07-11 09:22:40 +02:00
commit 22914846f2
39 changed files with 338 additions and 152 deletions

View File

@ -70,9 +70,9 @@ Symfony is the result of the work of many people who made the code better
- Deni
- Henrik Westphal (snc)
- Dariusz Górecki (canni)
- Jáchym Toušek (enumag)
- Titouan Galopin (tgalopin)
- Douglas Greenshields (shieldo)
- Jáchym Toušek (enumag)
- Konstantin Myakshin (koc)
- Lee McDermott
- Brandon Turner
@ -99,11 +99,12 @@ Symfony is the result of the work of many people who made the code better
- Baptiste Clavié (talus)
- Vladimir Reznichenko (kalessil)
- marc.weistroff
- Yonel Ceruto González (yonelceruto)
- lenar
- Włodzimierz Gajda (gajdaw)
- Yonel Ceruto González (yonelceruto)
- Alexander Schwenn (xelaris)
- Jacob Dreesen (jdreesen)
- Tobias Nyholm (tobias)
- Florian Voutzinos (florianv)
- Colin Frei
- Adrien Brault (adrienbrault)
@ -111,7 +112,6 @@ Symfony is the result of the work of many people who made the code better
- Peter Kokot (maastermedia)
- David Buchmann (dbu)
- excelwebzone
- Tobias Nyholm (tobias)
- Tomáš Votruba (tomas_votruba)
- Fabien Pennequin (fabienpennequin)
- Gordon Franke (gimler)
@ -133,12 +133,15 @@ Symfony is the result of the work of many people who made the code better
- Guilherme Blanco (guilhermeblanco)
- Pablo Godel (pgodel)
- Jérémie Augustin (jaugustin)
- Dany Maillard (maidmaid)
- Andréia Bohner (andreia)
- Rafael Dohms (rdohms)
- Arnaud Kleinpeter (nanocom)
- jwdeitch
- David Maicher (dmaicher)
- Mikael Pajunen
- Joel Wurtz (brouznouf)
- Grégoire Paris (greg0ire)
- Philipp Wahala (hifi)
- Vyacheslav Pavlov
- Richard van Laak (rvanlaak)
@ -147,11 +150,9 @@ Symfony is the result of the work of many people who made the code better
- Thomas Rabaix (rande)
- Rouven Weßling (realityking)
- Teoh Han Hui (teohhanhui)
- David Maicher (dmaicher)
- Jérôme Vasseur (jvasseur)
- Clemens Tolboom
- Helmer Aaviksoo
- Grégoire Paris (greg0ire)
- Hiromi Hishida (77web)
- Matthieu Ouellette-Vachon (maoueh)
- Michał Pipa (michal.pipa)
@ -236,7 +237,6 @@ Symfony is the result of the work of many people who made the code better
- Arjen Brouwer (arjenjb)
- Katsuhiro OGAWA
- Patrick McDougle (patrick-mcdougle)
- Dany Maillard (maidmaid)
- Alif Rachmawadi
- Kristen Gilden (kgilden)
- Pierre-Yves LEBECQ (pylebecq)
@ -622,6 +622,7 @@ Symfony is the result of the work of many people who made the code better
- develop
- ReenExe
- Mark Sonnabaum
- Maxime Veber (nek-)
- Richard Quadling
- jochenvdv
- Arturas Smorgun (asarturas)
@ -644,6 +645,7 @@ Symfony is the result of the work of many people who made the code better
- Trent Steel (trsteel88)
- Yuen-Chi Lian
- Besnik Br
- Jose Gonzalez
- Dariusz Ruminski
- Joshua Nye
- Claudio Zizza
@ -1083,7 +1085,6 @@ Symfony is the result of the work of many people who made the code better
- Max Summe
- WedgeSama
- Felds Liscia
- Maxime Veber (nek-)
- Sullivan SENECHAL
- Tadcka
- Beth Binkovitz
@ -1094,12 +1095,12 @@ Symfony is the result of the work of many people who made the code better
- Tomaz Ahlin
- Marcus Stöhr (dafish)
- Emmanuel Vella (emmanuel.vella)
- Adam Szaraniec (mimol)
- Carsten Nielsen (phreaknerd)
- Mathieu Rochette
- Jay Severson
- René Kerner
- Nathaniel Catchpole
- Jose Gonzalez
- Adrien Samson (adriensamson)
- Samuel Gordalina (gordalina)
- Max Romanovsky (maxromanovsky)
@ -1260,6 +1261,7 @@ Symfony is the result of the work of many people who made the code better
- Aarón Nieves Fernández
- Mike Meier
- Kirill Saksin
- Julien Pauli
- Koalabaerchen
- michalmarcinkowski
- Warwick
@ -1307,6 +1309,7 @@ Symfony is the result of the work of many people who made the code better
- klemens
- dened
- Dmitry Korotovsky
- mcorteel
- Michael van Tricht
- Sam Ward
- Walther Lalk
@ -1337,6 +1340,7 @@ Symfony is the result of the work of many people who made the code better
- Jan Marek (janmarek)
- Mark de Haan (markdehaan)
- Dan Patrick (mdpatrick)
- Pedro Magalhães (pmmaga)
- Rares Vlaseanu (raresvla)
- tante kinast (tante)
- Vincent LEFORT (vlefort)
@ -1544,6 +1548,7 @@ Symfony is the result of the work of many people who made the code better
- Abdulkadir N. A.
- Yevgen Kovalienia
- Lebnik
- Shude
- Ondřej Führer
- Sema
- Elan Ruusamäe

View File

@ -587,10 +587,6 @@ Validator
}
```
* The default value of the strict option of the `Choice` Constraint has been
changed to `true` as of 4.0. If you need the previous behaviour ensure to
set the option to `false`.
* Setting the `checkDNS` option of the `Url` constraint to `true` is dropped
in favor of `Url::CHECK_DNS_TYPE_*` constants values.

View File

@ -17,6 +17,7 @@
],
"require": {
"php": "^7.1.3",
"ext-xml": "*",
"doctrine/common": "~2.4",
"fig/link-util": "^1.0",
"twig/twig": "~1.34|~2.4",

View File

@ -72,6 +72,13 @@ class HttpFoundationExtension extends AbstractExtension
$port = ':'.$this->requestContext->getHttpsPort();
}
if ('#' === $path[0]) {
$queryString = $this->requestContext->getQueryString();
$path = $this->requestContext->getPathInfo().($queryString ? '?'.$queryString : '').$path;
} elseif ('?' === $path[0]) {
$path = $this->requestContext->getPathInfo().$path;
}
if ('/' !== $path[0]) {
$path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path;
}
@ -82,6 +89,12 @@ class HttpFoundationExtension extends AbstractExtension
return $path;
}
if ('#' === $path[0]) {
$path = $request->getRequestUri().$path;
} elseif ('?' === $path[0]) {
$path = $request->getPathInfo().$path;
}
if (!$path || '/' !== $path[0]) {
$prefix = $request->getPathInfo();
$last = strlen($prefix) - 1;

View File

@ -42,6 +42,15 @@ class HttpFoundationExtensionTest extends TestCase
array('http://example.com/baz', 'http://example.com/baz', '/'),
array('https://example.com/baz', 'https://example.com/baz', '/'),
array('//example.com/baz', '//example.com/baz', '/'),
array('http://localhost/foo/bar?baz', '?baz', '/foo/bar'),
array('http://localhost/foo/bar?baz=1', '?baz=1', '/foo/bar?foo=1'),
array('http://localhost/foo/baz?baz=1', 'baz?baz=1', '/foo/bar?foo=1'),
array('http://localhost/foo/bar#baz', '#baz', '/foo/bar'),
array('http://localhost/foo/bar?0#baz', '#baz', '/foo/bar?0'),
array('http://localhost/foo/bar?baz=1#baz', '?baz=1#baz', '/foo/bar?foo=1'),
array('http://localhost/foo/baz?baz=1#baz', 'baz?baz=1#baz', '/foo/bar?foo=1'),
);
}

View File

@ -17,6 +17,7 @@
],
"require": {
"php": "^7.1.3",
"ext-xml": "*",
"symfony/http-kernel": "~3.4|~4.0",
"symfony/twig-bridge": "~3.4|~4.0",
"symfony/var-dumper": "~3.4|~4.0"

View File

@ -108,7 +108,8 @@ CHANGELOG
* Deprecated using core form types without dependencies as services
* Added `Symfony\Component\HttpHernel\DataCollector\RequestDataCollector::onKernelResponse()`
* Added `Symfony\Bundle\FrameworkBundle\DataCollector\RequestDataCollector`
* Deprecated service `serializer.mapping.cache.apc` (use `serializer.mapping.cache.doctrine.apc` instead)
* The `framework.serializer.cache` option and the service `serializer.mapping.cache.apc` have been
deprecated. APCu should now be automatically used when available.
3.0.0
-----

View File

@ -12,6 +12,7 @@
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\Cache\Adapter\AbstractAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -72,7 +73,7 @@ class CachePoolPass implements CompilerPassInterface
}
$i = 0;
foreach ($attributes as $attr) {
if (isset($tags[0][$attr])) {
if (isset($tags[0][$attr]) && ('namespace' !== $attr || ArrayAdapter::class !== $adapter->getClass())) {
$pool->replaceArgument($i++, $tags[0][$attr]);
}
unset($tags[0][$attr]);

View File

@ -33,7 +33,7 @@ class ResolveControllerNameSubscriber implements EventSubscriberInterface
public function onKernelRequest(GetResponseEvent $event)
{
$controller = $event->getRequest()->attributes->get('_controller');
if ($controller && false === strpos($controller, '::') && 2 === substr_count($controller, ':')) {
if (is_string($controller) && false === strpos($controller, '::') && 2 === substr_count($controller, ':')) {
// controller in the a:b:c notation then
$event->getRequest()->attributes->set('_controller', $this->parser->parse($controller));
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
@ -49,6 +50,24 @@ class CachePoolPassTest extends TestCase
$this->assertSame('D07rhFx97S', $cachePool->getArgument(0));
}
public function testNamespaceArgumentIsNotReplacedIfArrayAdapterIsUsed()
{
$container = new ContainerBuilder();
$container->setParameter('kernel.environment', 'prod');
$container->setParameter('kernel.name', 'app');
$container->setParameter('kernel.root_dir', 'foo');
$container->register('cache.adapter.array', ArrayAdapter::class)->addArgument(0);
$cachePool = new ChildDefinition('cache.adapter.array');
$cachePool->addTag('cache.pool');
$container->setDefinition('app.cache_pool', $cachePool);
$this->cachePoolPass->process($container);
$this->assertCount(0, $container->getDefinition('app.cache_pool')->getArguments());
}
public function testArgsAreReplaced()
{
$container = new ContainerBuilder();

View File

@ -37,7 +37,10 @@ class ResolveControllerNameSubscriberTest extends TestCase
$this->assertEquals('App\\Final\\Format::methodName', $request->attributes->get('_controller'));
}
public function testSkipsOtherControllerFormats()
/**
* @dataProvider provideSkippedControllers
*/
public function testSkipsOtherControllerFormats($controller)
{
$parser = $this->getMockBuilder(ControllerNameParser::class)->disableOriginalConstructor()->getMock();
$parser->expects($this->never())
@ -45,10 +48,16 @@ class ResolveControllerNameSubscriberTest extends TestCase
$httpKernel = $this->getMockBuilder(HttpKernelInterface::class)->getMock();
$request = new Request();
$request->attributes->set('_controller', 'Other:format');
$request->attributes->set('_controller', $controller);
$subscriber = new ResolveControllerNameSubscriber($parser);
$subscriber->onKernelRequest(new GetResponseEvent($httpKernel, $request, HttpKernelInterface::MASTER_REQUEST));
$this->assertEquals('Other:format', $request->attributes->get('_controller'));
$this->assertEquals($controller, $request->attributes->get('_controller'));
}
public function provideSkippedControllers()
{
yield array('Other:format');
yield array(function () {});
}
}

View File

@ -33,7 +33,7 @@ class RedirectableUrlMatcherTest extends TestCase
'scheme' => null,
'httpPort' => $context->getHttpPort(),
'httpsPort' => $context->getHttpsPort(),
'_route' => null,
'_route' => 'foo',
),
$matcher->match('/foo')
);

View File

@ -17,6 +17,7 @@
],
"require": {
"php": "^7.1.3",
"ext-xml": "*",
"symfony/security": "~3.4|~4.0",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/http-kernel": "~3.4|~4.0"

View File

@ -9,6 +9,8 @@
<xsd:complexType name="config">
<xsd:sequence>
<xsd:element name="date" type="date" minOccurs="0" maxOccurs="1" />
<xsd:element name="number-format" type="number_format" minOccurs="0" maxOccurs="1" />
<xsd:element name="form-theme" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="global" type="global" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="path" type="path" minOccurs="0" maxOccurs="unbounded" />
@ -26,6 +28,18 @@
<xsd:attribute name="exception-controller" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="date">
<xsd:attribute name="format" type="xsd:string" />
<xsd:attribute name="interval-format" type="xsd:string" />
<xsd:attribute name="timezone" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="number_format">
<xsd:attribute name="decimals" type="xsd:integer" />
<xsd:attribute name="decimal-point" type="xsd:string" />
<xsd:attribute name="thousands-separator" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="path" mixed="true">
<xsd:attribute name="namespace" type="xsd:string" />
</xsd:complexType>

View File

@ -0,0 +1,14 @@
<?php
$container->loadFromExtension('twig', array(
'date' => array(
'format' => 'Y-m-d',
'interval_format' => '%d',
'timezone' => 'Europe/Berlin',
),
'number_format' => array(
'decimals' => 2,
'decimal_point' => ',',
'thousands_separator' => '.',
),
));

View File

@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config>
<twig:date format="Y-m-d" interval-format="%d" timezone="Europe/Berlin" />
<twig:number-format decimals="2" decimal-point="," thousands-separator="." />
</twig:config>
</container>

View File

@ -0,0 +1,9 @@
twig:
date:
format: Y-m-d
interval_format: '%d'
timezone: Europe/Berlin
number_format:
decimals: 2
decimal_point: ','
thousands_separator: .

View File

@ -116,6 +116,26 @@ class TwigExtensionTest extends TestCase
$this->assertEquals('name', $options['autoescape']);
}
/**
* @dataProvider getFormats
*/
public function testLoadCustomDateFormats($fileFormat)
{
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$this->loadFromFile($container, 'formats', $fileFormat);
$this->compileContainer($container);
$environmentConfigurator = $container->getDefinition('twig.configurator.environment');
$this->assertSame('Y-m-d', $environmentConfigurator->getArgument(0));
$this->assertSame('%d', $environmentConfigurator->getArgument(1));
$this->assertSame('Europe/Berlin', $environmentConfigurator->getArgument(2));
$this->assertSame(2, $environmentConfigurator->getArgument(3));
$this->assertSame(',', $environmentConfigurator->getArgument(4));
$this->assertSame('.', $environmentConfigurator->getArgument(5));
}
public function testGlobalsWithDifferentTypesAndValues()
{
$globals = array(

View File

@ -128,34 +128,6 @@ class ProfilerController
)), 200, array('Content-Type' => 'text/html'));
}
/**
* Displays information page.
*
* @param Request $request The current HTTP Request
* @param string $about The about message
*
* @return Response A Response instance
*
* @throws NotFoundHttpException
*/
public function infoAction(Request $request, $about)
{
if (null === $this->profiler) {
throw new NotFoundHttpException('The profiler must be enabled.');
}
$this->profiler->disable();
if (null !== $this->cspHandler) {
$this->cspHandler->disableCsp();
}
return new Response($this->twig->render('@WebProfiler/Profiler/info.html.twig', array(
'about' => $about,
'request' => $request,
)), 200, array('Content-Type' => 'text/html'));
}
/**
* Renders the Web Debug Toolbar.
*

View File

@ -16,10 +16,6 @@
<default key="_controller">web_profiler.controller.profiler:searchBarAction</default>
</route>
<route id="_profiler_info" path="/info/{about}">
<default key="_controller">web_profiler.controller.profiler:infoAction</default>
</route>
<route id="_profiler_phpinfo" path="/phpinfo">
<default key="_controller">web_profiler.controller.profiler:phpinfoAction</default>
</route>

View File

@ -1,25 +1,10 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% set messages = {
'purge' : {
status: 'success',
title: 'The profiler database was purged successfully',
message: 'Now you need to browse some pages with the Symfony Profiler enabled to collect data.'
},
'no_token' : {
status: 'error',
title: (token|default('') == 'latest') ? 'There are no profiles' : 'Token not found',
message: (token|default('') == 'latest') ? 'No profiles found in the database.' : 'Token "' ~ token|default('') ~ '" was not found in the database.'
},
'upload_error' : {
status: 'error',
title: 'A problem occurred when uploading the data',
message: 'No file given or the file was not uploaded successfully.'
},
'already_exists' : {
status: 'error',
title: 'A problem occurred when uploading the data',
message: 'The token already exists in the database.'
}
} %}

View File

@ -570,3 +570,10 @@
padding: 5px 0;
margin-right: 10px;
}
/***** Media query print: Do not print the Toolbar. *****/
@media print {
.sf-toolbar {
display: none;
}
}

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\Cache\Tests\Adapter;
use Psr\Log\NullLogger;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
class ApcuAdapterTest extends AdapterTestCase
@ -23,9 +24,14 @@ class ApcuAdapterTest extends AdapterTestCase
public function createCachePool($defaultLifetime = 0)
{
if (!function_exists('apcu_fetch') || !ini_get('apc.enabled') || ('cli' === PHP_SAPI && !ini_get('apc.enable_cli'))) {
if (!function_exists('apcu_fetch') || !ini_get('apc.enabled')) {
$this->markTestSkipped('APCu extension is required.');
}
if ('cli' === PHP_SAPI && !ini_get('apc.enable_cli')) {
if ('testWithCliSapi' !== $this->getName()) {
$this->markTestSkipped('APCu extension is required.');
}
}
if ('\\' === DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Fails transiently on Windows.');
}
@ -70,4 +76,24 @@ class ApcuAdapterTest extends AdapterTestCase
$this->assertFalse($item->isHit());
$this->assertNull($item->get());
}
public function testWithCliSapi()
{
try {
// disable PHPUnit error handler to mimic a production environment
$isCalled = false;
set_error_handler(function () use (&$isCalled) {
$isCalled = true;
});
$pool = new ApcuAdapter(str_replace('\\', '.', __CLASS__));
$pool->setLogger(new NullLogger());
$item = $pool->getItem('foo');
$item->isHit();
$pool->save($item->set('bar'));
$this->assertFalse($isCalled);
} finally {
restore_error_handler();
}
}
}

View File

@ -352,7 +352,7 @@ final class Dotenv
}
$name = $matches[3];
$value = isset($this->values[$name]) ? $this->values[$name] : (isset($_ENV[$name]) ? isset($_ENV[$name]) : (string) getenv($name));
$value = isset($this->values[$name]) ? $this->values[$name] : (isset($_ENV[$name]) ? $_ENV[$name] : (string) getenv($name));
if (!$matches[2] && isset($matches[4])) {
$value .= '}';

View File

@ -63,6 +63,7 @@ class DotenvTest extends TestCase
public function getEnvData()
{
putenv('LOCAL=local');
$_ENV['REMOTE'] = 'remote';
$tests = array(
// spaces
@ -134,6 +135,7 @@ class DotenvTest extends TestCase
array('FOO=" \\$ "', array('FOO' => ' $ ')),
array('FOO=" $ "', array('FOO' => ' $ ')),
array('BAR=$LOCAL', array('BAR' => 'local')),
array('BAR=$REMOTE', array('BAR' => 'remote')),
array('FOO=$NOTDEFINED', array('FOO' => '')),
);

View File

@ -37,7 +37,8 @@ class Filesystem
*/
public function copy($originFile, $targetFile, $overwriteNewerFiles = false)
{
if (stream_is_local($originFile) && !is_file($originFile)) {
$originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://');
if ($originIsLocal && !is_file($originFile)) {
throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile);
}
@ -68,11 +69,13 @@ class Filesystem
throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile);
}
// Like `cp`, preserve executable permission bits
@chmod($targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111));
if ($originIsLocal) {
// Like `cp`, preserve executable permission bits
@chmod($targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111));
if (stream_is_local($originFile) && $bytesCopied !== ($bytesOrigin = filesize($originFile))) {
throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile);
if ($bytesCopied !== $bytesOrigin = filesize($originFile)) {
throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile);
}
}
}
}

View File

@ -11,6 +11,7 @@ CHANGELOG
-----
* Added support for prioritized routing loaders.
* Add matched and default parameters to redirect responses
3.3.0
-----

View File

@ -333,10 +333,35 @@ EOF;
}
}
// the offset where the return value is appended below, with indendation
$retOffset = 12 + strlen($code);
// optimize parameters array
if ($matches || $hostMatches) {
$vars = array();
if ($hostMatches) {
$vars[] = '$hostMatches';
}
if ($matches) {
$vars[] = '$matches';
}
$vars[] = "array('_route' => '$name')";
$code .= sprintf(
" \$ret = \$this->mergeDefaults(array_replace(%s), %s);\n",
implode(', ', $vars),
str_replace("\n", '', var_export($route->getDefaults(), true))
);
} elseif ($route->getDefaults()) {
$code .= sprintf(" \$ret = %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
} else {
$code .= sprintf(" \$ret = array('_route' => '%s');\n", $name);
}
if ($hasTrailingSlash) {
$code .= <<<EOF
if (substr(\$pathinfo, -1) !== '/') {
return \$this->redirect(\$pathinfo.'/', '$name');
return array_replace(\$ret, \$this->redirect(\$pathinfo.'/', '$name'));
}
@ -351,33 +376,17 @@ EOF;
$code .= <<<EOF
\$requiredSchemes = $schemes;
if (!isset(\$requiredSchemes[\$scheme])) {
return \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes));
return array_replace(\$ret, \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes)));
}
EOF;
}
// optimize parameters array
if ($matches || $hostMatches) {
$vars = array();
if ($hostMatches) {
$vars[] = '$hostMatches';
}
if ($matches) {
$vars[] = '$matches';
}
$vars[] = "array('_route' => '$name')";
$code .= sprintf(
" return \$this->mergeDefaults(array_replace(%s), %s);\n",
implode(', ', $vars),
str_replace("\n", '', var_export($route->getDefaults(), true))
);
} elseif ($route->getDefaults()) {
$code .= sprintf(" return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
if ($hasTrailingSlash || $schemes) {
$code .= " return \$ret;\n";
} else {
$code .= sprintf(" return array('_route' => '%s');\n", $name);
$code = substr_replace($code, 'return', $retOffset, 6);
}
$code .= " }\n";

View File

@ -32,9 +32,9 @@ abstract class RedirectableUrlMatcher extends UrlMatcher implements Redirectable
}
try {
parent::match($pathinfo.'/');
$parameters = parent::match($pathinfo.'/');
return $this->redirect($pathinfo.'/', null);
return array_replace($parameters, $this->redirect($pathinfo.'/', isset($parameters['_route']) ? $parameters['_route'] : null));
} catch (ResourceNotFoundException $e2) {
throw $e;
}

View File

@ -163,15 +163,11 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
$status = $this->handleRouteRequirements($pathinfo, $name, $route);
if (self::ROUTE_MATCH === $status[0]) {
return $status[1];
}
if (self::REQUIREMENT_MISMATCH === $status[0]) {
continue;
}
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches));
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array()));
}
}

View File

@ -87,22 +87,24 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
// baz3
if ('/test/baz3' === $trimmedPathinfo) {
$ret = array('_route' => 'baz3');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'baz3');
return array_replace($ret, $this->redirect($pathinfo.'/', 'baz3'));
}
return array('_route' => 'baz3');
return $ret;
}
}
// baz4
if (preg_match('#^/test/(?P<foo>[^/]++)/?$#s', $pathinfo, $matches)) {
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'baz4');
return array_replace($ret, $this->redirect($pathinfo.'/', 'baz4'));
}
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
return $ret;
}
// baz5
@ -181,11 +183,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
// hey
if ('/multi/hey' === $trimmedPathinfo) {
$ret = array('_route' => 'hey');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'hey');
return array_replace($ret, $this->redirect($pathinfo.'/', 'hey'));
}
return array('_route' => 'hey');
return $ret;
}
// overridden2
@ -326,22 +329,24 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
// secure
if ('/secure' === $pathinfo) {
$ret = array('_route' => 'secure');
$requiredSchemes = array ( 'https' => 0,);
if (!isset($requiredSchemes[$scheme])) {
return $this->redirect($pathinfo, 'secure', key($requiredSchemes));
return array_replace($ret, $this->redirect($pathinfo, 'secure', key($requiredSchemes)));
}
return array('_route' => 'secure');
return $ret;
}
// nonsecure
if ('/nonsecure' === $pathinfo) {
$ret = array('_route' => 'nonsecure');
$requiredSchemes = array ( 'http' => 0,);
if (!isset($requiredSchemes[$scheme])) {
return $this->redirect($pathinfo, 'nonsecure', key($requiredSchemes));
return array_replace($ret, $this->redirect($pathinfo, 'nonsecure', key($requiredSchemes)));
}
return array('_route' => 'nonsecure');
return $ret;
}
throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();

View File

@ -61,29 +61,32 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if (0 === strpos($pathinfo, '/a')) {
// a_fourth
if ('/a/44' === $trimmedPathinfo) {
$ret = array('_route' => 'a_fourth');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'a_fourth');
return array_replace($ret, $this->redirect($pathinfo.'/', 'a_fourth'));
}
return array('_route' => 'a_fourth');
return $ret;
}
// a_fifth
if ('/a/55' === $trimmedPathinfo) {
$ret = array('_route' => 'a_fifth');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'a_fifth');
return array_replace($ret, $this->redirect($pathinfo.'/', 'a_fifth'));
}
return array('_route' => 'a_fifth');
return $ret;
}
// a_sixth
if ('/a/66' === $trimmedPathinfo) {
$ret = array('_route' => 'a_sixth');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'a_sixth');
return array_replace($ret, $this->redirect($pathinfo.'/', 'a_sixth'));
}
return array('_route' => 'a_sixth');
return $ret;
}
}
@ -96,29 +99,32 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if (0 === strpos($pathinfo, '/nested/group')) {
// nested_a
if ('/nested/group/a' === $trimmedPathinfo) {
$ret = array('_route' => 'nested_a');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'nested_a');
return array_replace($ret, $this->redirect($pathinfo.'/', 'nested_a'));
}
return array('_route' => 'nested_a');
return $ret;
}
// nested_b
if ('/nested/group/b' === $trimmedPathinfo) {
$ret = array('_route' => 'nested_b');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'nested_b');
return array_replace($ret, $this->redirect($pathinfo.'/', 'nested_b'));
}
return array('_route' => 'nested_b');
return $ret;
}
// nested_c
if ('/nested/group/c' === $trimmedPathinfo) {
$ret = array('_route' => 'nested_c');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'nested_c');
return array_replace($ret, $this->redirect($pathinfo.'/', 'nested_c'));
}
return array('_route' => 'nested_c');
return $ret;
}
}
@ -126,29 +132,32 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
elseif (0 === strpos($pathinfo, '/slashed/group')) {
// slashed_a
if ('/slashed/group' === $trimmedPathinfo) {
$ret = array('_route' => 'slashed_a');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'slashed_a');
return array_replace($ret, $this->redirect($pathinfo.'/', 'slashed_a'));
}
return array('_route' => 'slashed_a');
return $ret;
}
// slashed_b
if ('/slashed/group/b' === $trimmedPathinfo) {
$ret = array('_route' => 'slashed_b');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'slashed_b');
return array_replace($ret, $this->redirect($pathinfo.'/', 'slashed_b'));
}
return array('_route' => 'slashed_b');
return $ret;
}
// slashed_c
if ('/slashed/group/c' === $trimmedPathinfo) {
$ret = array('_route' => 'slashed_c');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'slashed_c');
return array_replace($ret, $this->redirect($pathinfo.'/', 'slashed_c'));
}
return array('_route' => 'slashed_c');
return $ret;
}
}

View File

@ -38,11 +38,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if (0 === strpos($pathinfo, '/trailing/simple')) {
// simple_trailing_slash_no_methods
if ('/trailing/simple/no-methods' === $trimmedPathinfo) {
$ret = array('_route' => 'simple_trailing_slash_no_methods');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'simple_trailing_slash_no_methods');
return array_replace($ret, $this->redirect($pathinfo.'/', 'simple_trailing_slash_no_methods'));
}
return array('_route' => 'simple_trailing_slash_no_methods');
return $ret;
}
// simple_trailing_slash_GET_method
@ -52,11 +53,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
goto not_simple_trailing_slash_GET_method;
}
$ret = array('_route' => 'simple_trailing_slash_GET_method');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'simple_trailing_slash_GET_method');
return array_replace($ret, $this->redirect($pathinfo.'/', 'simple_trailing_slash_GET_method'));
}
return array('_route' => 'simple_trailing_slash_GET_method');
return $ret;
}
not_simple_trailing_slash_GET_method:
@ -67,11 +69,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
goto not_simple_trailing_slash_HEAD_method;
}
$ret = array('_route' => 'simple_trailing_slash_HEAD_method');
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'simple_trailing_slash_HEAD_method');
return array_replace($ret, $this->redirect($pathinfo.'/', 'simple_trailing_slash_HEAD_method'));
}
return array('_route' => 'simple_trailing_slash_HEAD_method');
return $ret;
}
not_simple_trailing_slash_HEAD_method:
@ -91,11 +94,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
elseif (0 === strpos($pathinfo, '/trailing/regex')) {
// regex_trailing_slash_no_methods
if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P<param>[^/]++)/?$#s', $pathinfo, $matches)) {
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ());
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'regex_trailing_slash_no_methods');
return array_replace($ret, $this->redirect($pathinfo.'/', 'regex_trailing_slash_no_methods'));
}
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ());
return $ret;
}
// regex_trailing_slash_GET_method
@ -105,11 +109,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
goto not_regex_trailing_slash_GET_method;
}
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ());
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'regex_trailing_slash_GET_method');
return array_replace($ret, $this->redirect($pathinfo.'/', 'regex_trailing_slash_GET_method'));
}
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ());
return $ret;
}
not_regex_trailing_slash_GET_method:
@ -120,11 +125,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
goto not_regex_trailing_slash_HEAD_method;
}
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ());
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'regex_trailing_slash_HEAD_method');
return array_replace($ret, $this->redirect($pathinfo.'/', 'regex_trailing_slash_HEAD_method'));
}
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ());
return $ret;
}
not_regex_trailing_slash_HEAD_method:

View File

@ -24,7 +24,7 @@ class RedirectableUrlMatcherTest extends TestCase
$coll->add('foo', new Route('/foo/'));
$matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
$matcher->expects($this->once())->method('redirect');
$matcher->expects($this->once())->method('redirect')->will($this->returnValue(array()));
$matcher->match('/foo');
}
@ -65,8 +65,37 @@ class RedirectableUrlMatcherTest extends TestCase
$matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
$matcher
->expects($this->never())
->method('redirect')
;
->method('redirect');
$matcher->match('/foo');
}
public function testSchemeRedirectWithParams()
{
$coll = new RouteCollection();
$coll->add('foo', new Route('/foo/{bar}', array(), array(), array(), '', array('https')));
$matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
$matcher
->expects($this->once())
->method('redirect')
->with('/foo/baz', 'foo', 'https')
->will($this->returnValue(array('redirect' => 'value')))
;
$this->assertEquals(array('_route' => 'foo', 'bar' => 'baz', 'redirect' => 'value'), $matcher->match('/foo/baz'));
}
public function testSlashRedirectWithParams()
{
$coll = new RouteCollection();
$coll->add('foo', new Route('/foo/{bar}/'));
$matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
$matcher
->expects($this->once())
->method('redirect')
->with('/foo/baz/', 'foo', null)
->will($this->returnValue(array('redirect' => 'value')))
;
$this->assertEquals(array('_route' => 'foo', 'bar' => 'baz', 'redirect' => 'value'), $matcher->match('/foo/baz'));
}
}

View File

@ -13,7 +13,6 @@ namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@ -54,8 +53,6 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
/**
* {@inheritdoc}
*
* @throws CircularReferenceException
*/
public function normalize($object, $format = null, array $context = array())
{

View File

@ -33,8 +33,6 @@ class ArrayDenormalizer implements ContextAwareDenormalizerInterface, Serializer
/**
* {@inheritdoc}
*
* @throws UnexpectedValueException
*/
public function denormalize($data, $class, $format = null, array $context = array())
{

View File

@ -79,9 +79,6 @@ class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface
* Regex adapted from Brian Grinstead code.
*
* @see https://gist.github.com/bgrins/6194623
*
* @throws InvalidArgumentException
* @throws UnexpectedValueException
*/
public function denormalize($data, $class, $format = null, array $context = array())
{

View File

@ -11,6 +11,13 @@
namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\Serializer\Exception\BadMethodCallException;
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/**
* Defines the interface of denormalizers.
*
@ -27,6 +34,13 @@ interface DenormalizerInterface
* @param array $context options available to the denormalizer
*
* @return object
*
* @throws BadMethodCallException Occurs when the normalizer is not called in an expected context
* @throws InvalidArgumentException Occurs when the arguments are not coherent or not supported
* @throws UnexpectedValueException Occurs when the item cannot be hydrated with the given data
* @throws ExtraAttributesException Occurs when the item doesn't have attribute to receive given data
* @throws LogicException Occurs when the normalizer is not supposed to denormalize
* @throws RuntimeException Occurs if the class cannot be instantiated
*/
public function denormalize($data, $class, $format = null, array $context = array());

View File

@ -11,6 +11,10 @@
namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
/**
* Defines the interface of normalizers.
*
@ -26,6 +30,11 @@ interface NormalizerInterface
* @param array $context Context options for the normalizer
*
* @return array|scalar
*
* @throws InvalidArgumentException Occurs when the object given is not an attempted type for the normalizer
* @throws CircularReferenceException Occurs when the normalizer detects a circular reference when no circular
* reference handler can fix it
* @throws LogicException Occurs when the normalizer is not called in an expected context
*/
public function normalize($object, $format = null, array $context = array());