Merge branch '4.4' into 5.1
* 4.4: fix merge drop logger mock in favor of using the BufferingLogger catch ValueError thrown on PHP 8 [Yaml Parser] Fix edge cases when parsing multiple documents fix parsing comments not prefixed by a space [Translator] Make sure a null locale is handled properly deal with errors being thrown on PHP 8 [Cache] Allow cache tags to be objects implementing __toString() [HttpKernel] Do not override max_redirects option in HttpClientKernel remove superfluous cast [HttpClient] Support for CURLOPT_LOCALPORT. Upgrade PHPUnit to 8.5 (php 7.2) and 9.3 (php >= 7.3). Fixed exception message formatting [FrameworkBundle] Fix error in xsd which prevent to register more than one metadata [Console] work around disabled putenv() [PhpUnitBridge] Fix error with ReflectionClass [HttpClient][HttpClientTrait] don't calculate alternatives if option is auth_ntlm Change 'cache_key' to AbstractRendererEngine::CACHE_KEY_VAR Upgrade PHPUnit to 8.5 (php 7.2) and 9.3 (php >= 7.3).
This commit is contained in:
commit
4ee591bec6
6
phpunit
6
phpunit
@ -12,10 +12,10 @@ if (!getenv('SYMFONY_PHPUNIT_VERSION')) {
|
|||||||
if (false === getenv('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT') && false !== strpos(@file_get_contents(__DIR__.'/src/Symfony/Component/HttpKernel/Kernel.php'), 'const MAJOR_VERSION = 3;')) {
|
if (false === getenv('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT') && false !== strpos(@file_get_contents(__DIR__.'/src/Symfony/Component/HttpKernel/Kernel.php'), 'const MAJOR_VERSION = 3;')) {
|
||||||
putenv('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1');
|
putenv('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1');
|
||||||
}
|
}
|
||||||
if (\PHP_VERSION_ID >= 80000) {
|
if (\PHP_VERSION_ID < 70300) {
|
||||||
putenv('SYMFONY_PHPUNIT_VERSION=9.3');
|
putenv('SYMFONY_PHPUNIT_VERSION=8.5');
|
||||||
} else {
|
} else {
|
||||||
putenv('SYMFONY_PHPUNIT_VERSION=8.3');
|
putenv('SYMFONY_PHPUNIT_VERSION=9.3');
|
||||||
}
|
}
|
||||||
} elseif (\PHP_VERSION_ID >= 70000) {
|
} elseif (\PHP_VERSION_ID >= 70000) {
|
||||||
putenv('SYMFONY_PHPUNIT_VERSION=6.5');
|
putenv('SYMFONY_PHPUNIT_VERSION=6.5');
|
||||||
|
@ -109,7 +109,7 @@ class CoverageListenerTrait
|
|||||||
|
|
||||||
// Exclude internal classes; PHPUnit 9.1+ is picky about tests covering, say, a \RuntimeException
|
// Exclude internal classes; PHPUnit 9.1+ is picky about tests covering, say, a \RuntimeException
|
||||||
$covers = array_filter($covers, function ($class) {
|
$covers = array_filter($covers, function ($class) {
|
||||||
$reflector = new ReflectionClass($class);
|
$reflector = new \ReflectionClass($class);
|
||||||
|
|
||||||
return $reflector->isUserDefined();
|
return $reflector->isUserDefined();
|
||||||
});
|
});
|
||||||
|
@ -1197,7 +1197,7 @@ class Configuration implements ConfigurationInterface
|
|||||||
return $middleware;
|
return $middleware;
|
||||||
}
|
}
|
||||||
if (1 < \count($middleware)) {
|
if (1 < \count($middleware)) {
|
||||||
throw new \InvalidArgumentException(sprintf('Invalid middleware at path "framework.messenger": a map with a single factory id as key and its arguments as value was expected, %s given.', json_encode($middleware)));
|
throw new \InvalidArgumentException('Invalid middleware at path "framework.messenger": a map with a single factory id as key and its arguments as value was expected, '.json_encode($middleware).' given.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -331,7 +331,7 @@
|
|||||||
|
|
||||||
<xsd:complexType name="metadata">
|
<xsd:complexType name="metadata">
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:any minOccurs="0" processContents="lax"/>
|
<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
||||||
|
@ -10,6 +10,10 @@ $container->loadFromExtension('framework', [
|
|||||||
FrameworkExtensionTest::class,
|
FrameworkExtensionTest::class,
|
||||||
],
|
],
|
||||||
'initial_marking' => ['draft'],
|
'initial_marking' => ['draft'],
|
||||||
|
'metadata' => [
|
||||||
|
'title' => 'article workflow',
|
||||||
|
'description' => 'workflow for articles'
|
||||||
|
],
|
||||||
'places' => [
|
'places' => [
|
||||||
'draft',
|
'draft',
|
||||||
'wait_for_journalist',
|
'wait_for_journalist',
|
||||||
|
@ -35,6 +35,10 @@
|
|||||||
<framework:from>approved_by_spellchecker</framework:from>
|
<framework:from>approved_by_spellchecker</framework:from>
|
||||||
<framework:to>published</framework:to>
|
<framework:to>published</framework:to>
|
||||||
</framework:transition>
|
</framework:transition>
|
||||||
|
<framework:metadata>
|
||||||
|
<framework:title>article workflow</framework:title>
|
||||||
|
<framework:description>workflow for articles</framework:description>
|
||||||
|
</framework:metadata>
|
||||||
</framework:workflow>
|
</framework:workflow>
|
||||||
|
|
||||||
<framework:workflow name="pull_request">
|
<framework:workflow name="pull_request">
|
||||||
|
@ -5,6 +5,9 @@ framework:
|
|||||||
supports:
|
supports:
|
||||||
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
||||||
initial_marking: [draft]
|
initial_marking: [draft]
|
||||||
|
metadata:
|
||||||
|
title: article workflow
|
||||||
|
description: workflow for articles
|
||||||
places:
|
places:
|
||||||
# simple format
|
# simple format
|
||||||
- draft
|
- draft
|
||||||
|
@ -54,6 +54,7 @@ use Symfony\Component\Translation\DependencyInjection\TranslatorPass;
|
|||||||
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
|
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
|
||||||
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
|
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
|
||||||
use Symfony\Component\Workflow;
|
use Symfony\Component\Workflow;
|
||||||
|
use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore;
|
||||||
|
|
||||||
abstract class FrameworkExtensionTest extends TestCase
|
abstract class FrameworkExtensionTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -231,6 +232,12 @@ abstract class FrameworkExtensionTest extends TestCase
|
|||||||
);
|
);
|
||||||
$this->assertCount(4, $workflowDefinition->getArgument(1));
|
$this->assertCount(4, $workflowDefinition->getArgument(1));
|
||||||
$this->assertSame(['draft'], $workflowDefinition->getArgument(2));
|
$this->assertSame(['draft'], $workflowDefinition->getArgument(2));
|
||||||
|
$metadataStoreDefinition = $workflowDefinition->getArgument(3);
|
||||||
|
$this->assertSame(InMemoryMetadataStore::class, $metadataStoreDefinition->getClass());
|
||||||
|
$this->assertSame([
|
||||||
|
'title' => 'article workflow',
|
||||||
|
'description' => 'workflow for articles',
|
||||||
|
], $metadataStoreDefinition->getArgument(0));
|
||||||
|
|
||||||
$this->assertTrue($container->hasDefinition('state_machine.pull_request'), 'State machine is registered as a service');
|
$this->assertTrue($container->hasDefinition('state_machine.pull_request'), 'State machine is registered as a service');
|
||||||
$this->assertSame('state_machine.abstract', $container->getDefinition('state_machine.pull_request')->getParent());
|
$this->assertSame('state_machine.abstract', $container->getDefinition('state_machine.pull_request')->getParent());
|
||||||
@ -255,7 +262,7 @@ abstract class FrameworkExtensionTest extends TestCase
|
|||||||
|
|
||||||
$metadataStoreDefinition = $stateMachineDefinition->getArgument(3);
|
$metadataStoreDefinition = $stateMachineDefinition->getArgument(3);
|
||||||
$this->assertInstanceOf(Definition::class, $metadataStoreDefinition);
|
$this->assertInstanceOf(Definition::class, $metadataStoreDefinition);
|
||||||
$this->assertSame(Workflow\Metadata\InMemoryMetadataStore::class, $metadataStoreDefinition->getClass());
|
$this->assertSame(InMemoryMetadataStore::class, $metadataStoreDefinition->getClass());
|
||||||
|
|
||||||
$workflowMetadata = $metadataStoreDefinition->getArgument(0);
|
$workflowMetadata = $metadataStoreDefinition->getArgument(0);
|
||||||
$this->assertSame(['title' => 'workflow title'], $workflowMetadata);
|
$this->assertSame(['title' => 'workflow title'], $workflowMetadata);
|
||||||
|
@ -119,9 +119,10 @@ final class CacheItem implements ItemInterface
|
|||||||
$tags = [$tags];
|
$tags = [$tags];
|
||||||
}
|
}
|
||||||
foreach ($tags as $tag) {
|
foreach ($tags as $tag) {
|
||||||
if (!\is_string($tag)) {
|
if (!\is_string($tag) && !(\is_object($tag) && method_exists($tag, '__toString'))) {
|
||||||
throw new InvalidArgumentException(sprintf('Cache tag must be string, "%s" given.', get_debug_type($tag)));
|
throw new InvalidArgumentException(sprintf('Cache tag must be string or object that implements __toString(), "%s" given.', \is_object($tag) ? \get_class($tag) : \gettype($tag)));
|
||||||
}
|
}
|
||||||
|
$tag = (string) $tag;
|
||||||
if (isset($this->newMetadata[self::METADATA_TAGS][$tag])) {
|
if (isset($this->newMetadata[self::METADATA_TAGS][$tag])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Cache\Tests;
|
|||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Cache\CacheItem;
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\Tests\Fixtures\StringableTag;
|
||||||
|
|
||||||
class CacheItemTest extends TestCase
|
class CacheItemTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -61,9 +62,11 @@ class CacheItemTest extends TestCase
|
|||||||
|
|
||||||
$this->assertSame($item, $item->tag('foo'));
|
$this->assertSame($item, $item->tag('foo'));
|
||||||
$this->assertSame($item, $item->tag(['bar', 'baz']));
|
$this->assertSame($item, $item->tag(['bar', 'baz']));
|
||||||
|
$this->assertSame($item, $item->tag(new StringableTag('qux')));
|
||||||
|
$this->assertSame($item, $item->tag([new StringableTag('quux'), new StringableTag('quuux')]));
|
||||||
|
|
||||||
(\Closure::bind(function () use ($item) {
|
(\Closure::bind(function () use ($item) {
|
||||||
$this->assertSame(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'], $item->newMetadata[CacheItem::METADATA_TAGS]);
|
$this->assertSame(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz', 'qux' => 'qux', 'quux' => 'quux', 'quuux' => 'quuux'], $item->newMetadata[CacheItem::METADATA_TAGS]);
|
||||||
}, $this, CacheItem::class))();
|
}, $this, CacheItem::class))();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
30
src/Symfony/Component/Cache/Tests/Fixtures/StringableTag.php
Normal file
30
src/Symfony/Component/Cache/Tests/Fixtures/StringableTag.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?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\Tests\Fixtures;
|
||||||
|
|
||||||
|
class StringableTag
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $tag;
|
||||||
|
|
||||||
|
public function __construct(string $tag)
|
||||||
|
{
|
||||||
|
$this->tag = $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->tag;
|
||||||
|
}
|
||||||
|
}
|
@ -107,8 +107,10 @@ class Application implements ResetInterface
|
|||||||
*/
|
*/
|
||||||
public function run(InputInterface $input = null, OutputInterface $output = null)
|
public function run(InputInterface $input = null, OutputInterface $output = null)
|
||||||
{
|
{
|
||||||
putenv('LINES='.$this->terminal->getHeight());
|
if (\function_exists('putenv')) {
|
||||||
putenv('COLUMNS='.$this->terminal->getWidth());
|
@putenv('LINES='.$this->terminal->getHeight());
|
||||||
|
@putenv('COLUMNS='.$this->terminal->getWidth());
|
||||||
|
}
|
||||||
|
|
||||||
if (null === $input) {
|
if (null === $input) {
|
||||||
$input = new ArgvInput();
|
$input = new ArgvInput();
|
||||||
@ -891,7 +893,9 @@ class Application implements ResetInterface
|
|||||||
$input->setInteractive(false);
|
$input->setInteractive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
putenv('SHELL_VERBOSITY='.$shellVerbosity);
|
if (\function_exists('putenv')) {
|
||||||
|
@putenv('SHELL_VERBOSITY='.$shellVerbosity);
|
||||||
|
}
|
||||||
$_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
|
$_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
|
||||||
$_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
|
$_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\EventDispatcher\Tests\Debug;
|
namespace Symfony\Component\EventDispatcher\Tests\Debug;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\ErrorHandler\BufferingLogger;
|
||||||
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
|
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
@ -192,41 +193,57 @@ class TraceableEventDispatcherTest extends TestCase
|
|||||||
|
|
||||||
public function testLogger()
|
public function testLogger()
|
||||||
{
|
{
|
||||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
$logger = new BufferingLogger();
|
||||||
|
|
||||||
$dispatcher = new EventDispatcher();
|
$dispatcher = new EventDispatcher();
|
||||||
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
|
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
|
||||||
$tdispatcher->addListener('foo', $listener1 = function () {});
|
$tdispatcher->addListener('foo', $listener1 = function () {});
|
||||||
$tdispatcher->addListener('foo', $listener2 = function () {});
|
$tdispatcher->addListener('foo', $listener2 = function () {});
|
||||||
|
|
||||||
$logger->expects($this->exactly(2))
|
|
||||||
->method('debug')
|
|
||||||
->withConsecutive(
|
|
||||||
['Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']],
|
|
||||||
['Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']]
|
|
||||||
);
|
|
||||||
|
|
||||||
$tdispatcher->dispatch(new Event(), 'foo');
|
$tdispatcher->dispatch(new Event(), 'foo');
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
[
|
||||||
|
'debug',
|
||||||
|
'Notified event "{event}" to listener "{listener}".',
|
||||||
|
['event' => 'foo', 'listener' => 'closure'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'debug',
|
||||||
|
'Notified event "{event}" to listener "{listener}".',
|
||||||
|
['event' => 'foo', 'listener' => 'closure'],
|
||||||
|
],
|
||||||
|
], $logger->cleanLogs());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLoggerWithStoppedEvent()
|
public function testLoggerWithStoppedEvent()
|
||||||
{
|
{
|
||||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
$logger = new BufferingLogger();
|
||||||
|
|
||||||
$dispatcher = new EventDispatcher();
|
$dispatcher = new EventDispatcher();
|
||||||
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
|
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
|
||||||
$tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
|
$tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
|
||||||
$tdispatcher->addListener('foo', $listener2 = function () {});
|
$tdispatcher->addListener('foo', $listener2 = function () {});
|
||||||
|
|
||||||
$logger->expects($this->exactly(3))
|
|
||||||
->method('debug')
|
|
||||||
->withConsecutive(
|
|
||||||
['Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']],
|
|
||||||
['Listener "{listener}" stopped propagation of the event "{event}".', ['event' => 'foo', 'listener' => 'closure']],
|
|
||||||
['Listener "{listener}" was not called for event "{event}".', ['event' => 'foo', 'listener' => 'closure']]
|
|
||||||
);
|
|
||||||
|
|
||||||
$tdispatcher->dispatch(new Event(), 'foo');
|
$tdispatcher->dispatch(new Event(), 'foo');
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
[
|
||||||
|
'debug',
|
||||||
|
'Notified event "{event}" to listener "{listener}".',
|
||||||
|
['event' => 'foo', 'listener' => 'closure'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'debug',
|
||||||
|
'Listener "{listener}" stopped propagation of the event "{event}".',
|
||||||
|
['event' => 'foo', 'listener' => 'closure'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'debug',
|
||||||
|
'Listener "{listener}" was not called for event "{event}".',
|
||||||
|
['event' => 'foo', 'listener' => 'closure'],
|
||||||
|
],
|
||||||
|
], $logger->cleanLogs());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDispatchCallListeners()
|
public function testDispatchCallListeners()
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"symfony/dependency-injection": "^4.4|^5.0",
|
"symfony/dependency-injection": "^4.4|^5.0",
|
||||||
"symfony/expression-language": "^4.4|^5.0",
|
"symfony/expression-language": "^4.4|^5.0",
|
||||||
"symfony/config": "^4.4|^5.0",
|
"symfony/config": "^4.4|^5.0",
|
||||||
|
"symfony/error-handler": "^4.4|^5.0",
|
||||||
"symfony/http-foundation": "^4.4|^5.0",
|
"symfony/http-foundation": "^4.4|^5.0",
|
||||||
"symfony/service-contracts": "^1.1|^2",
|
"symfony/service-contracts": "^1.1|^2",
|
||||||
"symfony/stopwatch": "^4.4|^5.0",
|
"symfony/stopwatch": "^4.4|^5.0",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractRendererEngine;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
@ -112,7 +113,7 @@ abstract class BaseType extends AbstractType
|
|||||||
// collection form have different types (dynamically), they should
|
// collection form have different types (dynamically), they should
|
||||||
// be rendered differently.
|
// be rendered differently.
|
||||||
// https://github.com/symfony/symfony/issues/5038
|
// https://github.com/symfony/symfony/issues/5038
|
||||||
'cache_key' => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getBlockPrefix(),
|
AbstractRendererEngine::CACHE_KEY_VAR => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getBlockPrefix(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,14 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($options['bindto']) {
|
if ($options['bindto']) {
|
||||||
$curlopts[file_exists($options['bindto']) ? \CURLOPT_UNIX_SOCKET_PATH : \CURLOPT_INTERFACE] = $options['bindto'];
|
if (file_exists($options['bindto'])) {
|
||||||
|
$curlopts[\CURLOPT_UNIX_SOCKET_PATH] = $options['bindto'];
|
||||||
|
} elseif (preg_match('/^(.*):(\d+)$/', $options['bindto'], $matches)) {
|
||||||
|
$curlopts[\CURLOPT_INTERFACE] = $matches[1];
|
||||||
|
$curlopts[\CURLOPT_LOCALPORT] = $matches[2];
|
||||||
|
} else {
|
||||||
|
$curlopts[\CURLOPT_INTERFACE] = $options['bindto'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 < $options['max_duration']) {
|
if (0 < $options['max_duration']) {
|
||||||
|
@ -198,6 +198,16 @@ trait HttpClientTrait
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('auth_ntlm' === $name) {
|
||||||
|
if (!\extension_loaded('curl')) {
|
||||||
|
$msg = 'try installing the "curl" extension to use "%s" instead.';
|
||||||
|
} else {
|
||||||
|
$msg = 'try using "%s" instead.';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException(sprintf('Option "auth_ntlm" is not supported by "%s", '.$msg, __CLASS__, CurlHttpClient::class));
|
||||||
|
}
|
||||||
|
|
||||||
$alternatives = [];
|
$alternatives = [];
|
||||||
|
|
||||||
foreach ($defaultOptions as $key => $v) {
|
foreach ($defaultOptions as $key => $v) {
|
||||||
@ -206,10 +216,6 @@ trait HttpClientTrait
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('auth_ntlm' === $name) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Option "auth_ntlm" is not supported by "%s", try using CurlHttpClient instead.', __CLASS__));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidArgumentException(sprintf('Unsupported option "%s" passed to "%s", did you mean "%s"?', $name, __CLASS__, implode('", "', $alternatives ?: array_keys($defaultOptions))));
|
throw new InvalidArgumentException(sprintf('Unsupported option "%s" passed to "%s", did you mean "%s"?', $name, __CLASS__, implode('", "', $alternatives ?: array_keys($defaultOptions))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,74 @@ class CurlHttpClientTest extends HttpClientTestCase
|
|||||||
return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]);
|
return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testBindToPort()
|
||||||
|
{
|
||||||
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
$response = $client->request('GET', 'http://localhost:8057', ['bindto' => '127.0.0.1:9876']);
|
||||||
|
$response->getStatusCode();
|
||||||
|
|
||||||
|
$r = new \ReflectionProperty($response, 'handle');
|
||||||
|
$r->setAccessible(true);
|
||||||
|
|
||||||
|
$curlInfo = curl_getinfo($r->getValue($response));
|
||||||
|
|
||||||
|
self::assertSame('127.0.0.1', $curlInfo['local_ip']);
|
||||||
|
self::assertSame(9876, $curlInfo['local_port']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires PHP 7.2.17
|
||||||
|
*/
|
||||||
|
public function testHttp2PushVulcain()
|
||||||
|
{
|
||||||
|
$client = $this->getVulcainClient();
|
||||||
|
$logger = new TestLogger();
|
||||||
|
$client->setLogger($logger);
|
||||||
|
|
||||||
|
$responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [
|
||||||
|
'headers' => [
|
||||||
|
'Preload' => '/documents/*/id',
|
||||||
|
],
|
||||||
|
])->toArray();
|
||||||
|
|
||||||
|
foreach ($responseAsArray['documents'] as $document) {
|
||||||
|
$client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
$client->reset();
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'Request: "GET https://127.0.0.1:3000/json"',
|
||||||
|
'Queueing pushed response: "https://127.0.0.1:3000/json/1"',
|
||||||
|
'Queueing pushed response: "https://127.0.0.1:3000/json/2"',
|
||||||
|
'Queueing pushed response: "https://127.0.0.1:3000/json/3"',
|
||||||
|
'Response: "200 https://127.0.0.1:3000/json"',
|
||||||
|
'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"',
|
||||||
|
'Response: "200 https://127.0.0.1:3000/json/1"',
|
||||||
|
'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"',
|
||||||
|
'Response: "200 https://127.0.0.1:3000/json/2"',
|
||||||
|
'Accepting pushed response: "GET https://127.0.0.1:3000/json/3"',
|
||||||
|
'Response: "200 https://127.0.0.1:3000/json/3"',
|
||||||
|
];
|
||||||
|
$this->assertSame($expected, $logger->logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires PHP 7.2.17
|
||||||
|
*/
|
||||||
|
public function testHttp2PushVulcainWithUnusedResponse()
|
||||||
|
{
|
||||||
|
$client = $this->getVulcainClient();
|
||||||
|
$logger = new TestLogger();
|
||||||
|
$client->setLogger($logger);
|
||||||
|
|
||||||
|
$responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [
|
||||||
|
'headers' => [
|
||||||
|
'Preload' => '/documents/*/id',
|
||||||
|
],
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
public function testTimeoutIsNotAFatalError()
|
public function testTimeoutIsNotAFatalError()
|
||||||
{
|
{
|
||||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||||
|
@ -35,7 +35,7 @@ final class HttpClientKernel implements HttpKernelInterface
|
|||||||
|
|
||||||
public function __construct(HttpClientInterface $client = null)
|
public function __construct(HttpClientInterface $client = null)
|
||||||
{
|
{
|
||||||
if (!class_exists(HttpClient::class)) {
|
if (null === $client && !class_exists(HttpClient::class)) {
|
||||||
throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__));
|
throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,6 @@ final class HttpClientKernel implements HttpKernelInterface
|
|||||||
$response = $this->client->request($request->getMethod(), $request->getUri(), [
|
$response = $this->client->request($request->getMethod(), $request->getUri(), [
|
||||||
'headers' => $headers,
|
'headers' => $headers,
|
||||||
'body' => $body,
|
'body' => $body,
|
||||||
'max_redirects' => 0,
|
|
||||||
] + $request->attributes->get('http_client_options', []));
|
] + $request->attributes->get('http_client_options', []));
|
||||||
|
|
||||||
$response = new Response($response->getContent(!$catch), $response->getStatusCode(), $response->getHeaders(!$catch));
|
$response = new Response($response->getContent(!$catch), $response->getStatusCode(), $response->getHeaders(!$catch));
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
<?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\HttpKernel\Tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpKernel\HttpClientKernel;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||||
|
|
||||||
|
class HttpClientKernelTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testHandlePassesMaxRedirectsHttpClientOption()
|
||||||
|
{
|
||||||
|
$request = new Request();
|
||||||
|
$request->attributes->set('http_client_options', ['max_redirects' => 50]);
|
||||||
|
|
||||||
|
$response = $this->getMockBuilder(ResponseInterface::class)->getMock();
|
||||||
|
$response->expects($this->once())->method('getStatusCode')->willReturn(200);
|
||||||
|
|
||||||
|
$client = $this->getMockBuilder(HttpClientInterface::class)->getMock();
|
||||||
|
$client
|
||||||
|
->expects($this->once())
|
||||||
|
->method('request')
|
||||||
|
->willReturnCallback(function (string $method, string $uri, array $options) use ($request, $response) {
|
||||||
|
$this->assertSame($request->getMethod(), $method);
|
||||||
|
$this->assertSame($request->getUri(), $uri);
|
||||||
|
$this->assertArrayHasKey('max_redirects', $options);
|
||||||
|
$this->assertSame(50, $options['max_redirects']);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
});
|
||||||
|
|
||||||
|
$kernel = new HttpClientKernel($client);
|
||||||
|
$kernel->handle($request);
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@
|
|||||||
"symfony/deprecation-contracts": "^2.1",
|
"symfony/deprecation-contracts": "^2.1",
|
||||||
"symfony/error-handler": "^4.4|^5.0",
|
"symfony/error-handler": "^4.4|^5.0",
|
||||||
"symfony/event-dispatcher": "^5.0",
|
"symfony/event-dispatcher": "^5.0",
|
||||||
|
"symfony/http-client-contracts": "^1.1|^2",
|
||||||
"symfony/http-foundation": "^4.4|^5.0",
|
"symfony/http-foundation": "^4.4|^5.0",
|
||||||
"symfony/polyfill-ctype": "^1.8",
|
"symfony/polyfill-ctype": "^1.8",
|
||||||
"symfony/polyfill-php73": "^1.9",
|
"symfony/polyfill-php73": "^1.9",
|
||||||
|
@ -19,6 +19,19 @@ use Symfony\Component\Translation\Translator;
|
|||||||
|
|
||||||
class TranslatorTest extends TestCase
|
class TranslatorTest extends TestCase
|
||||||
{
|
{
|
||||||
|
private $defaultLocale;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->defaultLocale = \Locale::getDefault();
|
||||||
|
\Locale::setDefault('en');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
\Locale::setDefault($this->defaultLocale);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getInvalidLocalesTests
|
* @dataProvider getInvalidLocalesTests
|
||||||
*/
|
*/
|
||||||
|
@ -158,7 +158,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
|
|||||||
*/
|
*/
|
||||||
public function getLocale()
|
public function getLocale()
|
||||||
{
|
{
|
||||||
return $this->locale;
|
return $this->locale ?? \Locale::getDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,7 +78,11 @@ class TimezoneValidator extends ConstraintValidator
|
|||||||
private static function getPhpTimezones(int $zone, string $countryCode = null): array
|
private static function getPhpTimezones(int $zone, string $countryCode = null): array
|
||||||
{
|
{
|
||||||
if (null !== $countryCode) {
|
if (null !== $countryCode) {
|
||||||
return @\DateTimeZone::listIdentifiers($zone, $countryCode) ?: [];
|
try {
|
||||||
|
return @\DateTimeZone::listIdentifiers($zone, $countryCode) ?: [];
|
||||||
|
} catch (\ValueError $e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return \DateTimeZone::listIdentifiers($zone);
|
return \DateTimeZone::listIdentifiers($zone);
|
||||||
|
@ -110,6 +110,14 @@ class SplCaster
|
|||||||
|
|
||||||
$a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state';
|
$a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state';
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
} catch (\Error $e) {
|
||||||
|
if ('Object not initialized' !== $e->getMessage()) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
$a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state';
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ class Inline
|
|||||||
}
|
}
|
||||||
|
|
||||||
// some comments are allowed at the end
|
// some comments are allowed at the end
|
||||||
if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
|
if (preg_replace('/\s*#.*$/A', '', substr($value, $i))) {
|
||||||
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@ class Parser
|
|||||||
$this->refs = [];
|
$this->refs = [];
|
||||||
$this->skippedLineNumbers = [];
|
$this->skippedLineNumbers = [];
|
||||||
$this->locallySkippedLineNumbers = [];
|
$this->locallySkippedLineNumbers = [];
|
||||||
|
$this->totalNumberOfLines = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
@ -728,7 +729,7 @@ class Parser
|
|||||||
if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
|
if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
|
||||||
$modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
|
$modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
|
||||||
|
|
||||||
$data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs((int) $modifiers));
|
$data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), abs((int) $modifiers));
|
||||||
|
|
||||||
if ('' !== $matches['tag'] && '!' !== $matches['tag']) {
|
if ('' !== $matches['tag'] && '!' !== $matches['tag']) {
|
||||||
if ('!!binary' === $matches['tag']) {
|
if ('!!binary' === $matches['tag']) {
|
||||||
|
@ -837,6 +837,16 @@ class InlineTest extends TestCase
|
|||||||
self::assertSame('-0123456789', Inline::parse('-0123456789'));
|
self::assertSame('-0123456789', Inline::parse('-0123456789'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testParseCommentNotPrefixedBySpaces()
|
||||||
|
{
|
||||||
|
self::assertSame('foo', Inline::parse('"foo"#comment'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testParseUnquotedStringContainingHashTagNotPrefixedBySpace()
|
||||||
|
{
|
||||||
|
self::assertSame('foo#nocomment', Inline::parse('foo#nocomment'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider unquotedExclamationMarkThrowsProvider
|
* @dataProvider unquotedExclamationMarkThrowsProvider
|
||||||
*/
|
*/
|
||||||
|
@ -2437,6 +2437,39 @@ YAML;
|
|||||||
$this->parser->parse($yaml)
|
$this->parser->parse($yaml)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a regression test for a bug where a YAML block with a nested multiline string using | was parsed without
|
||||||
|
* a trailing \n when a shorter YAML document was parsed before.
|
||||||
|
*
|
||||||
|
* When a shorter document was parsed before, the nested string did not have a \n at the end of the string, because
|
||||||
|
* the Parser thought it was the end of the file, even though it is not.
|
||||||
|
*/
|
||||||
|
public function testParsingMultipleDocuments()
|
||||||
|
{
|
||||||
|
$shortDocument = 'foo: bar';
|
||||||
|
$longDocument = <<<YAML
|
||||||
|
a:
|
||||||
|
b: |
|
||||||
|
row
|
||||||
|
row2
|
||||||
|
c: d
|
||||||
|
YAML;
|
||||||
|
|
||||||
|
$expected = ['a' => ['b' => "row\nrow2\n"], 'c' => 'd'];
|
||||||
|
|
||||||
|
// The parser was not used before, so there is a new line after row2
|
||||||
|
$this->assertSame($expected, $this->parser->parse($longDocument));
|
||||||
|
|
||||||
|
$parser = new Parser();
|
||||||
|
// The first parsing set and fixed the totalNumberOfLines in the Parser before, so parsing the short document here
|
||||||
|
// to reproduce the issue. If the issue would not have been fixed, the next assertion will fail
|
||||||
|
$parser->parse($shortDocument);
|
||||||
|
|
||||||
|
// After the total number of lines has been rset the result will be the same as if a new parser was used
|
||||||
|
// (before, there was no \n after row2)
|
||||||
|
$this->assertSame($expected, $parser->parse($longDocument));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class B
|
class B
|
||||||
|
Reference in New Issue
Block a user