Merge branch '4.4' into 5.2

* 4.4:
  make fabbot happy
  Fix issue 40507: Tabs as separators between tokens
  [Cache] phpredis: Added full TLS support for RedisCluster
This commit is contained in:
Alexander M. Turek 2021-04-23 18:56:33 +02:00
commit 546f3b61ed
4 changed files with 116 additions and 27 deletions

View File

@ -0,0 +1,48 @@
<?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;
/**
* This file acts as a wrapper to the \RedisCluster implementation so it can accept the same type of calls as
* individual \Redis objects.
*
* Calls are made to individual nodes via: RedisCluster->{method}($host, ...args)'
* according to https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#directed-node-commands
*
* @author Jack Thomas <jack.thomas@solidalpha.com>
*
* @internal
*/
class RedisClusterNodeProxy
{
private $host;
private $redis;
/**
* @param \RedisCluster|RedisClusterProxy $redis
*/
public function __construct(array $host, $redis)
{
$this->host = $host;
$this->redis = $redis;
}
public function __call(string $method, array $args)
{
return $this->redis->{$method}($this->host, ...$args);
}
public function scan(&$iIterator, $strPattern = null, $iCount = null)
{
return $this->redis->scan($iIterator, $this->host, $strPattern, $iCount);
}
}

View File

@ -42,6 +42,7 @@ trait RedisTrait
'redis_sentinel' => null, 'redis_sentinel' => null,
'dbindex' => 0, 'dbindex' => 0,
'failover' => 'none', 'failover' => 'none',
'ssl' => null, // see https://php.net/context.ssl
]; ];
private $redis; private $redis;
private $marshaller; private $marshaller;
@ -188,7 +189,7 @@ trait RedisTrait
} }
try { try {
@$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout']); @$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ['stream' => $params['ssl'] ?? null]);
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
$isConnected = $redis->isConnected(); $isConnected = $redis->isConnected();
@ -251,7 +252,7 @@ trait RedisTrait
} }
try { try {
$redis = new $class(null, $hosts, $params['timeout'], $params['read_timeout'], (bool) $params['persistent'], $params['auth'] ?? ''); $redis = new $class(null, $hosts, $params['timeout'], $params['read_timeout'], (bool) $params['persistent'], $params['auth'] ?? '', $params['ssl'] ?? null);
} catch (\RedisClusterException $e) { } catch (\RedisClusterException $e) {
throw new InvalidArgumentException(sprintf('Redis connection "%s" failed: ', $dsn).$e->getMessage()); throw new InvalidArgumentException(sprintf('Redis connection "%s" failed: ', $dsn).$e->getMessage());
} }
@ -300,7 +301,7 @@ trait RedisTrait
} }
$params['exceptions'] = false; $params['exceptions'] = false;
$redis = new $class($hosts, array_diff_key($params, self::$defaultConnectionOptions)); $redis = new $class($hosts, array_diff_key($params, array_diff_key(self::$defaultConnectionOptions, ['ssl' => null])));
if (isset($params['redis_sentinel'])) { if (isset($params['redis_sentinel'])) {
$redis->getConnection()->setSentinelTimeout($params['timeout']); $redis->getConnection()->setSentinelTimeout($params['timeout']);
} }
@ -547,8 +548,7 @@ trait RedisTrait
} elseif ($this->redis instanceof RedisClusterProxy || $this->redis instanceof \RedisCluster) { } elseif ($this->redis instanceof RedisClusterProxy || $this->redis instanceof \RedisCluster) {
$hosts = []; $hosts = [];
foreach ($this->redis->_masters() as $host) { foreach ($this->redis->_masters() as $host) {
$hosts[] = $h = new \Redis(); $hosts[] = new RedisClusterNodeProxy($host, $this->redis);
$h->connect($host[0], $host[1]);
} }
} }

View File

