Merge branch '5.2' into 5.x

* 5.2:
  make fabbot happy
  use correct spelling when accessing the SMTP php.ini value
  Fix issue 40507: Tabs as separators between tokens
  [Cache] phpredis: Added full TLS support for RedisCluster
  [DependencyInjection][AliasDeprecatedPublicServicesPass] Noop when the service is private
This commit is contained in:
Alexander M. Turek 2021-04-23 18:57:12 +02:00
commit a9d582d681
8 changed files with 121 additions and 33 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;
@ -202,7 +203,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();
@ -265,7 +266,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());
} }
@ -311,7 +312,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']);
} }
@ -558,8 +559,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

@ -58,7 +58,7 @@ final class AliasDeprecatedPublicServicesPass extends AbstractRecursivePass
$definition = $container->getDefinition($id); $definition = $container->getDefinition($id);
if (!$definition->isPublic() || $definition->isPrivate()) { if (!$definition->isPublic() || $definition->isPrivate()) {
throw new InvalidArgumentException(sprintf('The "%s" service is private: it cannot have the "%s" tag.', $id, $this->tagName)); continue;
} }
$container $container

View File

@ -59,14 +59,13 @@ final class AliasDeprecatedPublicServicesPassTest extends TestCase
public function testProcessWithNonPublicService() public function testProcessWithNonPublicService()
{ {
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The "foo" service is private: it cannot have the "container.private" tag.');
$container = new ContainerBuilder(); $container = new ContainerBuilder();
$container $container
->register('foo') ->register('foo')
->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.2']); ->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.2']);
(new AliasDeprecatedPublicServicesPass())->process($container); (new AliasDeprecatedPublicServicesPass())->process($container);
$this->assertTrue($container->hasDefinition('foo'));
} }
} }

View File

@ -111,7 +111,7 @@ EOT;
{ {
self::$fakeConfiguration = [ self::$fakeConfiguration = [
'sendmail_path' => $sendmailPath, 'sendmail_path' => $sendmailPath,
'smtp' => $smtp, 'SMTP' => $smtp,
'smtp_port' => $smtpPort, 'smtp_port' => $smtpPort,
]; ];

View File

@ -39,7 +39,7 @@ final class NativeTransportFactory extends AbstractTransportFactory
// Only for windows hosts; at this point non-windows // Only for windows hosts; at this point non-windows
// host have already thrown an exception or returned a transport // host have already thrown an exception or returned a transport
$host = ini_get('smtp'); $host = ini_get('SMTP');
$port = (int) ini_get('smtp_port'); $port = (int) ini_get('smtp_port');
if (!$host || !$port) { if (!$host || !$port) {

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()