Skip to content

Commit

Permalink
Merge branch '3.4'
Browse files Browse the repository at this point in the history
* 3.4: (21 commits)
  fixed CS
  HttpCache lock update
  [Intl] Update ICU data to 60.1
  [YAML] Allow to parse custom tags when linting yaml files
  [HttpKernel][Debug] Remove noise from stack frames of deprecations
  [WebServerBundle] prevent console.terminate from being fired after stopping server
  [Validator] Fix Costa Rica IBAN format
  [Bridge/ProxyManager] Remove direct reference to value holder property
  [Validator] Add Belarus IBAN format
  [Config] Fix cannotBeEmpty()
  [Debug] More aggressively aggregate silenced notices per file+line
  [HttpFoundation] minor session-related fix
  [Cache][Lock] Add RedisProxy for lazy Redis connections
  [TwigBridge] [Bootstrap 4] Fix validation error design for expanded choiceType
  [FrameworkBundle] Specifically inject the debug dispatcher in the collector
  [WebserverBundle] fixed the bug that caused that the webserver would …
  update the pull request template
  [Stopwatch] minor fix
  Add default mapping path for validator component
  Add default mapping path for serializer component
  ...
  • Loading branch information
nicolas-grekas committed Nov 10, 2017
2 parents 61b7534 + f1a1181 commit 44b2f97
Show file tree
Hide file tree
Showing 852 changed files with 16,282 additions and 7,403 deletions.
5 changes: 2 additions & 3 deletions .github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
| Q | A
| ------------- | ---
| Branch? | 3.4 or master / 2.7, 2.8 or 3.3 <!-- see comment below -->
| Branch? | master / 2.7, 2.8, 3.3, or 3.4 <!-- see comment below -->
| Bug fix? | yes/no
| New feature? | yes/no <!-- don't forget to update src/**/CHANGELOG.md files -->
| BC breaks? | yes/no
Expand All @@ -13,8 +13,7 @@
<!--
- Bug fixes must be submitted against the lowest branch where they apply
(lowest branches are regularly merged to upper ones so they get the fixes too).
- Features and deprecations must be submitted against the 3.4,
legacy code removals go to the master branch.
- Features and deprecations must be submitted against the master branch.
- Please fill in this template according to the PR you're about to submit.
- Replace this comment by a description of what your PR is solving.
-->
Expand Up @@ -88,7 +88,11 @@ public function getProxyFactoryCode(Definition $definition, $id, $factoryCode =
*/
public function getProxyCode(Definition $definition)
{
return $this->classGenerator->generate($this->generateProxyClass($definition));
return preg_replace(
'/(\$this->initializer[0-9a-f]++) && \1->__invoke\(\$this->(valueHolder[0-9a-f]++), (.*?), \1\);/',
'$1 && ($1->__invoke(\$$2, $3, $1) || 1) && $this->$2 = \$$2;',
$this->classGenerator->generate($this->generateProxyClass($definition))
);
}

/**
Expand Down
Expand Up @@ -111,7 +111,7 @@
{{- form_widget(child, {
parent_label_class: label_attr.class|default(''),
translation_domain: choice_translation_domain,
valid: valid,
valid: true,
}) -}}
{% endfor -%}
</div>
Expand Down
Expand Up @@ -134,7 +134,7 @@ public static function getServiceProvider(ContainerBuilder $container, $name)
$definition = new Definition(AbstractAdapter::class);
$definition->setPublic(false);
$definition->setFactory(array(AbstractAdapter::class, 'createConnection'));
$definition->setArguments(array($dsn));
$definition->setArguments(array($dsn, array('lazy' => true)));
$container->setDefinition($name, $definition);
}
}
Expand Down
Expand Up @@ -1021,6 +1021,11 @@ private function registerValidatorMapping(ContainerBuilder $container, array $co
}
}

$projectDir = $container->getParameter('kernel.project_dir');
if ($container->fileExists($dir = $projectDir.'/config/validator', '/^$/')) {
$this->registerMappingFilesFromDir($dir, $fileRecorder);
}

$this->registerMappingFilesFromConfig($container, $config, $fileRecorder);
}

Expand Down Expand Up @@ -1188,6 +1193,11 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
}
}

$projectDir = $container->getParameter('kernel.project_dir');
if ($container->fileExists($dir = $projectDir.'/config/serializer', '/^$/')) {
$this->registerMappingFilesFromDir($dir, $fileRecorder);
}

$this->registerMappingFilesFromConfig($container, $config, $fileRecorder);

$chainLoader->replaceArgument(0, $serializerLoaders);
Expand Down Expand Up @@ -1253,7 +1263,7 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont
$connectionDefinition = new Definition(\stdClass::class);
$connectionDefinition->setPublic(false);
$connectionDefinition->setFactory(array(AbstractAdapter::class, 'createConnection'));
$connectionDefinition->setArguments(array($storeDsn));
$connectionDefinition->setArguments(array($storeDsn, array('lazy' => true)));
$container->setDefinition($connectionDefinitionId, $connectionDefinition);
}

Expand Down
Expand Up @@ -27,7 +27,7 @@

<service id="data_collector.events" class="Symfony\Component\HttpKernel\DataCollector\EventDataCollector">
<tag name="data_collector" template="@WebProfiler/Collector/events.html.twig" id="events" priority="290" />
<argument type="service" id="event_dispatcher" on-invalid="ignore" />
<argument type="service" id="debug.event_dispatcher" on-invalid="ignore" />
</service>

<service id="data_collector.logger" class="Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector">
Expand Down
Expand Up @@ -536,9 +536,13 @@ public function testTemplatingRequiresAtLeastOneEngine()
public function testValidation()
{
$container = $this->createContainerFromFile('full');
$projectDir = $container->getParameter('kernel.project_dir');

$ref = new \ReflectionClass('Symfony\Component\Form\Form');
$xmlMappings = array(dirname($ref->getFileName()).'/Resources/config/validation.xml');
$xmlMappings = array(
dirname($ref->getFileName()).'/Resources/config/validation.xml',
strtr($projectDir.'/config/validator/foo.xml', '/', DIRECTORY_SEPARATOR),
);

$calls = $container->getDefinition('validator.builder')->getMethodCalls();

Expand Down Expand Up @@ -626,7 +630,7 @@ public function testValidationPaths()
$this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[7][1]);

$xmlMappings = $calls[3][1][0];
$this->assertCount(2, $xmlMappings);
$this->assertCount(3, $xmlMappings);
try {
// Testing symfony/symfony
$this->assertStringEndsWith('Component'.DIRECTORY_SEPARATOR.'Form/Resources/config/validation.xml', $xmlMappings[0]);
Expand All @@ -652,7 +656,7 @@ public function testValidationPathsUsingCustomBundlePath()

$calls = $container->getDefinition('validator.builder')->getMethodCalls();
$xmlMappings = $calls[3][1][0];
$this->assertCount(2, $xmlMappings);
$this->assertCount(3, $xmlMappings);

try {
// Testing symfony/symfony
Expand Down Expand Up @@ -694,7 +698,7 @@ public function testValidationMapping()
$calls = $container->getDefinition('validator.builder')->getMethodCalls();

$this->assertSame('addXmlMappings', $calls[3][0]);
$this->assertCount(2, $calls[3][1][0]);
$this->assertCount(3, $calls[3][1][0]);

$this->assertSame('addYamlMappings', $calls[4][0]);
$this->assertCount(3, $calls[4][1][0]);
Expand Down Expand Up @@ -742,7 +746,7 @@ public function testSerializerEnabled()

$argument = $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0);

$this->assertCount(1, $argument);
$this->assertCount(2, $argument);
$this->assertEquals('Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader', $argument[0]->getClass());
$this->assertNull($container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1));
$this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.normalizer.object')->getArgument(1));
Expand Down Expand Up @@ -839,23 +843,33 @@ public function testSerializerCacheDisabled()
public function testSerializerMapping()
{
$container = $this->createContainerFromFile('serializer_mapping', array('kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'path' => __DIR__.'/Fixtures/TestBundle'))));
$projectDir = $container->getParameter('kernel.project_dir');
$configDir = __DIR__.'/Fixtures/TestBundle/Resources/config';
$expectedLoaders = array(
new Definition(AnnotationLoader::class, array(new Reference('annotation_reader'))),
new Definition(XmlFileLoader::class, array($configDir.'/serialization.xml')),
new Definition(YamlFileLoader::class, array($configDir.'/serialization.yml')),
new Definition(YamlFileLoader::class, array($projectDir.'/config/serializer/foo.yml')),
new Definition(XmlFileLoader::class, array($configDir.'/serializer_mapping/files/foo.xml')),
new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/files/foo.yml')),
new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/serialization.yml')),
new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/serialization.yaml')),
);

foreach ($expectedLoaders as $definition) {
if (is_file($arg = $definition->getArgument(0))) {
$definition->replaceArgument(0, strtr($arg, '/', DIRECTORY_SEPARATOR));
}
$definition->setPublic(false);
}

$loaders = $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0);
$this->assertEquals(sort($expectedLoaders), sort($loaders));
foreach ($loaders as $loader) {
if (is_file($arg = $loader->getArgument(0))) {
$loader->replaceArgument(0, strtr($arg, '/', DIRECTORY_SEPARATOR));
}
}
$this->assertEquals($expectedLoaders, $loaders);
}

public function testAssetHelperWhenAssetsAreEnabled()
Expand Down
Empty file.
Empty file.
Expand Up @@ -80,6 +80,7 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('twig.exception_listener.controller', $config['exception_controller']);

$container->setParameter('twig.form.resources', $config['form_themes']);
$container->setParameter('twig.default_path', $config['default_path']);

$envConfiguratorDefinition = $container->getDefinition('twig.configurator.environment');
$envConfiguratorDefinition->replaceArgument(0, $config['date']['format']);
Expand Down
Expand Up @@ -20,6 +20,7 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\EventDispatcher\EventDispatcher;

/**
* Runs a local web server in a background process.
Expand Down Expand Up @@ -127,6 +128,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
$io->error('Running this server in production environment is NOT recommended!');
}

// replace event dispatcher with an empty one to prevent console.terminate from firing
// as container could have changed between start and stop
$this->getApplication()->setDispatcher(new EventDispatcher());

try {
$server = new WebServer();
if ($server->isRunning($input->getOption('pidfile'))) {
Expand Down
Expand Up @@ -44,6 +44,7 @@ public function testCreateConnection()
'persistent_id' => null,
'read_timeout' => 0,
'retry_interval' => 0,
'lazy' => false,
'database' => '1',
'password' => null,
);
Expand Down
11 changes: 10 additions & 1 deletion src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php
Expand Up @@ -13,13 +13,22 @@

use Symfony\Component\Cache\Adapter\AbstractAdapter;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Traits\RedisProxy;

class RedisAdapterTest extends AbstractRedisAdapterTest
{
public static function setupBeforeClass()
{
parent::setupBeforeClass();
self::$redis = AbstractAdapter::createConnection('redis://'.getenv('REDIS_HOST'));
self::$redis = AbstractAdapter::createConnection('redis://'.getenv('REDIS_HOST'), array('lazy' => true));
}

public function createCachePool($defaultLifetime = 0)
{
$adapter = parent::createCachePool($defaultLifetime);
$this->assertInstanceOf(RedisProxy::class, self::$redis);

return $adapter;
}

public function testCreateConnection()
Expand Down
65 changes: 65 additions & 0 deletions src/Symfony/Component/Cache/Traits/RedisProxy.php
@@ -0,0 +1,65 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Cache\Traits;

/**
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
class RedisProxy
{
private $redis;
private $initializer;
private $ready = false;

public function __construct(\Redis $redis, \Closure $initializer)
{
$this->redis = $redis;
$this->initializer = $initializer;
}

public function __call($method, array $args)
{
$this->ready ?: $this->ready = $this->initializer->__invoke($this->redis);

return \call_user_func_array(array($this->redis, $method), $args);
}

public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
{
$this->ready ?: $this->ready = $this->initializer->__invoke($this->redis);

return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount);
}

public function scan(&$iIterator, $strPattern = null, $iCount = null)
{
$this->ready ?: $this->ready = $this->initializer->__invoke($this->redis);

return $this->redis->scan($iIterator, $strPattern, $iCount);
}

public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
{
$this->ready ?: $this->ready = $this->initializer->__invoke($this->redis);

return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount);
}

public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
{
$this->ready ?: $this->ready = $this->initializer->__invoke($this->redis);

return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount);
}
}
36 changes: 24 additions & 12 deletions src/Symfony/Component/Cache/Traits/RedisTrait.php
Expand Up @@ -34,6 +34,7 @@ trait RedisTrait
'timeout' => 30,
'read_timeout' => 0,
'retry_interval' => 0,
'lazy' => false,
);
private $redis;

Expand All @@ -49,7 +50,7 @@ public function init($redisClient, $namespace = '', $defaultLifetime = 0)
}
if ($redisClient instanceof \RedisCluster) {
$this->enableVersioning();
} elseif (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \Predis\Client) {
} elseif (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy) {
throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, is_object($redisClient) ? get_class($redisClient) : gettype($redisClient)));
}
$this->redis = $redisClient;
Expand Down Expand Up @@ -117,19 +118,30 @@ public static function createConnection($dsn, array $options = array())
if (is_a($class, \Redis::class, true)) {
$connect = $params['persistent'] || $params['persistent_id'] ? 'pconnect' : 'connect';
$redis = new $class();
@$redis->{$connect}($params['host'], $params['port'], $params['timeout'], $params['persistent_id'], $params['retry_interval']);

if (@!$redis->isConnected()) {
$e = ($e = error_get_last()) && preg_match('/^Redis::p?connect\(\): (.*)/', $e['message'], $e) ? sprintf(' (%s)', $e[1]) : '';
throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $e, $dsn));
}
$initializer = function ($redis) use ($connect, $params, $dsn, $auth) {
@$redis->{$connect}($params['host'], $params['port'], $params['timeout'], $params['persistent_id'], $params['retry_interval']);

if (@!$redis->isConnected()) {
$e = ($e = error_get_last()) && preg_match('/^Redis::p?connect\(\): (.*)/', $e['message'], $e) ? sprintf(' (%s)', $e[1]) : '';
throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $e, $dsn));
}

if ((null !== $auth && !$redis->auth($auth))
|| ($params['dbindex'] && !$redis->select($params['dbindex']))
|| ($params['read_timeout'] && !$redis->setOption(\Redis::OPT_READ_TIMEOUT, $params['read_timeout']))
) {
$e = preg_replace('/^ERR /', '', $redis->getLastError());
throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e, $dsn));
}

return true;
};

if ((null !== $auth && !$redis->auth($auth))
|| ($params['dbindex'] && !$redis->select($params['dbindex']))
|| ($params['read_timeout'] && !$redis->setOption(\Redis::OPT_READ_TIMEOUT, $params['read_timeout']))
) {
$e = preg_replace('/^ERR /', '', $redis->getLastError());
throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e, $dsn));
if ($params['lazy']) {
$redis = new RedisProxy($redis, $initializer);
} else {
$initializer($redis);
}
} elseif (is_a($class, \Predis\Client::class, true)) {
$params['scheme'] = $scheme;
Expand Down
Expand Up @@ -424,7 +424,11 @@ protected function createNode()
$node->setKeyAttribute($this->key, $this->removeKeyItem);
}

if (true === $this->atLeastOne || false === $this->allowEmptyValue) {
if (false === $this->allowEmptyValue) {
@trigger_error(sprintf('Using %s::cannotBeEmpty() at path "%s" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.', __CLASS__, $node->getPath()), E_USER_DEPRECATED);
}

if (true === $this->atLeastOne) {
$node->setMinNumberOfElements(1);
}

Expand Down Expand Up @@ -486,9 +490,7 @@ protected function validateConcreteNode(ArrayNode $node)
}

if (false === $this->allowEmptyValue) {
throw new InvalidDefinitionException(
sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s"', $path)
);
@trigger_error(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s". In 4.0 it will throw an exception.', $path), E_USER_DEPRECATED);
}

if (true === $this->atLeastOne) {
Expand Down

0 comments on commit 44b2f97

Please sign in to comment.