@ -212,7 +212,7 @@ class Parser
array_pop($this->refsBeingParsed); array_pop($this->refsBeingParsed);
} }
} elseif ( } elseif (
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:( ++(?P<value>.+))?$#u', rtrim($this->currentLine), $values) self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(( |\t)++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
&& (false === strpos($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"])) && (false === strpos($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"]))
) { ) {
if ($context && 'sequence' == $context) { if ($context && 'sequence' == $context) {
@ -230,7 +230,7 @@ class Parser
} }
if (!\is_string($key) && !\is_int($key)) { if (!\is_string($key) && !\is_int($key)) {
throw new ParseException(sprintf('%s keys are not supported. Quote your evaluable mapping keys instead.', is_numeric($key) ? 'Numeric' : 'Non-string'), $this->getRealCurrentLineNb() + 1, $this->currentLine); throw new ParseException((is_numeric($key) ? 'Numeric' : 'Non-string').' keys are not supported. Quote your evaluable mapping keys instead.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
} }
// Convert float keys to strings, to avoid being converted to integers by PHP // Convert float keys to strings, to avoid being converted to integers by PHP
@ -245,7 +245,7 @@ class Parser
$refName = substr(rtrim($values['value']), 1); $refName = substr(rtrim($values['value']), 1);
if (!\array_key_exists($refName, $this->refs)) { if (!\array_key_exists($refName, $this->refs)) {
if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) { if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) {
throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$refName])), $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename);
} }
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
@ -732,7 +732,7 @@ class Parser
if (!\array_key_exists($value, $this->refs)) { if (!\array_key_exists($value, $this->refs)) {
if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) { if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) {
throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$value])), $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
} }
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
@ -1225,7 +1225,7 @@ class Parser
} }
} while ($this->moveToNextLine()); } while ($this->moveToNextLine());
throw new ParseException('Malformed inline YAML string'); throw new ParseException('Malformed inline YAML string.');
} }
private function lexUnquotedString(int &$cursor): string private function lexUnquotedString(int &$cursor): string
@ -1296,7 +1296,7 @@ class Parser
} }
} while ($this->moveToNextLine()); } while ($this->moveToNextLine());
throw new ParseException('Malformed inline YAML string'); throw new ParseException('Malformed inline YAML string.');
} }
private function consumeWhitespaces(int &$cursor): bool private function consumeWhitespaces(int &$cursor): bool

View File

@ -52,26 +52,67 @@ class ParserTest extends TestCase
return $this->loadTestsFromFixtureFiles('nonStringKeys.yml'); return $this->loadTestsFromFixtureFiles('nonStringKeys.yml');
} }
public function testTabsInYaml() /**
* @dataProvider invalidIndentation
*/
public function testTabsAsIndentationInYaml(string $given, string $expectedMessage)
{ {
// test tabs in YAML $this->expectException(ParseException::class);
$yamls = [ $this->expectExceptionMessage($expectedMessage);
"foo:\n bar", $this->parser->parse($given);
"foo:\n bar", }
"foo:\n bar",
"foo:\n bar", public function invalidIndentation(): array
{
return [
[
"foo:\n\tbar",
"A YAML file cannot contain tabs as indentation at line 2 (near \"\tbar\").",
],
[
"foo:\n \tbar",
"A YAML file cannot contain tabs as indentation at line 2 (near \"\tbar\").",
],
[
"foo:\n\t bar",
"A YAML file cannot contain tabs as indentation at line 2 (near \"\t bar\").",
],
[
"foo:\n \t bar",
"A YAML file cannot contain tabs as indentation at line 2 (near \"\t bar\").",
],
]; ];
}
foreach ($yamls as $yaml) { /**
try { * @dataProvider validTokenSeparators
$this->parser->parse($yaml); */
public function testValidTokenSeparation(string $given, array $expected)
{
$actual = $this->parser->parse($given);
$this->assertEquals($expected, $actual);
}
$this->fail('YAML files must not contain tabs'); public function validTokenSeparators(): array
} catch (\Exception $e) { {
$this->assertInstanceOf(\Exception::class, $e, 'YAML files must not contain tabs'); return [
$this->assertEquals('A YAML file cannot contain tabs as indentation at line 2 (near "'.strpbrk($yaml, "\t").'").', $e->getMessage(), 'YAML files must not contain tabs'); [
} 'foo: bar',
} ['foo' => 'bar'],
],
[
"foo:\tbar",
['foo' => 'bar'],
],
[
"foo: \tbar",
['foo' => 'bar'],
],
[
"foo:\t bar",
['foo' => 'bar'],
],
];
} }
public function testEndOfTheDocumentMarker() public function testEndOfTheDocumentMarker()