diff --git a/CHANGELOG-3.3.md b/CHANGELOG-3.3.md index 17b9ed1ed3..8a6626da32 100644 --- a/CHANGELOG-3.3.md +++ b/CHANGELOG-3.3.md @@ -7,6 +7,36 @@ in 3.3 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.3.0...v3.3.1 +* 3.3.1 (2017-06-05) + + * bug #23067 [HttpFoundation][FrameworkBundle] Revert "trusted proxies" BC break (nicolas-grekas) + * bug #23065 [Cache] Fallback to positional when keyed results are broken (nicolas-grekas) + * bug #22981 [DependencyInjection] Fix named args support in ChildDefinition (dunglas) + * bug #23050 [Form][Profiler] Fixes form collector triggering deprecations (ogizanagi) + * bug #22971 [Profiler] Fix code excerpt wrapping (ogizanagi) + * bug #23049 [FrameworkBundle] mitigate BC break with empty trusted_proxies (xabbuh) + * bug #23045 [Cache] fix Redis scheme detection (xabbuh) + * bug #23013 Parse the _controller format in sub-requests (weaverryan) + * bug #23015 [PhpUnitBridge] Fix detection of PHPUnit 5 (enumag) + * bug #23041 [Config] Always protected ClassExistenceResource against bad parents (nicolas-grekas) + * bug #22988 [PropertyInfo][DoctrineBridge] The bigint Doctrine's type must be converted to string (dunglas) + * bug #23014 Fix optional cache warmers are always instantiated whereas they should be lazy-loaded (romainneutron) + * feature #23022 [Di] Remove closure-proxy arguments (nicolas-grekas) + * bug #23024 [EventDispatcher] Fix ContainerAwareEventDispatcher::hasListeners(null) (nicolas-grekas) + * bug #23008 [EventDispatcher] Handle laziness internally instead of relying on ClosureProxyArgument (nicolas-grekas) + * bug #23018 [FrameworkBundle] Fix CacheCollectorPass priority (chalasr) + * bug #23009 [Routing] Allow GET requests to be redirected. Fixes #23004 (frankdejonge) + * bug #22996 [Form] Fix \IntlDateFormatter timezone parameter usage to bypass PHP bug #66323 (romainneutron) + * bug #22965 [Cache] Ignore missing annotations.php (ro0NL) + * bug #22993 [DI] Autowiring exception thrown when inlined service is removed (weaverryan) + * bug #22999 Better DI type deprecation message (weaverryan) + * bug #22985 [Config] Allow empty globs (nicolas-grekas) + * bug #22961 [HttpKernel] Support unknown format in LoggerDataCollector (iltar) + * bug #22991 [DI] Don't throw Autowire exception for removed service with private __construct (weaverryan) + * bug #22968 [Profiler] Fix text selection & click on file links on exception pages (ogizanagi) + * bug #22994 Harden the debugging of Twig filters and functions (stof) + * bug #22960 [Cache] Fix decoration of TagAware adapters in dev (chalasr) + * 3.3.0 (2017-05-29) * bug #22940 [Config] Fallback to regular import when glob fails (nicolas-grekas) diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php index c69ca843e8..b3c89d18eb 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php @@ -14,6 +14,7 @@ namespace Symfony\Bridge\Twig\NodeVisitor; use Symfony\Bridge\Twig\Node\TransNode; use Symfony\Bridge\Twig\Node\TransDefaultDomainNode; use Twig\Environment; +use Twig\Node\BlockNode; use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\AssignNameExpression; use Twig\Node\Expression\ConstantExpression; @@ -21,6 +22,7 @@ use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\ModuleNode; use Twig\Node\Node; +use Twig\Node\SetNode; use Twig\NodeVisitor\AbstractNodeVisitor; /** @@ -48,7 +50,7 @@ class TranslationDefaultDomainNodeVisitor extends AbstractNodeVisitor */ protected function doEnterNode(Node $node, Environment $env) { - if ($node instanceof Node_Block || $node instanceof ModuleNode) { + if ($node instanceof BlockNode || $node instanceof ModuleNode) { $this->scope = $this->scope->enter(); } @@ -62,7 +64,7 @@ class TranslationDefaultDomainNodeVisitor extends AbstractNodeVisitor $name = new AssignNameExpression($var, $node->getTemplateLine()); $this->scope->set('domain', new NameExpression($var, $node->getTemplateLine())); - return new Node_Set(false, new Node(array($name)), new Node(array($node->getNode('expr'))), $node->getTemplateLine()); + return new SetNode(false, new Node(array($name)), new Node(array($node->getNode('expr'))), $node->getTemplateLine()); } } @@ -104,7 +106,7 @@ class TranslationDefaultDomainNodeVisitor extends AbstractNodeVisitor return false; } - if ($node instanceof Node_Block || $node instanceof ModuleNode) { + if ($node instanceof BlockNode || $node instanceof ModuleNode) { $this->scope = $this->scope->leave(); } diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 65a97e13d8..3fd2c62269 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -24,7 +24,7 @@ CHANGELOG * Not defining the `type` option of the `framework.workflows.*` configuration entries is deprecated. The default value will be `state_machine` in Symfony 4.0. * Deprecated the `CompilerDebugDumpPass` class - * [BC BREAK] Removed the "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter + * Deprecated the "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter * Added a new new version strategy option called json_manifest_path that allows you to use the `JsonManifestVersionStrategy`. * Added `Symfony\Bundle\FrameworkBundle\Controller\AbstractController`. It provides diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php index 256bebebe8..a650960c94 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php @@ -19,6 +19,8 @@ use Symfony\Component\Routing\RouterInterface; * Generates the router matcher and generator classes. * * @author Fabien Potencier + * + * @final since version 3.4, to be given a container instead in 4.0 */ class RouterCacheWarmer implements CacheWarmerInterface { diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php index 33f9465044..eab97ce9db 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php @@ -11,7 +11,8 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -21,26 +22,15 @@ use Symfony\Component\Translation\TranslatorInterface; * * @author Xavier Leune */ -class TranslationsCacheWarmer implements CacheWarmerInterface +class TranslationsCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface { private $container; private $translator; - /** - * TranslationsCacheWarmer constructor. - * - * @param ContainerInterface|TranslatorInterface $container - */ - public function __construct($container) + public function __construct(ContainerInterface $container) { // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. - if ($container instanceof ContainerInterface) { - $this->container = $container; - } elseif ($container instanceof TranslatorInterface) { - $this->translator = $container; - } else { - throw new \InvalidArgumentException(sprintf('%s only accepts instance of Symfony\Component\DependencyInjection\ContainerInterface or Symfony\Component\Translation\TranslatorInterface as first argument.', __CLASS__)); - } + $this->container = $container; } /** @@ -64,4 +54,14 @@ class TranslationsCacheWarmer implements CacheWarmerInterface { return true; } + + /** + * {@inheritdoc} + */ + public static function getSubscribedServices() + { + return array( + 'translator' => TranslatorInterface::class, + ); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index acaec0bf5d..206b01e4f0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -307,11 +307,4 @@ abstract class Descriptor implements DescriptorInterface return $serviceIds; } - - protected function formatClosure(\Closure $closure) - { - $r = new \ReflectionFunction($closure); - - return 'closure'; - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 892a5069d7..dccca775c9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -366,7 +366,7 @@ class JsonDescriptor extends Descriptor } if ($callable instanceof \Closure) { - $data['type'] = $this->formatClosure($callable); + $data['type'] = 'closure'; return $data; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index ddd3328c8b..4bf3c5d5a3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -345,8 +345,7 @@ class MarkdownDescriptor extends Descriptor } if ($callable instanceof \Closure) { - $formatted = $this->formatClosure($callable); - $string .= "\n- Type: `$formatted`"; + $string .= "\n- Type: `closure`"; return $this->write($string."\n"); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 8c9160fd29..c28cf12bd3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -470,13 +470,7 @@ class TextDescriptor extends Descriptor } if ($callable instanceof \Closure) { - $formatted = $this->formatClosure($callable); - - if ('closure' === $formatted) { - return '\Closure()'; - } - - return $formatted.'()'; + return '\Closure()'; } if (method_exists($callable, '__invoke')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index f1770d8c3c..5baa8de11c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -593,7 +593,7 @@ class XmlDescriptor extends Descriptor } if ($callable instanceof \Closure) { - $callableXML->setAttribute('type', $this->formatClosure($callable)); + $callableXML->setAttribute('type', 'closure'); return $dom; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml index 5f98febda6..4f8999d0cb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml @@ -123,8 +123,9 @@ - + + diff --git a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php index 9a60a92e85..cdd3a5d740 100644 --- a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php +++ b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php @@ -11,7 +11,8 @@ namespace Symfony\Bundle\TwigBundle\CacheWarmer; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Twig\Environment; use Twig\Error\Error; @@ -21,29 +22,16 @@ use Twig\Error\Error; * * @author Fabien Potencier */ -class TemplateCacheWarmer implements CacheWarmerInterface +class TemplateCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface { private $container; private $twig; private $iterator; - /** - * TemplateCacheWarmer constructor. - * - * @param ContainerInterface|Environment $container - * @param \Traversable $iterator - */ - public function __construct($container, \Traversable $iterator) + public function __construct(ContainerInterface $container, \Traversable $iterator) { // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. - if ($container instanceof ContainerInterface) { - $this->container = $container; - } elseif ($container instanceof Environment) { - $this->twig = $container; - } else { - throw new \InvalidArgumentException(sprintf('%s only accepts instance of Symfony\Component\DependencyInjection\ContainerInterface or Environment as first argument.', __CLASS__)); - } - + $this->container = $container; $this->iterator = $iterator; } @@ -73,4 +61,14 @@ class TemplateCacheWarmer implements CacheWarmerInterface { return true; } + + /** + * {@inheritdoc} + */ + public static function getSubscribedServices() + { + return array( + 'twig' => Environment::class, + ); + } } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index 310ab14063..bf53c2b2b2 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -45,7 +45,8 @@ - + + diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig index 1c8c76e78d..cb455274ee 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig @@ -95,7 +95,7 @@ header .container { display: flex; justify-content: space-between; } .trace-head .icon svg { height: 24px; width: 24px; } .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } - +.trace-details { table-layout: fixed; } .trace-line { position: relative; padding-left: 36px; } .trace-line.sf-toggle:hover { background: #F5F5F5; } .trace-line a { color: #222; } @@ -108,12 +108,12 @@ header .container { display: flex; justify-content: space-between; } .trace-method { color: #B0413E; color: #222; font-weight: bold; color: #B0413E; } .trace-arguments { color: #222; color: #999; font-weight: normal; color: #795da3; color: #777; padding-left: 2px; } -.trace-code { background: #FFF; font-size: 12px; margin: 10px 0 2px; padding: 10px; } -.trace-code ol { margin: 0; } -.trace-code li { color: #969896; margin: 0; padding-left: 10px; } -.trace-code li + li { margin-top: 10px; } -.trace-code li.selected { background: #F8EEC7; padding: 3px 0 3px 10px; } -.trace-code li code { color: #222; } +.trace-code { background: #FFF; font-size: 12px; margin: 10px 0 2px; padding: 10px; overflow-x: auto; } +.trace-code ol { margin: 0; float: left; } +.trace-code li { color: #969896; margin: 0; padding-left: 10px; float: left; width: 100%; } +.trace-code li + li { margin-top: 5px; } +.trace-code li.selected { background: #F8EEC7; padding: 3px 0 3px 10px; margin-top: 2px; } +.trace-code li code { color: #222; white-space: nowrap; } .trace-as-text .stacktrace { line-height: 1.8; margin: 0 0 15px; white-space: pre-wrap; } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index 2f8b83049d..02b77319ac 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -525,7 +525,7 @@ Model Format {% if data.default_data.model is defined %} - {{ profiler_dump(data.default_data.model) }} + {{ profiler_dump(data.default_data.seek('model')) }} {% else %} same as normalized format {% endif %} @@ -533,13 +533,13 @@ Normalized Format - {{ profiler_dump(data.default_data.norm) }} + {{ profiler_dump(data.default_data.seek('norm')) }} View Format {% if data.default_data.view is defined %} - {{ profiler_dump(data.default_data.view) }} + {{ profiler_dump(data.default_data.seek('view')) }} {% else %} same as normalized format {% endif %} @@ -571,7 +571,7 @@ View Format {% if data.submitted_data.view is defined %} - {{ profiler_dump(data.submitted_data.view) }} + {{ profiler_dump(data.submitted_data.seek('view')) }} {% else %} same as normalized format {% endif %} @@ -579,13 +579,13 @@ Normalized Format - {{ profiler_dump(data.submitted_data.norm) }} + {{ profiler_dump(data.submitted_data.seek('norm')) }} Model Format {% if data.submitted_data.model is defined %} - {{ profiler_dump(data.submitted_data.model) }} + {{ profiler_dump(data.submitted_data.seek('model')) }} {% else %} same as normalized format {% endif %} @@ -630,7 +630,7 @@ {% if resolved_option_value == option_value %} same as passed value {% else %} - {{ profiler_dump(data.resolved_options[option]) }} + {{ profiler_dump(data.resolved_options.seek(option)) }} {% endif %} diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php index c761b9a201..e4fa4c97b7 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php @@ -266,6 +266,9 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface try { foreach ($items as $id => $value) { + if (!isset($keys[$id])) { + $id = key($keys); + } $key = $keys[$id]; unset($keys[$id]); yield $key => $f($key, $value, true); diff --git a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php b/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php index 5c8784e69c..3acf8bdb86 100644 --- a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php @@ -19,6 +19,18 @@ class MemcachedAdapter extends AbstractAdapter protected $maxIdLength = 250; + /** + * Constructor. + * + * Using a MemcachedAdapter with a TagAwareAdapter for storing tags is discouraged. + * Using a RedisAdapter is recommended instead. If you cannot do otherwise, be aware that: + * - the Memcached::OPT_BINARY_PROTOCOL must be enabled + * (that's the default when using MemcachedAdapter::createConnection()); + * - tags eviction by Memcached's LRU algorithm will break by-tags invalidation; + * your Memcached memory should be large enough to never trigger LRU. + * + * Using a MemcachedAdapter as a pure items store is fine. + */ public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0) { $this->init($client, $namespace, $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index 1d9dc7ce57..1e0617ebe2 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -281,7 +281,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface foreach ($items as $key => $item) { if (!$tagKeys) { - yield $key => $f($item, self::TAGS_PREFIX.$key, $itemTags); + yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); continue; } if (!isset($tagKeys[$key])) { @@ -306,7 +306,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface $tagVersions = $tagKeys = null; foreach ($bufferedItems as $key => $item) { - yield $key => $f($item, self::TAGS_PREFIX.$key, $itemTags); + yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); } $bufferedItems = null; } diff --git a/src/Symfony/Component/Cache/Simple/AbstractCache.php b/src/Symfony/Component/Cache/Simple/AbstractCache.php index 4c44b9b323..bbd35780fd 100644 --- a/src/Symfony/Component/Cache/Simple/AbstractCache.php +++ b/src/Symfony/Component/Cache/Simple/AbstractCache.php @@ -162,6 +162,9 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface { try { foreach ($values as $id => $value) { + if (!isset($keys[$id])) { + $id = key($keys); + } $key = $keys[$id]; unset($keys[$id]); yield $key => $value; diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TraceableTagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TraceableTagAwareAdapterTest.php index 38f3c6c087..9b50bfabe6 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TraceableTagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TraceableTagAwareAdapterTest.php @@ -15,6 +15,9 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; +/** + * @group time-sensitive + */ class TraceableTagAwareAdapterTest extends TraceableAdapterTest { public function testInvalidateTags() diff --git a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php index 81d412bd66..744c40bcfb 100644 --- a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php @@ -15,6 +15,11 @@ use Cache\IntegrationTests\SimpleCacheTest; abstract class CacheTestCase extends SimpleCacheTest { + public static function validKeys() + { + return array_merge(parent::validKeys(), array(array("a\0b"))); + } + public function testDefaultLifeTime() { if (isset($this->skippedTests[__FUNCTION__])) { diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 8877b0911a..f28da39346 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -90,6 +90,11 @@ trait RedisTrait $params['dbindex'] = $m[1]; $params['path'] = substr($params['path'], 0, -strlen($m[0])); } + if (isset($params['host'])) { + $scheme = 'tcp'; + } else { + $scheme = 'unix'; + } $params += array( 'host' => isset($params['host']) ? $params['host'] : $params['path'], 'port' => isset($params['host']) ? 6379 : null, @@ -120,7 +125,7 @@ trait RedisTrait throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e, $dsn)); } } elseif (is_a($class, \Predis\Client::class, true)) { - $params['scheme'] = isset($params['host']) ? 'tcp' : 'unix'; + $params['scheme'] = $scheme; $params['database'] = $params['dbindex'] ?: null; $params['password'] = $auth; $redis = new $class((new Factory())->create($params)); diff --git a/src/Symfony/Component/DependencyInjection/ChildDefinition.php b/src/Symfony/Component/DependencyInjection/ChildDefinition.php index dd02f86067..5701caa474 100644 --- a/src/Symfony/Component/DependencyInjection/ChildDefinition.php +++ b/src/Symfony/Component/DependencyInjection/ChildDefinition.php @@ -62,7 +62,7 @@ class ChildDefinition extends Definition * If replaceArgument() has been used to replace an argument, this method * will return the replacement value. * - * @param int $index + * @param int|string $index * * @return mixed The argument value * @@ -74,13 +74,7 @@ class ChildDefinition extends Definition return $this->arguments['index_'.$index]; } - $lastIndex = count(array_filter(array_keys($this->arguments), 'is_int')) - 1; - - if ($index < 0 || $index > $lastIndex) { - throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, $lastIndex)); - } - - return $this->arguments[$index]; + return parent::getArgument($index); } /** @@ -91,8 +85,8 @@ class ChildDefinition extends Definition * certain conventions when you want to overwrite the arguments of the * parent definition, otherwise your arguments will only be appended. * - * @param int $index - * @param mixed $value + * @param int|string $index + * @param mixed $value * * @return self the current instance * @@ -105,7 +99,7 @@ class ChildDefinition extends Definition } elseif (0 === strpos($index, '$')) { $this->arguments[$index] = $value; } else { - throw new InvalidArgumentException('$index must be an integer.'); + throw new InvalidArgumentException('The argument must be an existing index or the name of a constructor\'s parameter.'); } return $this; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index ee7298d046..b8b41619ad 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -170,7 +170,7 @@ class PhpDumper extends Dumper } } if ($unusedEnvs) { - throw new EnvParameterException($unusedEnvs); + throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.'); } return $code; diff --git a/src/Symfony/Component/DependencyInjection/Exception/EnvParameterException.php b/src/Symfony/Component/DependencyInjection/Exception/EnvParameterException.php index 44dbab45b6..3839a4633b 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/EnvParameterException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/EnvParameterException.php @@ -18,8 +18,8 @@ namespace Symfony\Component\DependencyInjection\Exception; */ class EnvParameterException extends InvalidArgumentException { - public function __construct(array $usedEnvs, \Exception $previous = null) + public function __construct(array $envs, \Exception $previous = null, $message = 'Incompatible use of dynamic environment variables "%s" found in parameters.') { - parent::__construct(sprintf('Incompatible use of dynamic environment variables "%s" found in parameters.', implode('", "', $usedEnvs)), 0, $previous); + parent::__construct(sprintf($message, implode('", "', $envs)), 0, $previous); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php index a367a8b2c4..a941b96074 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php @@ -113,6 +113,10 @@ class ChildDefinitionTest extends TestCase $this->assertSame('baz', $def->getArgument(1)); $this->assertSame(array(0 => 'foo', 1 => 'bar', 'index_1' => 'baz'), $def->getArguments()); + + $this->assertSame($def, $def->replaceArgument('$bar', 'val')); + $this->assertSame('val', $def->getArgument('$bar')); + $this->assertSame(array(0 => 'foo', 1 => 'bar', 'index_1' => 'baz', '$bar' => 'val'), $def->getArguments()); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 506ce841fe..4aaf2710be 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -335,7 +335,7 @@ class PhpDumperTest extends TestCase /** * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvParameterException - * @expectedExceptionMessage Incompatible use of dynamic environment variables "FOO" found in parameters. + * @expectedExceptionMessage Environment variables "FOO" are never used. Please, check your container's configuration. */ public function testUnusedEnvParameter() { diff --git a/src/Symfony/Component/Form/FormFactory.php b/src/Symfony/Component/Form/FormFactory.php index 4360a62567..886cdee630 100644 --- a/src/Symfony/Component/Form/FormFactory.php +++ b/src/Symfony/Component/Form/FormFactory.php @@ -115,7 +115,13 @@ class FormFactory implements FormFactoryInterface // user options may override guessed options if ($typeGuess) { - $options = array_merge($typeGuess->getOptions(), $options); + $attrs = array(); + $typeGuessOptions = $typeGuess->getOptions(); + if (isset($typeGuessOptions['attr']) && isset($options['attr'])) { + $attrs = array('attr' => array_merge($typeGuessOptions['attr'], $options['attr'])); + } + + $options = array_merge($typeGuessOptions, $options, $attrs); } return $this->createNamedBuilder($property, $type, $data, $options); diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index 30aea951fb..6029f34a8c 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -310,7 +310,7 @@ class FormFactoryTest extends TestCase ->with('Application\Author', 'firstName') ->will($this->returnValue(new TypeGuess( 'Symfony\Component\Form\Extension\Core\Type\TextType', - array('attr' => array('maxlength' => 10)), + array('attr' => array('class' => 'foo', 'maxlength' => 10)), Guess::MEDIUM_CONFIDENCE ))); @@ -318,7 +318,7 @@ class FormFactoryTest extends TestCase $factory->expects($this->once()) ->method('createNamedBuilder') - ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array('attr' => array('maxlength' => 11))) + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array('attr' => array('class' => 'foo', 'maxlength' => 11))) ->will($this->returnValue('builderInstance')); $this->builder = $factory->createBuilderForProperty( diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index ac812fe152..e1fdf77b9b 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -4,7 +4,7 @@ CHANGELOG 3.3.0 ----- - * [BC BREAK] the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument, + * the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument, see http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info, * deprecated the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods, * added `File\Stream`, to be passed to `BinaryFileResponse` when the size of the served file is unknown, diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 74a09bea20..c15d7d3165 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -568,7 +568,7 @@ class Request * You should only list the reverse proxies that you manage directly. * * @param array $proxies A list of trusted proxies - * @param int $trustedHeaderSet A bit field of Request::HEADER_*, usually either Request::HEADER_FORWARDED or Request::HEADER_X_FORWARDED_ALL, to set which headers to trust from your proxies + * @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies * * @throws \InvalidArgumentException When $trustedHeaderSet is invalid */ @@ -577,10 +577,11 @@ class Request self::$trustedProxies = $proxies; if (2 > func_num_args()) { - // @deprecated code path in 3.3, to be replaced by mandatory argument in 4.0. - throw new \InvalidArgumentException(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument. Defining it is required since version 3.3. See http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info.', __METHOD__)); + @trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since version 3.3. Defining it will be required in 4.0. ', __METHOD__), E_USER_DEPRECATED); + + return; } - $trustedHeaderSet = func_get_arg(1); + $trustedHeaderSet = (int) func_get_arg(1); foreach (self::$trustedHeaderNames as $header => $name) { self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null; @@ -652,11 +653,11 @@ class Request * * @throws \InvalidArgumentException * - * @deprecated since version 3.3, to be removed in 4.0. Use "X-Forwarded-*" headers or the "Forwarded" header defined in RFC7239, and the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead. + * @deprecated since version 3.3, to be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead. */ public static function setTrustedHeaderName($key, $value) { - @trigger_error(sprintf('The "%s()" method is deprecated since version 3.3 and will be removed in 4.0. Use "X-Forwarded-*" headers or the "Forwarded" header defined in RFC7239, and the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since version 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), E_USER_DEPRECATED); if (!array_key_exists($key, self::$trustedHeaders)) { throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); @@ -666,6 +667,9 @@ class Request if (null !== $value) { self::$trustedHeaderNames[$key] = $value; + self::$trustedHeaderSet |= $key; + } else { + self::$trustedHeaderSet &= ~$key; } } @@ -873,8 +877,8 @@ class Request * adding the IP address where it received the request from. * * If your reverse proxy uses a different header name than "X-Forwarded-For", - * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with - * the "client-ip" key. + * ("Client-Ip" for instance), configure it via the $trustedHeaderSet + * argument of the Request::setTrustedProxies() method instead. * * @return string|null The client IP address * @@ -980,7 +984,8 @@ class Request * The "X-Forwarded-Port" header must contain the client port. * * If your reverse proxy uses a different header name than "X-Forwarded-Port", - * configure it via "setTrustedHeaderName()" with the "client-port" key. + * configure it via via the $trustedHeaderSet argument of the + * Request::setTrustedProxies() method instead. * * @return int|string can be a string if fetched from the server bag */ @@ -1197,8 +1202,8 @@ class Request * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". * * If your reverse proxy uses a different header name than "X-Forwarded-Proto" - * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with - * the "client-proto" key. + * ("SSL_HTTPS" for instance), configure it via the $trustedHeaderSet + * argument of the Request::setTrustedProxies() method instead. * * @return bool */ @@ -1222,7 +1227,8 @@ class Request * The "X-Forwarded-Host" header must contain the client host name. * * If your reverse proxy uses a different header name than "X-Forwarded-Host", - * configure it via "setTrustedHeaderName()" with the "client-host" key. + * configure it via the $trustedHeaderSet argument of the + * Request::setTrustedProxies() method instead. * * @return string * diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 7cb59dc1cd..789fa7c897 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1713,7 +1713,7 @@ class RequestTest extends TestCase /** * @group legacy - * @expectedDeprecation The "Symfony\Component\HttpFoundation\Request::setTrustedHeaderName()" method is deprecated since version 3.3 and will be removed in 4.0. Use "X-Forwarded-*" headers or the "Forwarded" header defined in RFC7239, and the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead. + * @expectedDeprecation The "Symfony\Component\HttpFoundation\Request::setTrustedHeaderName()" method is deprecated since version 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead. */ public function testLegacyTrustedProxies() {