Merge branch '2.8' into 3.2
* 2.8: (40 commits) Show exception is checked twice in ExceptionController of twig allow SSI fragments configuration in XML files Display a better error message when the toolbar cannot be displayed render hidden _method field in form_rest() return fallback locales whenever possible [Console] Fix catching exception type in QuestionHelper [WebProfilerBundle] Eliminate line wrap on count columnt (routing) [Routing] Fix XmlFileLoader exception message [Translation] Fix FileLoader::loadResource() php doc Sessions: configurable "use_strict_mode" option for NativeSessionStorage [FrameworkBundle] [Command] Clean bundle directory, fixes #23177 Reset redirectCount when throwing exception [TwigBundle] Remove template.xml services when templating is disabled add content-type header on exception response Embedding a response that combines expiration and validation, that should not defeat expiration on the combined response Fix two edge cases in ResponseCacheStrategy [Routing] Expose request in route conditions, if needed and possible [Routing] Expose request in route conditions, if needed and possible [Translation][FrameworkBundle] Fix resource loading order inconsistency reported in #23034 [Filesystem] added workaround in Filesystem::rename for PHP bug ...
This commit is contained in:
commit
80b114e66b
@ -20,13 +20,13 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Javier Eguiluz (javier.eguiluz)
|
||||
- Hugo Hamon (hhamon)
|
||||
- Abdellatif Ait boudad (aitboudad)
|
||||
- Romain Neutron (romain)
|
||||
- Pascal Borreli (pborreli)
|
||||
- Wouter De Jong (wouterj)
|
||||
- Romain Neutron (romain)
|
||||
- Grégoire Pineau (lyrixx)
|
||||
- Robin Chalas (chalas_r)
|
||||
- Joseph Bielawski (stloyd)
|
||||
- Maxime Steinhausser (ogizanagi)
|
||||
- Grégoire Pineau (lyrixx)
|
||||
- Joseph Bielawski (stloyd)
|
||||
- Karma Dordrak (drak)
|
||||
- Lukas Kahwe Smith (lsmith)
|
||||
- Martin Hasoň (hason)
|
||||
@ -72,8 +72,8 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Dariusz Górecki (canni)
|
||||
- Titouan Galopin (tgalopin)
|
||||
- Douglas Greenshields (shieldo)
|
||||
- Konstantin Myakshin (koc)
|
||||
- Jáchym Toušek (enumag)
|
||||
- Konstantin Myakshin (koc)
|
||||
- Lee McDermott
|
||||
- Brandon Turner
|
||||
- Luis Cordova (cordoval)
|
||||
@ -112,10 +112,10 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Jacob Dreesen (jdreesen)
|
||||
- Tobias Nyholm (tobias)
|
||||
- Tomáš Votruba (tomas_votruba)
|
||||
- Yonel Ceruto González (yonelceruto)
|
||||
- Fabien Pennequin (fabienpennequin)
|
||||
- Gordon Franke (gimler)
|
||||
- Eric GELOEN (gelo)
|
||||
- Yonel Ceruto González (yonelceruto)
|
||||
- Daniel Wehner (dawehner)
|
||||
- Tugdual Saunier (tucksaun)
|
||||
- Théo FIDRY (theofidry)
|
||||
@ -130,6 +130,7 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Daniel Gomes (danielcsgomes)
|
||||
- Hidenori Goto (hidenorigoto)
|
||||
- Guilherme Blanco (guilhermeblanco)
|
||||
- Vincent AUBERT (vincent)
|
||||
- Pablo Godel (pgodel)
|
||||
- Jérémie Augustin (jaugustin)
|
||||
- Andréia Bohner (andreia)
|
||||
@ -140,10 +141,10 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Joel Wurtz (brouznouf)
|
||||
- Philipp Wahala (hifi)
|
||||
- Vyacheslav Pavlov
|
||||
- Richard van Laak (rvanlaak)
|
||||
- Javier Spagnoletti (phansys)
|
||||
- Richard Shank (iampersistent)
|
||||
- Thomas Rabaix (rande)
|
||||
- Vincent AUBERT (vincent)
|
||||
- Rouven Weßling (realityking)
|
||||
- Teoh Han Hui (teohhanhui)
|
||||
- Jérôme Vasseur (jvasseur)
|
||||
@ -151,7 +152,6 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Helmer Aaviksoo
|
||||
- Grégoire Paris (greg0ire)
|
||||
- Hiromi Hishida (77web)
|
||||
- Richard van Laak (rvanlaak)
|
||||
- Matthieu Ouellette-Vachon (maoueh)
|
||||
- Michał Pipa (michal.pipa)
|
||||
- Amal Raghav (kertz)
|
||||
@ -244,6 +244,7 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Uwe Jäger (uwej711)
|
||||
- Eugene Leonovich (rybakit)
|
||||
- Filippo Tessarotto
|
||||
- Oleg Voronkovich
|
||||
- Joseph Rouff (rouffj)
|
||||
- Félix Labrecque (woodspire)
|
||||
- GordonsLondon
|
||||
@ -279,7 +280,6 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Jordan Samouh (jordansamouh)
|
||||
- Chris Smith (cs278)
|
||||
- Florian Klein (docteurklein)
|
||||
- Oleg Voronkovich
|
||||
- Manuel Kiessling (manuelkiessling)
|
||||
- Atsuhiro KUBO (iteman)
|
||||
- Andrew Moore (finewolf)
|
||||
@ -389,6 +389,7 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Emanuele Gaspari (inmarelibero)
|
||||
- Sébastien Santoro (dereckson)
|
||||
- Brian King
|
||||
- Frank de Jonge (frenkynet)
|
||||
- Michel Salib (michelsalib)
|
||||
- geoffrey
|
||||
- Steffen Roßkamp
|
||||
@ -525,7 +526,6 @@ Symfony is the result of the work of many people who made the code better
|
||||
- Romain Pierre (romain-pierre)
|
||||
- Jan Behrens
|
||||
- Mantas Var (mvar)
|
||||
- Frank de Jonge (frenkynet)
|
||||
- Sebastian Krebs
|
||||
- Jean-Christophe Cuvelier [Artack]
|
||||
- Christopher Davis (chrisguitarguy)
|
||||
@ -1280,6 +1280,7 @@ Symfony is the result of the work of many people who made the code better
|
||||
- ged15
|
||||
- Daan van Renterghem
|
||||
- Nicole Cordes
|
||||
- Martin Kirilov
|
||||
- Bram Van der Sype (brammm)
|
||||
- Christopher Hertel (chertel)
|
||||
- Guile (guile)
|
||||
|
@ -285,6 +285,7 @@
|
||||
{%- endblock form -%}
|
||||
|
||||
{%- block form_start -%}
|
||||
{%- do form.setMethodRendered() -%}
|
||||
{% set method = method|upper %}
|
||||
{%- if method in ["GET", "POST"] -%}
|
||||
{% set form_method = method %}
|
||||
@ -320,6 +321,20 @@
|
||||
{{- form_row(child) -}}
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
|
||||
{% if not form.methodRendered %}
|
||||
{%- do form.setMethodRendered() -%}
|
||||
{% set method = method|upper %}
|
||||
{%- if method in ["GET", "POST"] -%}
|
||||
{% set form_method = method %}
|
||||
{%- else -%}
|
||||
{% set form_method = "POST" %}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if form_method != method -%}
|
||||
<input type="hidden" name="_method" value="{{ method }}" />
|
||||
{%- endif -%}
|
||||
{% endif %}
|
||||
{% endblock form_rest %}
|
||||
|
||||
{# Support #}
|
||||
|
@ -22,7 +22,7 @@
|
||||
"require-dev": {
|
||||
"symfony/asset": "~2.8|~3.0",
|
||||
"symfony/finder": "~2.8|~3.0",
|
||||
"symfony/form": "^3.2.7",
|
||||
"symfony/form": "^3.2.10|^3.3.3",
|
||||
"symfony/http-kernel": "~3.2",
|
||||
"symfony/polyfill-intl-icu": "~1.0",
|
||||
"symfony/routing": "~2.8|~3.0",
|
||||
@ -36,6 +36,9 @@
|
||||
"symfony/var-dumper": "~2.8.10|~3.1.4|~3.2",
|
||||
"symfony/expression-language": "~2.8|~3.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/form": "<3.2.10|~3.3,<3.3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/finder": "",
|
||||
"symfony/asset": "For using the AssetExtension",
|
||||
|
@ -110,6 +110,7 @@ EOT
|
||||
$rows = array();
|
||||
$copyUsed = false;
|
||||
$exitCode = 0;
|
||||
$validAssetDirs = array();
|
||||
/** @var BundleInterface $bundle */
|
||||
foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) {
|
||||
if (!is_dir($originDir = $bundle->getPath().'/Resources/public')) {
|
||||
@ -148,6 +149,13 @@ EOT
|
||||
$exitCode = 1;
|
||||
$rows[] = array(sprintf('<fg=red;options=bold>%s</>', '\\' === DIRECTORY_SEPARATOR ? 'ERROR' : "\xE2\x9C\x98" /* HEAVY BALLOT X (U+2718) */), $message, $e->getMessage());
|
||||
}
|
||||
$validAssetDirs[] = $targetDir;
|
||||
}
|
||||
// remove the assets of the bundles that no longer exist
|
||||
foreach (new \FilesystemIterator($bundlesDir) as $dir) {
|
||||
if (!in_array($dir, $validAssetDirs)) {
|
||||
$filesystem->remove($dir);
|
||||
}
|
||||
}
|
||||
|
||||
$io->table(array('', 'Bundle', 'Method / Error'), $rows);
|
||||
|
@ -147,7 +147,7 @@ EOF
|
||||
$safeTempKernel = str_replace('\\', '\\\\', get_class($tempKernel));
|
||||
$realKernelFQN = get_class($realKernel);
|
||||
|
||||
foreach (Finder::create()->files()->name('*.meta')->in($warmupDir) as $file) {
|
||||
foreach (Finder::create()->files()->depth('<3')->name('*.meta')->in($warmupDir) as $file) {
|
||||
file_put_contents($file, preg_replace(
|
||||
'/(C\:\d+\:)"'.$safeTempKernel.'"/',
|
||||
sprintf('$1"%s"', $realKernelFQN),
|
||||
@ -159,14 +159,16 @@ EOF
|
||||
$search = array($warmupDir, str_replace('\\', '\\\\', $warmupDir));
|
||||
$replace = str_replace('\\', '/', $realCacheDir);
|
||||
foreach (Finder::create()->files()->in($warmupDir) as $file) {
|
||||
$content = str_replace($search, $replace, file_get_contents($file));
|
||||
$content = str_replace($search, $replace, file_get_contents($file), $count);
|
||||
if ($count) {
|
||||
file_put_contents($file, $content);
|
||||
}
|
||||
}
|
||||
|
||||
// fix references to container's class
|
||||
$tempContainerClass = get_class($tempKernel->getContainer());
|
||||
$realContainerClass = get_class($realKernel->getContainer());
|
||||
foreach (Finder::create()->files()->name($tempContainerClass.'*')->in($warmupDir) as $file) {
|
||||
foreach (Finder::create()->files()->depth('<2')->name($tempContainerClass.'*')->in($warmupDir) as $file) {
|
||||
$content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file));
|
||||
file_put_contents($file, $content);
|
||||
rename($file, str_replace(DIRECTORY_SEPARATOR.$tempContainerClass, DIRECTORY_SEPARATOR.$realContainerClass, $file));
|
||||
|
@ -400,6 +400,7 @@ class Configuration implements ConfigurationInterface
|
||||
->scalarNode('gc_divisor')->end()
|
||||
->scalarNode('gc_probability')->defaultValue(1)->end()
|
||||
->scalarNode('gc_maxlifetime')->end()
|
||||
->booleanNode('use_strict_mode')->end()
|
||||
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
|
||||
->integerNode('metadata_update_threshold')
|
||||
->defaultValue('0')
|
||||
|
@ -555,7 +555,7 @@ class FrameworkExtension extends Extension
|
||||
// session storage
|
||||
$container->setAlias('session.storage', $config['storage_id']);
|
||||
$options = array();
|
||||
foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor') as $key) {
|
||||
foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'use_strict_mode') as $key) {
|
||||
if (isset($config[$key])) {
|
||||
$options[$key] = $config[$key];
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
<xsd:element name="form" type="form" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="csrf-protection" type="csrf_protection" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="esi" type="esi" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="ssi" type="ssi" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="fragments" type="fragments" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="profiler" type="profiler" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="router" type="router" minOccurs="0" maxOccurs="1" />
|
||||
@ -57,6 +58,10 @@
|
||||
<xsd:attribute name="enabled" type="xsd:boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="ssi">
|
||||
<xsd:attribute name="enabled" type="xsd:boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="fragments">
|
||||
<xsd:attribute name="enabled" type="xsd:boolean" />
|
||||
<xsd:attribute name="path" type="xsd:string" />
|
||||
@ -105,6 +110,7 @@
|
||||
<xsd:attribute name="gc-maxlifetime" type="xsd:string" />
|
||||
<xsd:attribute name="gc-divisor" type="xsd:string" />
|
||||
<xsd:attribute name="gc-probability" type="xsd:string" />
|
||||
<xsd:attribute name="use-strict-mode" type="xsd:boolean" />
|
||||
<xsd:attribute name="save-path" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
|
@ -14,6 +14,9 @@ $container->loadFromExtension('framework', array(
|
||||
'esi' => array(
|
||||
'enabled' => true,
|
||||
),
|
||||
'ssi' => array(
|
||||
'enabled' => true,
|
||||
),
|
||||
'profiler' => array(
|
||||
'only_exceptions' => true,
|
||||
'enabled' => false,
|
||||
|
@ -12,6 +12,7 @@
|
||||
<framework:csrf-protection field-name="_csrf"/>
|
||||
</framework:form>
|
||||
<framework:esi enabled="true" />
|
||||
<framework:ssi enabled="true" />
|
||||
<framework:profiler only-exceptions="true" enabled="false" />
|
||||
<framework:router resource="%kernel.root_dir%/config/routing.xml" type="xml" />
|
||||
<framework:session gc-maxlifetime="90000" gc-probability="1" gc-divisor="108" storage-id="session.storage.native" handler-id="session.handler.native_file" name="_SYMFONY" cookie-lifetime="86400" cookie-path="/" cookie-domain="example.com" cookie-secure="true" cookie-httponly="false" use-cookies="true" save-path="/path/to/sessions" />
|
||||
|
@ -9,6 +9,8 @@ framework:
|
||||
trusted_proxies: ['127.0.0.1', '10.0.0.1']
|
||||
esi:
|
||||
enabled: true
|
||||
ssi:
|
||||
enabled: true
|
||||
profiler:
|
||||
only_exceptions: true
|
||||
enabled: false
|
||||
|
@ -133,6 +133,13 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
$this->assertTrue($container->hasDefinition('esi'), '->registerEsiConfiguration() loads esi.xml');
|
||||
}
|
||||
|
||||
public function testSsi()
|
||||
{
|
||||
$container = $this->createContainerFromFile('full');
|
||||
|
||||
$this->assertTrue($container->hasDefinition('ssi'), '->registerSsiConfiguration() loads ssi.xml');
|
||||
}
|
||||
|
||||
public function testEnabledProfiler()
|
||||
{
|
||||
$container = $this->createContainerFromFile('profiler');
|
||||
|
@ -144,7 +144,52 @@ class TranslatorTest extends TestCase
|
||||
{
|
||||
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();
|
||||
|
||||
(new Translator($container, new MessageSelector(), array(), array('foo' => 'bar')));
|
||||
(new Translator($container, new MessageSelector(), [], ['foo' => 'bar']));
|
||||
}
|
||||
|
||||
/** @dataProvider getDebugModeAndCacheDirCombinations */
|
||||
public function testResourceFilesOptionLoadsBeforeOtherAddedResources($debug, $enableCache)
|
||||
{
|
||||
$someCatalogue = $this->getCatalogue('some_locale', array());
|
||||
|
||||
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
|
||||
|
||||
$loader->expects($this->at(0))
|
||||
->method('load')
|
||||
/* The "messages.some_locale.loader" is passed via the resource_file option and shall be loaded first */
|
||||
->with('messages.some_locale.loader', 'some_locale', 'messages')
|
||||
->willReturn($someCatalogue);
|
||||
|
||||
$loader->expects($this->at(1))
|
||||
->method('load')
|
||||
/* This resource is added by an addResource() call and shall be loaded after the resource_files */
|
||||
->with('second_resource.some_locale.loader', 'some_locale', 'messages')
|
||||
->willReturn($someCatalogue);
|
||||
|
||||
$options = array(
|
||||
'resource_files' => array('some_locale' => array('messages.some_locale.loader')),
|
||||
'debug' => $debug,
|
||||
);
|
||||
|
||||
if ($enableCache) {
|
||||
$options['cache_dir'] = $this->tmpDir;
|
||||
}
|
||||
|
||||
/** @var Translator $translator */
|
||||
$translator = $this->createTranslator($loader, $options);
|
||||
$translator->addResource('loader', 'second_resource.some_locale.loader', 'some_locale', 'messages');
|
||||
|
||||
$translator->trans('some_message', array(), null, 'some_locale');
|
||||
}
|
||||
|
||||
public function getDebugModeAndCacheDirCombinations()
|
||||
{
|
||||
return array(
|
||||
array(false, false),
|
||||
array(true, false),
|
||||
array(false, true),
|
||||
array(true, true),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getCatalogue($locale, $messages, $resources = array())
|
||||
|
@ -38,6 +38,14 @@ class Translator extends BaseTranslator implements WarmableInterface
|
||||
*/
|
||||
private $resourceLocales;
|
||||
|
||||
/**
|
||||
* Holds parameters from addResource() calls so we can defer the actual
|
||||
* parent::addResource() calls until initialize() is executed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $resources = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@ -66,9 +74,7 @@ class Translator extends BaseTranslator implements WarmableInterface
|
||||
|
||||
$this->options = array_merge($this->options, $options);
|
||||
$this->resourceLocales = array_keys($this->options['resource_files']);
|
||||
if (null !== $this->options['cache_dir'] && $this->options['debug']) {
|
||||
$this->loadResources();
|
||||
}
|
||||
$this->addResourceFiles($this->options['resource_files']);
|
||||
|
||||
parent::__construct($container->getParameter('kernel.default_locale'), $selector, $this->options['cache_dir'], $this->options['debug']);
|
||||
}
|
||||
@ -94,6 +100,11 @@ class Translator extends BaseTranslator implements WarmableInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function addResource($format, $resource, $locale, $domain = null)
|
||||
{
|
||||
$this->resources[] = array($format, $resource, $locale, $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -105,7 +116,12 @@ class Translator extends BaseTranslator implements WarmableInterface
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
$this->loadResources();
|
||||
foreach ($this->resources as $key => $params) {
|
||||
list($format, $resource, $locale, $domain) = $params;
|
||||
parent::addResource($format, $resource, $locale, $domain);
|
||||
}
|
||||
$this->resources = array();
|
||||
|
||||
foreach ($this->loaderIds as $id => $aliases) {
|
||||
foreach ($aliases as $alias) {
|
||||
$this->addLoader($alias, $this->container->get($id));
|
||||
@ -113,14 +129,13 @@ class Translator extends BaseTranslator implements WarmableInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function loadResources()
|
||||
private function addResourceFiles($filesByLocale)
|
||||
{
|
||||
foreach ($this->options['resource_files'] as $locale => $files) {
|
||||
foreach ($filesByLocale as $locale => $files) {
|
||||
foreach ($files as $key => $file) {
|
||||
// filename is domain.locale.format
|
||||
list($domain, $locale, $format) = explode('.', basename($file), 3);
|
||||
$this->addResource($format, $file, $locale, $domain);
|
||||
unset($this->options['resource_files'][$locale][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ class SecurityDataCollector extends DataCollector
|
||||
'logout_url' => $logoutUrl,
|
||||
'user' => $token->getUsername(),
|
||||
'roles' => $this->cloneVar(array_map(function (RoleInterface $role) { return $role->getRole(); }, $assignedRoles)),
|
||||
'inherited_roles' => $this->cloneVar(array_map(function (RoleInterface $role) { return $role->getRole(); }, $inheritedRoles)),
|
||||
'inherited_roles' => $this->cloneVar(array_unique(array_map(function (RoleInterface $role) { return $role->getRole(); }, $inheritedRoles))),
|
||||
'supports_role_hierarchy' => null !== $this->roleHierarchy,
|
||||
);
|
||||
}
|
||||
|
@ -166,6 +166,11 @@ class SecurityDataCollectorTest extends TestCase
|
||||
array('ROLE_ADMIN'),
|
||||
array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
|
||||
),
|
||||
array(
|
||||
array('ROLE_ADMIN', 'ROLE_OPERATOR'),
|
||||
array('ROLE_ADMIN', 'ROLE_OPERATOR'),
|
||||
array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -173,6 +178,7 @@ class SecurityDataCollectorTest extends TestCase
|
||||
{
|
||||
return new RoleHierarchy(array(
|
||||
'ROLE_ADMIN' => array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
|
||||
'ROLE_OPERATOR' => array('ROLE_USER'),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ class ExceptionController
|
||||
'logger' => $logger,
|
||||
'currentContent' => $currentContent,
|
||||
)
|
||||
));
|
||||
), 200, array('Content-Type' => $request->getMimeType($request->getRequestFormat()) ?: 'text/html'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,7 +123,7 @@ class ExceptionController
|
||||
// default to a generic HTML exception
|
||||
$request->setRequestFormat('html');
|
||||
|
||||
return sprintf('@Twig/Exception/%s.html.twig', $showException ? 'exception_full' : $name);
|
||||
return sprintf('@Twig/Exception/%s.html.twig', $name);
|
||||
}
|
||||
|
||||
// to be removed when the minimum required version of Twig is >= 3.0
|
||||
|
@ -96,6 +96,7 @@ class ExtensionPass implements CompilerPassInterface
|
||||
} else {
|
||||
$twigLoader->replaceArgument(1, $composerRootDir);
|
||||
$container->setAlias('twig.loader.filesystem', new Alias('twig.loader.native_filesystem', false));
|
||||
$container->removeDefinition('templating.engine.twig');
|
||||
}
|
||||
|
||||
if ($container->has('assets.packages')) {
|
||||
|
@ -76,7 +76,6 @@ class FilesystemLoader extends BaseFilesystemLoader
|
||||
}
|
||||
|
||||
$file = null;
|
||||
$previous = null;
|
||||
try {
|
||||
$file = parent::findTemplate($logicalName);
|
||||
} catch (LoaderError $e) {
|
||||
|
@ -22,14 +22,9 @@ class ExceptionControllerTest extends TestCase
|
||||
{
|
||||
public function testShowActionCanBeForcedToShowErrorPage()
|
||||
{
|
||||
$twig = new Environment(
|
||||
new ArrayLoader(array(
|
||||
'@Twig/Exception/error404.html.twig' => 'ok',
|
||||
))
|
||||
);
|
||||
$twig = $this->createTwigEnv(array('@Twig/Exception/error404.html.twig' => '<html>not found</html>'));
|
||||
|
||||
$request = Request::create('whatever', 'GET');
|
||||
$request->headers->set('X-Php-Ob-Level', 1);
|
||||
$request = $this->createRequest('html');
|
||||
$request->attributes->set('showException', false);
|
||||
$exception = FlattenException::create(new \Exception(), 404);
|
||||
$controller = new ExceptionController($twig, /* "showException" defaults to --> */ true);
|
||||
@ -37,25 +32,47 @@ class ExceptionControllerTest extends TestCase
|
||||
$response = $controller->showAction($request, $exception, null);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode()); // successful request
|
||||
$this->assertEquals('ok', $response->getContent()); // content of the error404.html template
|
||||
$this->assertEquals('<html>not found</html>', $response->getContent());
|
||||
}
|
||||
|
||||
public function testFallbackToHtmlIfNoTemplateForRequestedFormat()
|
||||
{
|
||||
$twig = new Environment(
|
||||
new ArrayLoader(array(
|
||||
'@Twig/Exception/error.html.twig' => 'html',
|
||||
))
|
||||
);
|
||||
$twig = $this->createTwigEnv(array('@Twig/Exception/error.html.twig' => '<html></html>'));
|
||||
|
||||
$request = Request::create('whatever');
|
||||
$request->headers->set('X-Php-Ob-Level', 1);
|
||||
$request->setRequestFormat('txt');
|
||||
$request = $this->createRequest('txt');
|
||||
$exception = FlattenException::create(new \Exception());
|
||||
$controller = new ExceptionController($twig, false);
|
||||
|
||||
$controller->showAction($request, $exception);
|
||||
|
||||
$this->assertEquals('html', $request->getRequestFormat());
|
||||
}
|
||||
|
||||
public function testResponseHasRequestedMimeType()
|
||||
{
|
||||
$twig = $this->createTwigEnv(array('@Twig/Exception/error.json.twig' => '{}'));
|
||||
|
||||
$request = $this->createRequest('json');
|
||||
$exception = FlattenException::create(new \Exception());
|
||||
$controller = new ExceptionController($twig, false);
|
||||
|
||||
$response = $controller->showAction($request, $exception);
|
||||
|
||||
$this->assertEquals('html', $request->getRequestFormat());
|
||||
$this->assertEquals('json', $request->getRequestFormat());
|
||||
$this->assertEquals($request->getMimeType('json'), $response->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
private function createRequest($requestFormat)
|
||||
{
|
||||
$request = Request::create('whatever');
|
||||
$request->headers->set('X-Php-Ob-Level', 1);
|
||||
$request->setRequestFormat($requestFormat);
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
private function createTwigEnv(array $templates)
|
||||
{
|
||||
return new Environment(new ArrayLoader($templates));
|
||||
}
|
||||
}
|
||||
|
@ -136,9 +136,10 @@ class TwigExtensionTest extends TestCase
|
||||
|
||||
$calls = $container->getDefinition('twig')->getMethodCalls();
|
||||
foreach (array_slice($calls, 2) as $call) {
|
||||
list($name, $value) = each($globals);
|
||||
$this->assertEquals($name, $call[1][0]);
|
||||
$this->assertSame($value, $call[1][1]);
|
||||
$this->assertEquals(key($globals), $call[1][0]);
|
||||
$this->assertSame(current($globals), $call[1][1]);
|
||||
|
||||
next($globals);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,6 +369,15 @@
|
||||
100% { background: #222; }
|
||||
}
|
||||
|
||||
.sf-toolbar-block.sf-toolbar-block-dump {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.sf-toolbar-block.sf-toolbar-block-dump .sf-toolbar-info {
|
||||
max-width: none;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.sf-toolbar-block-dump pre.sf-dump {
|
||||
background-color: #222;
|
||||
border-color: #777;
|
||||
|
@ -96,8 +96,17 @@
|
||||
})
|
||||
},
|
||||
function(xhr) {
|
||||
var errorToolbarHtml = '
|
||||
<style>
|
||||
.sfErrorToolbar { background: #222; bottom: 0; color: #f5f5f5; font: 13px/36px Arial, sans-serif; height: 36px; padding: 0 15px; position: fixed; width: 100%; }
|
||||
.sfErrorToolbar a { color: #99cdd8; margin-left: 5px; text-decoration: underline; }
|
||||
.sfErrorToolbar a:hover { text-decoration: none; }
|
||||
</style>
|
||||
<div class="sfErrorToolbar">An error occurred while loading the web debug toolbar. <a href="{{ path("_profiler", { "token": token }) }}">Open the web profiler.</a></div>
|
||||
';
|
||||
|
||||
if (xhr.status !== 0) {
|
||||
confirm('An error occurred while loading the web debug toolbar (' + xhr.status + ': ' + xhr.statusText + ').\n\nDo you want to open the profiler?') && (window.location = '{{ path("_profiler", { "token": token }) }}');
|
||||
window.document.body.insertAdjacentHTML('beforeend', errorToolbarHtml);
|
||||
}
|
||||
},
|
||||
{ maxTries: 5 }
|
||||
|
@ -54,7 +54,7 @@
|
||||
<tbody>
|
||||
{% for trace in traces %}
|
||||
<tr class="{{ trace.level == 1 ? 'status-warning' : trace.level == 2 ? 'status-success' }}">
|
||||
<td class="font-normal text-muted">{{ loop.index }}</td>
|
||||
<td class="font-normal text-muted nowrap">{{ loop.index }}</td>
|
||||
<td>{{ trace.name }}</td>
|
||||
<td>{{ trace.path }}</td>
|
||||
<td class="font-normal">
|
||||
|
@ -468,6 +468,7 @@ abstract class Client
|
||||
|
||||
if (-1 !== $this->maxRedirects) {
|
||||
if ($this->redirectCount > $this->maxRedirects) {
|
||||
$this->redirectCount = 0;
|
||||
throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects));
|
||||
}
|
||||
}
|
||||
|
@ -124,8 +124,7 @@ class QuestionHelper extends Helper
|
||||
*
|
||||
* @return bool|mixed|null|string
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
|
||||
*/
|
||||
private function doAsk(OutputInterface $output, Question $question)
|
||||
{
|
||||
@ -139,7 +138,7 @@ class QuestionHelper extends Helper
|
||||
if ($question->isHidden()) {
|
||||
try {
|
||||
$ret = trim($this->getHiddenResponse($output, $inputStream));
|
||||
} catch (\RuntimeException $e) {
|
||||
} catch (RuntimeException $e) {
|
||||
if (!$question->isHiddenFallback()) {
|
||||
throw $e;
|
||||
}
|
||||
|
@ -276,6 +276,13 @@ class Filesystem
|
||||
}
|
||||
|
||||
if (true !== @rename($origin, $target)) {
|
||||
if (is_dir($origin)) {
|
||||
// See https://bugs.php.net/bug.php?id=54097 & http://php.net/manual/en/function.rename.php#113943
|
||||
$this->mirror($origin, $target, null, array('override' => $overwrite, 'delete' => $overwrite));
|
||||
$this->remove($origin);
|
||||
|
||||
return;
|
||||
}
|
||||
throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target);
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class LockHandlerTest extends TestCase
|
||||
$this->markTestSkipped('This test cannot run on Windows.');
|
||||
}
|
||||
|
||||
$lockPath = sys_get_temp_dir().'/'.uniqid();
|
||||
$lockPath = sys_get_temp_dir().'/'.uniqid('', true);
|
||||
$e = null;
|
||||
$wrongMessage = null;
|
||||
|
||||
|
@ -54,7 +54,7 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
|
||||
|
||||
if (!is_callable($preferredChoices) && !empty($preferredChoices)) {
|
||||
$preferredChoices = function ($choice) use ($preferredChoices) {
|
||||
return false !== array_search($choice, $preferredChoices, true);
|
||||
return in_array($choice, $preferredChoices, true);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuild
|
||||
* @param string|null $type
|
||||
* @param array $options
|
||||
*
|
||||
* @return $this
|
||||
* @return self
|
||||
*/
|
||||
public function add($child, $type = null, array $options = array());
|
||||
|
||||
@ -58,7 +58,7 @@ interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuild
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
* @return self
|
||||
*/
|
||||
public function remove($name);
|
||||
|
||||
|
@ -305,6 +305,6 @@ class FormRenderer implements FormRendererInterface
|
||||
*/
|
||||
public function humanize($text)
|
||||
{
|
||||
return ucfirst(trim(strtolower(preg_replace(array('/([A-Z])/', '/[_\s]+/'), array('_$1', ' '), $text))));
|
||||
return ucfirst(strtolower(trim(preg_replace(array('/([A-Z])/', '/[_\s]+/'), array('_$1', ' '), $text))));
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ class FormView implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
*/
|
||||
private $rendered = false;
|
||||
|
||||
private $methodRendered = false;
|
||||
|
||||
public function __construct(FormView $parent = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
@ -90,6 +92,19 @@ class FormView implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isMethodRendered()
|
||||
{
|
||||
return $this->methodRendered;
|
||||
}
|
||||
|
||||
public function setMethodRendered()
|
||||
{
|
||||
$this->methodRendered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a child by name (implements \ArrayAccess).
|
||||
*
|
||||
|
@ -18,6 +18,8 @@ namespace Symfony\Component\HttpFoundation;
|
||||
*/
|
||||
class IpUtils
|
||||
{
|
||||
private static $checkedIps = array();
|
||||
|
||||
/**
|
||||
* This class should not be instantiated.
|
||||
*/
|
||||
@ -61,26 +63,31 @@ class IpUtils
|
||||
*/
|
||||
public static function checkIp4($requestIp, $ip)
|
||||
{
|
||||
$cacheKey = $requestIp.'-'.$ip;
|
||||
if (isset(self::$checkedIps[$cacheKey])) {
|
||||
return self::$checkedIps[$cacheKey];
|
||||
}
|
||||
|
||||
if (!filter_var($requestIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
return false;
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
|
||||
if (false !== strpos($ip, '/')) {
|
||||
list($address, $netmask) = explode('/', $ip, 2);
|
||||
|
||||
if ($netmask === '0') {
|
||||
return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
|
||||
return self::$checkedIps[$cacheKey] = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
|
||||
}
|
||||
|
||||
if ($netmask < 0 || $netmask > 32) {
|
||||
return false;
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
} else {
|
||||
$address = $ip;
|
||||
$netmask = 32;
|
||||
}
|
||||
|
||||
return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
|
||||
return self::$checkedIps[$cacheKey] = 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,6 +107,11 @@ class IpUtils
|
||||
*/
|
||||
public static function checkIp6($requestIp, $ip)
|
||||
{
|
||||
$cacheKey = $requestIp.'-'.$ip;
|
||||
if (isset(self::$checkedIps[$cacheKey])) {
|
||||
return self::$checkedIps[$cacheKey];
|
||||
}
|
||||
|
||||
if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
|
||||
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
|
||||
}
|
||||
@ -108,7 +120,7 @@ class IpUtils
|
||||
list($address, $netmask) = explode('/', $ip, 2);
|
||||
|
||||
if ($netmask < 1 || $netmask > 128) {
|
||||
return false;
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
} else {
|
||||
$address = $ip;
|
||||
@ -119,7 +131,7 @@ class IpUtils
|
||||
$bytesTest = unpack('n*', @inet_pton($requestIp));
|
||||
|
||||
if (!$bytesAddr || !$bytesTest) {
|
||||
return false;
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
|
||||
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
|
||||
@ -127,10 +139,10 @@ class IpUtils
|
||||
$left = ($left <= 16) ? $left : 16;
|
||||
$mask = ~(0xffff >> $left) & 0xffff;
|
||||
if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
|
||||
return false;
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return self::$checkedIps[$cacheKey] = true;
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,10 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
* upload_progress.freq, "1%"
|
||||
* upload_progress.min-freq, "1"
|
||||
* url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
|
||||
* sid_length, "32"
|
||||
* sid_bits_per_character, "5"
|
||||
* trans_sid_hosts, $_SERVER['HTTP_HOST']
|
||||
* trans_sid_tags, "a=href,area=href,frame=src,form="
|
||||
*
|
||||
* @param array $options Session configuration options
|
||||
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
|
||||
@ -316,6 +320,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
|
||||
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
|
||||
'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
|
||||
'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
|
||||
));
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
|
@ -39,7 +39,7 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
|
||||
*/
|
||||
public function add(Response $response)
|
||||
{
|
||||
if ($response->isValidateable()) {
|
||||
if (!$response->isFresh() || !$response->isCacheable()) {
|
||||
$this->cacheable = false;
|
||||
} else {
|
||||
$maxAge = $response->getMaxAge();
|
||||
@ -70,6 +70,9 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
|
||||
if ($response->isValidateable()) {
|
||||
$response->setEtag(null);
|
||||
$response->setLastModified(null);
|
||||
}
|
||||
|
||||
if (!$response->isFresh()) {
|
||||
$this->cacheable = false;
|
||||
}
|
||||
|
||||
|
@ -75,4 +75,148 @@ class ResponseCacheStrategyTest extends TestCase
|
||||
|
||||
$this->assertFalse($response->headers->hasCacheControlDirective('s-maxage'));
|
||||
}
|
||||
|
||||
public function testMasterResponseNotCacheableWhenEmbeddedResponseRequiresValidation()
|
||||
{
|
||||
$cacheStrategy = new ResponseCacheStrategy();
|
||||
|
||||
$embeddedResponse = new Response();
|
||||
$embeddedResponse->setLastModified(new \DateTime());
|
||||
$cacheStrategy->add($embeddedResponse);
|
||||
|
||||
$masterResponse = new Response();
|
||||
$masterResponse->setSharedMaxAge(3600);
|
||||
$cacheStrategy->update($masterResponse);
|
||||
|
||||
$this->assertTrue($masterResponse->headers->hasCacheControlDirective('no-cache'));
|
||||
$this->assertTrue($masterResponse->headers->hasCacheControlDirective('must-revalidate'));
|
||||
$this->assertFalse($masterResponse->isFresh());
|
||||
}
|
||||
|
||||
public function testValidationOnMasterResponseIsNotPossibleWhenItContainsEmbeddedResponses()
|
||||
{
|
||||
$cacheStrategy = new ResponseCacheStrategy();
|
||||
|
||||
// This master response uses the "validation" model
|
||||
$masterResponse = new Response();
|
||||
$masterResponse->setLastModified(new \DateTime());
|
||||
$masterResponse->setEtag('foo');
|
||||
|
||||
// Embedded response uses "expiry" model
|
||||
$embeddedResponse = new Response();
|
||||
$masterResponse->setSharedMaxAge(3600);
|
||||
$cacheStrategy->add($embeddedResponse);
|
||||
|
||||
$cacheStrategy->update($masterResponse);
|
||||
|
||||
$this->assertFalse($masterResponse->isValidateable());
|
||||
$this->assertFalse($masterResponse->headers->has('Last-Modified'));
|
||||
$this->assertFalse($masterResponse->headers->has('ETag'));
|
||||
$this->assertTrue($masterResponse->headers->hasCacheControlDirective('no-cache'));
|
||||
$this->assertTrue($masterResponse->headers->hasCacheControlDirective('must-revalidate'));
|
||||
}
|
||||
|
||||
public function testMasterResponseWithValidationIsUnchangedWhenThereIsNoEmbeddedResponse()
|
||||
{
|
||||
$cacheStrategy = new ResponseCacheStrategy();
|
||||
|
||||
$masterResponse = new Response();
|
||||
$masterResponse->setLastModified(new \DateTime());
|
||||
$cacheStrategy->update($masterResponse);
|
||||
|
||||
$this->assertTrue($masterResponse->isValidateable());
|
||||
}
|
||||
|
||||
public function testMasterResponseWithExpirationIsUnchangedWhenThereIsNoEmbeddedResponse()
|
||||
{
|
||||
$cacheStrategy = new ResponseCacheStrategy();
|
||||
|
||||
$masterResponse = new Response();
|
||||
$masterResponse->setSharedMaxAge(3600);
|
||||
$cacheStrategy->update($masterResponse);
|
||||
|
||||
$this->assertTrue($masterResponse->isFresh());
|
||||
}
|
||||
|
||||
public function testMasterResponseIsNotCacheableWhenEmbeddedResponseIsNotCacheable()
|
||||
{
|
||||
$cacheStrategy = new ResponseCacheStrategy();
|
||||
|
||||
$masterResponse = new Response();
|
||||
$masterResponse->setSharedMaxAge(3600); // Public, cacheable
|
||||
|
||||
/* This response has no validation or expiration information.
|
||||
That makes it uncacheable, it is always stale.
|
||||
(It does *not* make this private, though.) */
|
||||
$embeddedResponse = new Response();
|
||||
$this->assertFalse($embeddedResponse->isFresh()); // not fresh, as no lifetime is provided
|
||||
|
||||
$cacheStrategy->add($embeddedResponse);
|
||||
$cacheStrategy->update($masterResponse);
|
||||
|
||||
$this->assertTrue($masterResponse->headers->hasCacheControlDirective('no-cache'));
|
||||
$this->assertTrue($masterResponse->headers->hasCacheControlDirective('must-revalidate'));
|
||||
$this->assertFalse($masterResponse->isFresh());
|
||||
}
|
||||
|
||||
public function testEmbeddingPrivateResponseMakesMainResponsePrivate()
|
||||
{
|
||||
$cacheStrategy = new ResponseCacheStrategy();
|
||||
|
||||
$masterResponse = new Response();
|
||||
$masterResponse->setSharedMaxAge(3600); // public, cacheable
|
||||
|
||||
// The embedded response might for example contain per-user data that remains valid for 60 seconds
|
||||
$embeddedResponse = new Response();
|
||||
$embeddedResponse->setPrivate();
|
||||
$embeddedResponse->setMaxAge(60); // this would implicitly set "private" as well, but let's be explicit
|
||||
|
||||
$cacheStrategy->add($embeddedResponse);
|
||||
$cacheStrategy->update($masterResponse);
|
||||
|
||||
$this->assertTrue($masterResponse->headers->hasCacheControlDirective('private'));
|
||||
// Not sure if we should pass "max-age: 60" in this case, as long as the response is private and
|
||||
// that's the more conservative of both the master and embedded response...?
|
||||
}
|
||||
|
||||
public function testResponseIsExiprableWhenEmbeddedResponseCombinesExpiryAndValidation()
|
||||
{
|
||||
/* When "expiration wins over validation" (https://symfony.com/doc/current/http_cache/validation.html)
|
||||
* and both the main and embedded response provide s-maxage, then the more restricting value of both
|
||||
* should be fine, regardless of whether the embedded response can be validated later on or must be
|
||||
* completely regenerated.
|
||||
*/
|
||||
$cacheStrategy = new ResponseCacheStrategy();
|
||||
|
||||
$masterResponse = new Response();
|
||||
$masterResponse->setSharedMaxAge(3600);
|
||||
|
||||
$embeddedResponse = new Response();
|
||||
$embeddedResponse->setSharedMaxAge(60);
|
||||
$embeddedResponse->setEtag('foo');
|
||||
|
||||
$cacheStrategy->add($embeddedResponse);
|
||||
$cacheStrategy->update($masterResponse);
|
||||
|
||||
$this->assertSame('60', $masterResponse->headers->getCacheControlDirective('s-maxage'));
|
||||
}
|
||||
|
||||
public function testResponseIsExpirableButNotValidateableWhenMasterResponseCombinesExpirationAndValidation()
|
||||
{
|
||||
$cacheStrategy = new ResponseCacheStrategy();
|
||||
|
||||
$masterResponse = new Response();
|
||||
$masterResponse->setSharedMaxAge(3600);
|
||||
$masterResponse->setEtag('foo');
|
||||
$masterResponse->setLastModified(new \DateTime());
|
||||
|
||||
$embeddedResponse = new Response();
|
||||
$embeddedResponse->setSharedMaxAge(60);
|
||||
|
||||
$cacheStrategy->add($embeddedResponse);
|
||||
$cacheStrategy->update($masterResponse);
|
||||
|
||||
$this->assertSame('60', $masterResponse->headers->getCacheControlDirective('s-maxage'));
|
||||
$this->assertFalse($masterResponse->isValidateable());
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ class XmlFileLoader extends FileLoader
|
||||
$condition = trim($n->textContent);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement" or "option".', $n->localName, $path));
|
||||
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement", "option" or "condition".', $n->localName, $path));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ abstract class RedirectableUrlMatcher extends UrlMatcher implements Redirectable
|
||||
protected function handleRouteRequirements($pathinfo, $name, Route $route)
|
||||
{
|
||||
// expression condition
|
||||
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request))) {
|
||||
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) {
|
||||
return array(self::REQUIREMENT_MISMATCH, null);
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ class TraceableUrlMatcher extends UrlMatcher
|
||||
|
||||
// check condition
|
||||
if ($condition = $route->getCondition()) {
|
||||
if (!$this->getExpressionLanguage()->evaluate($condition, array('context' => $this->context, 'request' => $this->request))) {
|
||||
if (!$this->getExpressionLanguage()->evaluate($condition, array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) {
|
||||
$this->addTrace(sprintf('Condition "%s" does not evaluate to "true"', $condition), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
|
||||
continue;
|
||||
|
@ -207,7 +207,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
protected function handleRouteRequirements($pathinfo, $name, Route $route)
|
||||
{
|
||||
// expression condition
|
||||
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request))) {
|
||||
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) {
|
||||
return array(self::REQUIREMENT_MISMATCH, null);
|
||||
}
|
||||
|
||||
@ -248,4 +248,19 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function createRequest($pathinfo)
|
||||
{
|
||||
if (!class_exists('Symfony\Component\HttpFoundation\Request')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Request::create($this->context->getScheme().'://'.$this->context->getHost().$this->context->getBaseUrl().$pathinfo, $this->context->getMethod(), $this->context->getParameters(), array(), array(), array(
|
||||
'SCRIPT_FILENAME' => $this->context->getBaseUrl(),
|
||||
'SCRIPT_NAME' => $this->context->getBaseUrl(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -337,6 +337,16 @@ class UrlMatcherTest extends TestCase
|
||||
$matcher->match('/foo');
|
||||
}
|
||||
|
||||
public function testRequestCondition()
|
||||
{
|
||||
$coll = new RouteCollection();
|
||||
$route = new Route('/foo/{bar}');
|
||||
$route->setCondition('request.getBaseUrl() == "/sub/front.php" and request.getPathInfo() == "/foo/bar"');
|
||||
$coll->add('foo', $route);
|
||||
$matcher = new UrlMatcher($coll, new RequestContext('/sub/front.php'));
|
||||
$this->assertEquals(array('bar' => 'bar', '_route' => 'foo'), $matcher->match('/foo/bar'));
|
||||
}
|
||||
|
||||
public function testDecodeOnce()
|
||||
{
|
||||
$coll = new RouteCollection();
|
||||
|
@ -161,7 +161,7 @@ class SwitchUserListener implements ListenerInterface
|
||||
*/
|
||||
private function attemptExitUser(Request $request)
|
||||
{
|
||||
if (false === $original = $this->getOriginalToken($this->tokenStorage->getToken())) {
|
||||
if (null === ($currentToken = $this->tokenStorage->getToken()) || false === $original = $this->getOriginalToken($currentToken)) {
|
||||
throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,17 @@ class SwitchUserListenerTest extends TestCase
|
||||
$this->assertNull($this->tokenStorage->getToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException
|
||||
*/
|
||||
public function testExitUserThrowsAuthenticationExceptionIfNoCurrentToken()
|
||||
{
|
||||
$this->tokenStorage->setToken(null);
|
||||
$this->request->query->set('_switch_user', '_exit');
|
||||
$listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
|
||||
$listener->handle($this->event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException
|
||||
*/
|
||||
|
@ -97,7 +97,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
|
||||
*/
|
||||
public function getFallbackLocales()
|
||||
{
|
||||
if ($this->translator instanceof Translator) {
|
||||
if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
|
||||
return $this->translator->getFallbackLocales();
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ abstract class FileLoader extends ArrayLoader
|
||||
return $catalogue;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* @param string $resource
|
||||
*
|
||||
* @return array
|
||||
|
@ -96,7 +96,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
|
||||
*/
|
||||
public function getFallbackLocales()
|
||||
{
|
||||
if ($this->translator instanceof Translator) {
|
||||
if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
|
||||
return $this->translator->getFallbackLocales();
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ class ResourceCaster
|
||||
|
||||
public static function castStreamContext($stream, array $a, Stub $stub, $isNested)
|
||||
{
|
||||
return stream_context_get_params($stream);
|
||||
return @stream_context_get_params($stream) ?: $a;
|
||||
}
|
||||
|
||||
public static function castGd($gd, array $a, Stub $stub, $isNested)
|
||||
|
@ -184,7 +184,7 @@ class SplCaster
|
||||
$storage = array();
|
||||
unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967
|
||||
|
||||
foreach ($c as $obj) {
|
||||
foreach (clone $c as $obj) {
|
||||
$storage[spl_object_hash($obj)] = array(
|
||||
'object' => $obj,
|
||||
'info' => $c->getInfo(),
|
||||
|
Reference in New Issue
Block a user