Merge branch '4.3' into 4.4

* 4.3: (34 commits)
  [PhpunitBridge] Read environment variable from superglobals
  [Bridge/PhpUnit] Fix PHP5.5 compat
  [PhpUnitBridge] More accurate grouping
  fixed CS
  Extract unrecoverable exception to interface
  [FrameworkBundle] Fix calling Client::getProfile() before sending a request
  Fix type error
  [Security/Core] require libsodium >= 1.0.14
  [Workflow] re-add workflow.definition tag to workflow services
  [Security/Core] Don't use ParagonIE_Sodium_Compat
  revert #30525 due to performance penalty
  collect called listeners information only once
  [Lock] fix missing inherit docs in RedisStore
  [Messenger] fix retrying handlers using DoctrineTransactionMiddleware
  [Mailgun Mailer] fixed issue when using html body
  [HttpClient] fix timing measurements with NativeHttpClient
  [HttpClient] fix dealing with 1xx informational responses
  add test to avoid regressions
  fix mirroring directory into parent directory
  fix typos
  ...
This commit is contained in:
Nicolas Grekas 2019-06-26 11:30:56 +02:00
commit 55c0b02587
60 changed files with 612 additions and 227 deletions

View File

@ -138,6 +138,9 @@ class DoctrineDataCollector extends DataCollector
if (!\is_array($query['params'])) {
$query['params'] = [$query['params']];
}
if (!\is_array($query['types'])) {
$query['types'] = [];
}
foreach ($query['params'] as $j => $param) {
if (isset($query['types'][$j])) {
// Transform the param according to the type

View File

@ -13,7 +13,9 @@ namespace Symfony\Bridge\Doctrine\Messenger;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\HandledStamp;
/**
* Wraps all handlers in a single doctrine transaction.
@ -36,6 +38,12 @@ class DoctrineTransactionMiddleware extends AbstractDoctrineMiddleware
} catch (\Throwable $exception) {
$entityManager->getConnection()->rollBack();
if ($exception instanceof HandlerFailedException) {
// Remove all HandledStamp from the envelope so the retry will execute all handlers again.
// When a handler fails, the queries of allegedly successful previous handlers just got rolled back.
throw new HandlerFailedException($exception->getEnvelope()->withoutAll(HandledStamp::class), $exception->getNestedExceptions());
}
throw $exception;
}
}

View File

@ -102,6 +102,18 @@ class DoctrineDataCollectorTest extends TestCase
$this->assertTrue($collectedQueries['default'][1]['explainable']);
}
public function testCollectQueryWithNoTypes()
{
$queries = [
['sql' => 'SET sql_mode=(SELECT REPLACE(@@sql_mode, \'ONLY_FULL_GROUP_BY\', \'\'))', 'params' => [], 'types' => null, 'executionMS' => 1],
];
$c = $this->createCollector($queries);
$c->collect(new Request(), new Response());
$collectedQueries = $c->getQueries();
$this->assertSame([], $collectedQueries['default'][0]['types']);
}
public function testReset()
{
$queries = [

View File

@ -139,10 +139,13 @@ class DeprecationErrorHandler
$group = 'unsilenced';
} elseif ($deprecation->isLegacy(self::$utilPrefix)) {
$group = 'legacy';
} elseif (!$deprecation->isSelf()) {
$group = $deprecation->isIndirect() ? 'remaining indirect' : 'remaining direct';
} else {
$group = 'remaining self';
$group = [
Deprecation::TYPE_SELF => 'remaining self',
Deprecation::TYPE_DIRECT => 'remaining direct',
Deprecation::TYPE_INDIRECT => 'remaining indirect',
Deprecation::TYPE_UNDETERMINED => 'other',
][$deprecation->getType()];
}
if ($this->getConfiguration()->shouldDisplayStackTrace($msg)) {
@ -216,7 +219,13 @@ class DeprecationErrorHandler
return $this->configuration;
}
if (false === $mode = $this->mode) {
$mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
if (isset($_SERVER['SYMFONY_DEPRECATIONS_HELPER'])) {
$mode = $_SERVER['SYMFONY_DEPRECATIONS_HELPER'];
} elseif (isset($_ENV['SYMFONY_DEPRECATIONS_HELPER'])) {
$mode = $_ENV['SYMFONY_DEPRECATIONS_HELPER'];
} else {
$mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
}
}
if ('strict' === $mode) {
return $this->configuration = Configuration::inStrictMode();

View File

@ -18,6 +18,15 @@ use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor;
*/
class Deprecation
{
const PATH_TYPE_VENDOR = 'path_type_vendor';
const PATH_TYPE_SELF = 'path_type_internal';
const PATH_TYPE_UNDETERMINED = 'path_type_undetermined';
const TYPE_SELF = 'type_self';
const TYPE_DIRECT = 'type_direct';
const TYPE_INDIRECT = 'type_indirect';
const TYPE_UNDETERMINED = 'type_undetermined';
/**
* @var array
*/
@ -39,13 +48,21 @@ class Deprecation
private $originMethod;
/**
* @var bool
* @var string one of the PATH_TYPE_* constants
*/
private $self;
private $triggeringFilePathType;
/** @var string[] absolute paths to vendor directories */
private static $vendors;
/**
* @var string[] absolute paths to source or tests of the project. This
* excludes cache directories, because it is based on
* autoloading rules and cache systems typically do not use
* those.
*/
private static $internalPaths;
/**
* @param string $message
* @param string $file
@ -59,7 +76,7 @@ class Deprecation
// No-op
}
$line = $trace[$i];
$this->self = !$this->pathOriginatesFromVendor($file);
$this->trigerringFilePathType = $this->getPathType($file);
if (isset($line['object']) || isset($line['class'])) {
if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) {
$parsedMsg = unserialize($this->message);
@ -70,8 +87,9 @@ class Deprecation
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
// then we need to use the serialized information to determine
// if the error has been triggered from vendor code.
$this->self = isset($parsedMsg['triggering_file'])
&& $this->pathOriginatesFromVendor($parsedMsg['triggering_file']);
if (isset($parsedMsg['triggering_file'])) {
$this->trigerringFilePathType = $this->getPathType($parsedMsg['triggering_file']);
}
return;
}
@ -101,14 +119,6 @@ class Deprecation
return isset($this->originClass);
}
/**
* @return bool
*/
public function isSelf()
{
return $this->self;
}
/**
* @return string
*/
@ -163,10 +173,16 @@ class Deprecation
* Tells whether both the calling package and the called package are vendor
* packages.
*
* @return bool
* @return string
*/
public function isIndirect()
public function getType()
{
if (self::PATH_TYPE_SELF === $this->trigerringFilePathType) {
return self::TYPE_SELF;
}
if (self::PATH_TYPE_UNDETERMINED === $this->trigerringFilePathType) {
return self::TYPE_UNDETERMINED;
}
$erroringFile = $erroringPackage = null;
foreach ($this->trace as $line) {
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
@ -179,13 +195,16 @@ class Deprecation
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
continue;
}
if (!$this->pathOriginatesFromVendor($file)) {
return false;
if (self::PATH_TYPE_SELF === $this->getPathType($file)) {
return self::TYPE_DIRECT;
}
if (self::PATH_TYPE_UNDETERMINED === $this->getPathType($file)) {
return self::TYPE_UNDETERMINED;
}
if (null !== $erroringFile && null !== $erroringPackage) {
$package = $this->getPackage($file);
if ('composer' !== $package && $package !== $erroringPackage) {
return true;
return self::TYPE_INDIRECT;
}
continue;
}
@ -193,11 +212,11 @@ class Deprecation
$erroringPackage = $this->getPackage($file);
}
return false;
return self::TYPE_DIRECT;
}
/**
* pathOriginatesFromVendor() should always be called prior to calling this method.
* getPathType() should always be called prior to calling this method.
*
* @param string $path
*
@ -237,6 +256,15 @@ class Deprecation
$v = \dirname(\dirname($r->getFileName()));
if (file_exists($v.'/composer/installed.json')) {
self::$vendors[] = $v;
$loader = require $v.'/autoload.php';
$paths = self::getSourcePathsFromPrefixes(array_merge($loader->getPrefixes(), $loader->getPrefixesPsr4()));
}
}
}
foreach ($paths as $path) {
foreach (self::$vendors as $vendor) {
if (0 !== strpos($path, $vendor)) {
self::$internalPaths[] = $path;
}
}
}
@ -245,24 +273,41 @@ class Deprecation
return self::$vendors;
}
private static function getSourcePathsFromPrefixes(array $prefixesByNamespace)
{
foreach ($prefixesByNamespace as $prefixes) {
foreach ($prefixes as $prefix) {
if (false !== realpath($prefix)) {
yield realpath($prefix);
}
}
}
}
/**
* @param string $path
*
* @return bool
* @return string
*/
private function pathOriginatesFromVendor($path)
private function getPathType($path)
{
$realPath = realpath($path);
if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) {
return true;
return self::PATH_TYPE_UNDETERMINED;
}
foreach (self::getVendors() as $vendor) {
if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
return true;
return self::PATH_TYPE_VENDOR;
}
}
return false;
foreach (self::$internalPaths as $internalPath) {
if (0 === strpos($realPath, $internalPath)) {
return self::PATH_TYPE_SELF;
}
}
return self::PATH_TYPE_UNDETERMINED;
}
/**
@ -281,19 +326,4 @@ class Deprecation
"\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $exception->getTraceAsString()).
"\n";
}
private function getPackageFromLine(array $line)
{
if (!isset($line['file'])) {
return 'internal function';
}
if (!$this->pathOriginatesFromVendor($line['file'])) {
return 'source code';
}
try {
return $this->getPackage($line['file']);
} catch (\RuntimeException $e) {
return 'unknown';
}
}
}

View File

@ -27,7 +27,7 @@ class DeprecationTest extends TestCase
public function testItCanTellWhetherItIsInternal()
{
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
$this->assertTrue($deprecation->isSelf());
$this->assertSame(Deprecation::TYPE_SELF, $deprecation->getType());
}
public function testLegacyTestMethodIsDetectedAsSuch()
@ -46,7 +46,7 @@ class DeprecationTest extends TestCase
public function testItRulesOutFilesOutsideVendorsAsIndirect()
{
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
$this->assertFalse($deprecation->isIndirect());
$this->assertNotSame(Deprecation::TYPE_INDIRECT, $deprecation->getType());
}
/**

View File

@ -73,15 +73,13 @@ Unsilenced deprecation notices (3)
1x: unsilenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Remaining self deprecation notices (1)
Legacy deprecation notices (1)
Other deprecation notices (2)
1x: root deprecation
1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Legacy deprecation notices (1)
Other deprecation notices (1)
1x: root deprecation
I get precedence over any exit statements inside the deprecation error handler.

View File

@ -1,9 +1,9 @@
--TEST--
Test DeprecationErrorHandler in weak mode
Test DeprecationErrorHandler in disabled mode
--FILE--
<?php
putenv('SYMFONY_DEPRECATIONS_HELPER=disabled');
$_SERVER['SYMFONY_DEPRECATIONS_HELPER'] = 'disabled';
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');

View File

@ -0,0 +1,24 @@
--TEST--
Test DeprecationErrorHandler in disabled mode (via putenv)
--FILE--
<?php
$_ENV['SYMFONY_DEPRECATIONS_HELPER'] = 'disabled';
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');
$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
require_once __DIR__.'/../../bootstrap.php';
echo (int) set_error_handler('var_dump');
echo (int) class_exists('Symfony\Bridge\PhpUnit\DeprecationErrorHandler', false);
?>
--EXPECTF--
00

View File

@ -0,0 +1,24 @@
--TEST--
Test DeprecationErrorHandler in disabled mode (via putenv)
--FILE--
<?php
putenv('SYMFONY_DEPRECATIONS_HELPER=disabled');
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');
$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
require_once __DIR__.'/../../bootstrap.php';
echo (int) set_error_handler('var_dump');
echo (int) class_exists('Symfony\Bridge\PhpUnit\DeprecationErrorHandler', false);
?>
--EXPECTF--
00

View File

@ -1,3 +1,5 @@
<?php
require_once __DIR__.'/composer/autoload_real.php';
return ComposerAutoloaderInitFake::getLoader();

View File

@ -1,5 +1,22 @@
<?php
class ComposerLoaderFake
{
public function getPrefixes()
{
return [];
}
public function getPrefixesPsr4()
{
return [];
}
}
class ComposerAutoloaderInitFake
{
public static function getLoader()
{
return new ComposerLoaderFake();
}
}

View File

@ -61,14 +61,12 @@ Unsilenced deprecation notices (3)
1x: unsilenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Remaining self deprecation notices (1)
Legacy deprecation notices (1)
Other deprecation notices (2)
1x: root deprecation
1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Legacy deprecation notices (1)
Other deprecation notices (1)
1x: root deprecation

View File

@ -73,17 +73,15 @@ Unsilenced deprecation notices (3)
1x: unsilenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Remaining self deprecation notices (1)
Legacy deprecation notices (1)
Other deprecation notices (2)
1x: root deprecation
1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Legacy deprecation notices (1)
Other deprecation notices (1)
1x: root deprecation
Shutdown-time deprecations:
Other deprecation notices (1)

View File

@ -61,14 +61,13 @@ Unsilenced deprecation notices (3)
1x: unsilenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Remaining self deprecation notices (1)
Legacy deprecation notices (1)
Other deprecation notices (2)
1x: root deprecation
1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Legacy deprecation notices (1)
Other deprecation notices (1)
1x: root deprecation

View File

@ -114,6 +114,7 @@
<div class="form-group{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
{{- form_label(form) }} {# -#}
{{ form_widget(form, widget_attr) }} {# -#}
{{- form_help(form) -}}
{{ form_errors(form) }} {# -#}
</div> {# -#}
{%- endblock form_row %}

View File

@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
@ -43,9 +44,9 @@ class DebugExtension extends Extension
->addMethodCall('setMinDepth', [$config['min_depth']])
->addMethodCall('setMaxString', [$config['max_string_length']]);
if (method_exists(ReflectionClass::class, 'unsetClosureFileInfo')) {
if (method_exists(ReflectionCaster::class, 'unsetClosureFileInfo')) {
$container->getDefinition('var_dumper.cloner')
->addMethodCall('addCasters', ReflectionClass::UNSET_CLOSURE_FILE_INFO);
->addMethodCall('addCasters', [ReflectionCaster::UNSET_CLOSURE_FILE_INFO]);
}
if (method_exists(HtmlDumper::class, 'setTheme') && 'dark' !== $config['theme']) {

View File

@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
use Symfony\Bundle\DebugBundle\DependencyInjection\DebugExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
class DebugExtensionTest extends TestCase
{
@ -36,6 +37,39 @@ class DebugExtensionTest extends TestCase
$this->assertSame($expectedTags, $container->getDefinition('data_collector.dump')->getTag('data_collector'));
}
public function testUnsetClosureFileInfoShouldBeRegisteredInVarCloner()
{
if (!method_exists(ReflectionCaster::class, 'unsetClosureFileInfo')) {
$this->markTestSkipped('Method not available');
}
$container = $this->createContainer();
$container->registerExtension(new DebugExtension());
$container->loadFromExtension('debug', []);
$this->compileContainer($container);
$definition = $container->getDefinition('var_dumper.cloner');
$called = false;
foreach ($definition->getMethodCalls() as $call) {
if ('addCasters' !== $call[0]) {
continue;
}
$argument = $call[1][0] ?? null;
if (null === $argument) {
continue;
}
if (['Closure' => ReflectionCaster::class.'::unsetClosureFileInfo'] === $argument) {
$called = true;
break;
}
}
$this->assertTrue($called);
}
private function createContainer()
{
$container = new ContainerBuilder(new ParameterBag([

View File

@ -23,7 +23,7 @@ CHANGELOG
* [BC Break] When using Messenger, the default transport changed from
using Symfony's serializer service to use `PhpSerializer`, which uses
PHP's native `serialize()` and `unserialize()` functions. To use the
original serialization method, set the `framework.messenger.defaut_serializer`
original serialization method, set the `framework.messenger.default_serializer`
config option to `messenger.transport.symfony_serializer`. Or set the
`serializer` option under one specific `transport`.
* [BC Break] The `framework.messenger.serializer` config key changed to

View File

@ -66,7 +66,7 @@ class Client extends HttpKernelBrowser
*/
public function getProfile()
{
if (!$this->kernel->getContainer()->has('profiler')) {
if (null === $this->response || !$this->kernel->getContainer()->has('profiler')) {
return false;
}

View File

@ -652,6 +652,10 @@ class FrameworkExtension extends Extension
$definitionDefinition->addArgument($transitions);
$definitionDefinition->addArgument($initialMarking);
$definitionDefinition->addArgument($metadataStoreDefinition);
$definitionDefinition->addTag('workflow.definition', [
'name' => $name,
'type' => $type,
]);
// Create MarkingStore
if (isset($workflow['marking_store']['type'])) {

View File

@ -311,6 +311,8 @@ abstract class FrameworkExtensionTest extends TestCase
$workflowDefinition->getArgument(0),
'Places are passed to the workflow definition'
);
$this->assertSame(['workflow.definition' => [['name' => 'legacy', 'type' => 'state_machine']]], $workflowDefinition->getTags());
}
/**

View File

@ -28,9 +28,9 @@ class ProfilerTest extends WebTestCase
// enable the profiler for the next request
$client->enableProfiler();
$crawler = $client->request('GET', '/profiler');
$profile = $client->getProfile();
$this->assertInternalType('object', $profile);
$this->assertFalse($client->getProfile());
$client->request('GET', '/profiler');
$this->assertInternalType('object', $client->getProfile());
$client->request('GET', '/profiler');
$this->assertFalse($client->getProfile());

View File

@ -48,7 +48,7 @@
"symfony/security-http": "^3.4|^4.0|^5.0",
"symfony/serializer": "^4.3|^5.0",
"symfony/stopwatch": "^3.4|^4.0|^5.0",
"symfony/translation": "^4.2|^5.0",
"symfony/translation": "^4.3|^5.0",
"symfony/templating": "^3.4|^4.0|^5.0",
"symfony/twig-bundle": "^3.4|^4.0|^5.0",
"symfony/validator": "^4.1|^5.0",

View File

@ -12,8 +12,8 @@
namespace Symfony\Component\Cache\Tests\Adapter;
use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
use Symfony\Component\Cache\Simple\FilesystemCache;
use Symfony\Component\Cache\Simple\ArrayCache;
use Symfony\Component\Cache\Simple\FilesystemCache;
/**
* @group time-sensitive

View File

@ -314,7 +314,7 @@ class DotenvTest extends TestCase
$dotenv->load(__DIR__);
}
public function testServerSuperglobalIsNotOverriden()
public function testServerSuperglobalIsNotOverridden()
{
$originalValue = $_SERVER['argc'];
@ -324,7 +324,7 @@ class DotenvTest extends TestCase
$this->assertSame($originalValue, $_SERVER['argc']);
}
public function testEnvVarIsNotOverriden()
public function testEnvVarIsNotOverridden()
{
putenv('TEST_ENV_VAR=original_value');
$_SERVER['TEST_ENV_VAR'] = 'original_value';
@ -335,7 +335,7 @@ class DotenvTest extends TestCase
$this->assertSame('original_value', getenv('TEST_ENV_VAR'));
}
public function testHttpVarIsPartiallyOverriden()
public function testHttpVarIsPartiallyOverridden()
{
$_SERVER['HTTP_TEST_ENV_VAR'] = 'http_value';

View File

@ -569,14 +569,15 @@ class Filesystem
}
$this->mkdir($targetDir);
$targetDirInfo = new \SplFileInfo($targetDir);
$filesCreatedWhileMirroring = [];
foreach ($iterator as $file) {
if ($file->getPathname() === $targetDir || $file->getRealPath() === $targetDir || 0 === strpos($file->getRealPath(), $targetDirInfo->getRealPath())) {
if ($file->getPathname() === $targetDir || $file->getRealPath() === $targetDir || isset($filesCreatedWhileMirroring[$file->getRealPath()])) {
continue;
}
$target = $targetDir.substr($file->getPathname(), $originDirLen);
$filesCreatedWhileMirroring[$target] = true;
if (!$copyOnWindows && is_link($file)) {
$this->symlink($file->getLinkTarget(), $target);

View File

@ -1362,6 +1362,22 @@ class FilesystemTest extends FilesystemTestCase
$this->assertFalse($this->filesystem->exists($targetPath.'target'));
}
public function testMirrorFromSubdirectoryInToParentDirectory()
{
$targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR;
$sourcePath = $targetPath.'bar'.\DIRECTORY_SEPARATOR;
$file1 = $sourcePath.'file1';
$file2 = $sourcePath.'file2';
$this->filesystem->mkdir($sourcePath);
file_put_contents($file1, 'FILE1');
file_put_contents($file2, 'FILE2');
$this->filesystem->mirror($sourcePath, $targetPath);
$this->assertFileEquals($file1, $targetPath.'file1');
}
/**
* @dataProvider providePathsForIsAbsolutePath
*/

View File

@ -0,0 +1,19 @@
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="28">
<source>This form should not contain extra fields.</source>
<target>Form ekstra alanlar içeremez.</target>
</trans-unit>
<trans-unit id="29">
<source>The uploaded file was too large. Please try to upload a smaller file.</source>
<target>Yüklenen dosya boyutu çok yüksek. Lütfen daha küçük bir dosya yüklemeyi deneyin.</target>
</trans-unit>
<trans-unit id="30">
<source>The CSRF token is invalid. Please try to resubmit the form.</source>
<target>CSRF fişi geçersiz. Formu tekrar göndermeyi deneyin.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -98,9 +98,9 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
'http_code' => 0,
'redirect_count' => 0,
'start_time' => 0.0,
'fopen_time' => 0.0,
'connect_time' => 0.0,
'redirect_time' => 0.0,
'pretransfer_time' => 0.0,
'starttransfer_time' => 0.0,
'total_time' => 0.0,
'namelookup_time' => 0.0,
@ -118,7 +118,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
$onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info) {
$progressInfo = $info;
$progressInfo['url'] = implode('', $info['url']);
unset($progressInfo['fopen_time'], $progressInfo['size_body']);
unset($progressInfo['size_body']);
if ($progress && -1 === $progress[0]) {
// Response completed
@ -133,14 +133,14 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
// Always register a notification callback to compute live stats about the response
$notification = static function (int $code, int $severity, ?string $msg, int $msgCode, int $dlNow, int $dlSize) use ($onProgress, &$info) {
$now = microtime(true);
$info['total_time'] = $now - $info['start_time'];
$info['total_time'] = microtime(true) - $info['start_time'];
if (STREAM_NOTIFY_PROGRESS === $code) {
$info['starttransfer_time'] = $info['starttransfer_time'] ?: $info['total_time'];
$info['size_upload'] += $dlNow ? 0 : $info['size_body'];
$info['size_download'] = $dlNow;
} elseif (STREAM_NOTIFY_CONNECT === $code) {
$info['connect_time'] += $now - $info['fopen_time'];
$info['connect_time'] = $info['total_time'];
$info['debug'] .= $info['request_header'];
unset($info['request_header']);
} else {
@ -310,7 +310,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
throw new TransportException(sprintf('Could not resolve host "%s".', $host));
}
$info['namelookup_time'] += microtime(true) - $now;
$info['namelookup_time'] = microtime(true) - ($info['start_time'] ?: $now);
$multi->dnsCache[$host] = $ip = $ip[0];
$info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
} else {
@ -368,10 +368,9 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
return null;
}
$now = microtime(true);
$info['url'] = $url;
++$info['redirect_count'];
$info['redirect_time'] = $now - $info['start_time'];
$info['redirect_time'] = microtime(true) - $info['start_time'];
// Do like curl and browsers: turn POST to GET on 301, 302 and 303
if (\in_array($info['http_code'], [301, 302, 303], true)) {

View File

@ -55,6 +55,7 @@ final class CurlResponse implements ResponseInterface
$this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
$info = &$this->info;
$headers = &$this->headers;
$debugBuffer = $this->debugBuffer;
if (!$info['response_headers']) {
// Used to keep track of what we're waiting for
@ -88,9 +89,11 @@ final class CurlResponse implements ResponseInterface
if ($onProgress = $options['on_progress']) {
$url = isset($info['url']) ? ['url' => $info['url']] : [];
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi) {
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi, $debugBuffer) {
try {
$onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info);
rewind($debugBuffer);
$debug = ['debug' => stream_get_contents($debugBuffer)];
$onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info + $debug);
} catch (\Throwable $e) {
$multi->handlesActivity[(int) $ch][] = null;
$multi->handlesActivity[(int) $ch][] = $e;
@ -148,12 +151,6 @@ final class CurlResponse implements ResponseInterface
if (!$info = $this->finalInfo) {
self::perform($this->multi);
if ('debug' === $type) {
rewind($this->debugBuffer);
return stream_get_contents($this->debugBuffer);
}
$info = array_merge($this->info, curl_getinfo($this->handle));
$info['url'] = $this->info['url'] ?? $info['url'];
$info['redirect_url'] = $this->info['redirect_url'] ?? null;
@ -164,9 +161,10 @@ final class CurlResponse implements ResponseInterface
$info['starttransfer_time'] = 0.0;
}
rewind($this->debugBuffer);
$info['debug'] = stream_get_contents($this->debugBuffer);
if (!\in_array(curl_getinfo($this->handle, CURLINFO_PRIVATE), ['headers', 'content'], true)) {
rewind($this->debugBuffer);
$info['debug'] = stream_get_contents($this->debugBuffer);
curl_setopt($this->handle, CURLOPT_VERBOSE, false);
rewind($this->debugBuffer);
ftruncate($this->debugBuffer, 0);
@ -289,7 +287,19 @@ final class CurlResponse implements ResponseInterface
// Regular header line: add it to the list
self::addResponseHeaders([substr($data, 0, -2)], $info, $headers);
if (0 === strpos($data, 'HTTP') && 300 <= $info['http_code'] && $info['http_code'] < 400) {
if (0 !== strpos($data, 'HTTP/')) {
if (0 === stripos($data, 'Location:')) {
$location = trim(substr($data, 9, -2));
}
return \strlen($data);
}
if (\function_exists('openssl_x509_read') && $certinfo = curl_getinfo($ch, CURLINFO_CERTINFO)) {
$info['peer_certificate_chain'] = array_map('openssl_x509_read', array_column($certinfo, 'Cert'));
}
if (300 <= $info['http_code'] && $info['http_code'] < 400) {
if (curl_getinfo($ch, CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) {
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
} elseif (303 === $info['http_code'] || ('POST' === $info['http_method'] && \in_array($info['http_code'], [301, 302], true))) {
@ -298,15 +308,14 @@ final class CurlResponse implements ResponseInterface
}
}
if (0 === stripos($data, 'Location:')) {
$location = trim(substr($data, 9, -2));
}
return \strlen($data);
}
// End of headers: handle redirects and add to the activity list
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if (200 > $statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE)) {
return \strlen($data);
}
$info['redirect_url'] = null;
if (300 <= $statusCode && $statusCode < 400 && null !== $location) {
@ -336,10 +345,6 @@ final class CurlResponse implements ResponseInterface
return 0;
}
if (\function_exists('openssl_x509_read') && $certinfo = curl_getinfo($ch, CURLINFO_CERTINFO)) {
$info['peer_certificate_chain'] = array_map('openssl_x509_read', array_column($certinfo, 'Cert'));
}
curl_setopt($ch, CURLOPT_PRIVATE, 'content');
} elseif (null !== $info['redirect_url'] && $logger) {
$logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));

View File

@ -78,18 +78,12 @@ final class NativeResponse implements ResponseInterface
if (!$info = $this->finalInfo) {
self::perform($this->multi);
if ('debug' === $type) {
return $this->info['debug'];
}
$info = $this->info;
$info['url'] = implode('', $info['url']);
unset($info['fopen_time'], $info['size_body'], $info['request_header']);
unset($info['size_body'], $info['request_header']);
if (null === $this->buffer) {
$this->finalInfo = $info;
} else {
unset($info['debug']);
}
}
@ -134,7 +128,6 @@ final class NativeResponse implements ResponseInterface
$this->info['request_header'] .= implode("\r\n", $context['http']['header'])."\r\n\r\n";
// Send request and follow redirects when needed
$this->info['fopen_time'] = microtime(true);
$this->handle = $h = fopen($url, 'r', false, $this->context);
self::addResponseHeaders($http_response_header, $this->info, $this->headers, $this->info['debug']);
$url = ($this->resolveRedirect)($this->multi, $this->headers['location'][0] ?? null, $this->context);
@ -152,7 +145,7 @@ final class NativeResponse implements ResponseInterface
return;
} finally {
$this->info['starttransfer_time'] = $this->info['total_time'] = microtime(true) - $this->info['start_time'];
$this->info['pretransfer_time'] = $this->info['total_time'] = microtime(true) - $this->info['start_time'];
restore_error_handler();
}
@ -273,6 +266,7 @@ final class NativeResponse implements ResponseInterface
if (null !== $e || !$remaining || feof($h)) {
// Stream completed
$info['total_time'] = microtime(true) - $info['start_time'];
$info['starttransfer_time'] = $info['starttransfer_time'] ?: $info['total_time'];
if ($onProgress) {
try {

View File

@ -253,7 +253,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function getBag($name)
{
return $this->storage->getBag($name)->getBag();
$bag = $this->storage->getBag($name);
return method_exists($bag, 'getBag') ? $bag->getBag() : $bag;
}
/**

View File

@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionBagProxy;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
/**
@ -260,4 +261,28 @@ class SessionTest extends TestCase
$flash->get('hello');
$this->assertTrue($this->session->isEmpty());
}
public function testGetBagWithBagImplementingGetBag()
{
$bag = new AttributeBag();
$bag->setName('foo');
$storage = new MockArraySessionStorage();
$storage->registerBag($bag);
$this->assertSame($bag, (new Session($storage))->getBag('foo'));
}
public function testGetBagWithBagNotImplementingGetBag()
{
$data = [];
$bag = new AttributeBag();
$bag->setName('foo');
$storage = new MockArraySessionStorage();
$storage->registerBag(new SessionBagProxy($bag, $data, $usageIndex));
$this->assertSame($bag, (new Session($storage))->getBag('foo'));
}
}

View File

@ -71,6 +71,9 @@ class RedisStore implements StoreInterface
$this->checkNotExpired($key);
}
/**
* {@inheritdoc}
*/
public function waitAndSave(Key $key)
{
throw new InvalidArgumentException(sprintf('The store "%s" does not supports blocking locks.', \get_class($this)));

View File

@ -68,7 +68,7 @@ class MailgunTransport extends AbstractApiTransport
{
$headers = $email->getHeaders();
$html = $email->getHtmlBody();
if (null !== $html) {
if (null !== $html && \is_resource($html)) {
if (stream_get_meta_data($html)['seekable'] ?? false) {
rewind($html);
}

View File

@ -71,11 +71,18 @@ class TransportTest extends TestCase
Transport::fromDsn('some://');
}
public function testNoScheme()
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The "//sendmail" mailer DSN must contain a transport scheme.');
Transport::fromDsn('//sendmail');
}
public function testFromInvalidDsnNoHost()
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The "?!" mailer DSN must contain a mailer name.');
Transport::fromDsn('?!');
$this->expectExceptionMessage('The "file:///some/path" mailer DSN must contain a mailer name.');
Transport::fromDsn('file:///some/path');
}
public function testFromInvalidTransportName()
@ -168,6 +175,19 @@ class TransportTest extends TestCase
$transport = Transport::fromDsn('api://'.urlencode('u$er').':'.urlencode('pa$s').'@mailgun?region=us', $dispatcher, $client, $logger);
$transport->send($message);
$message = (new Email())->from('me@me.com')->to('you@you.com')->subject('hello')->html('test');
$client = $this->createMock(HttpClientInterface::class);
$client->expects($this->once())->method('request')->with('POST', 'https://api.mailgun.net/v3/pa%24s/messages')->willReturn($response);
$transport = Transport::fromDsn('api://'.urlencode('u$er').':'.urlencode('pa$s').'@mailgun?region=us', $dispatcher, $client, $logger);
$transport->send($message);
$stream = fopen('data://text/plain,'.$message->getTextBody(), 'r');
$message = (new Email())->from('me@me.com')->to('you@you.com')->subject('hello')->html($stream);
$client = $this->createMock(HttpClientInterface::class);
$client->expects($this->once())->method('request')->with('POST', 'https://api.mailgun.net/v3/pa%24s/messages')->willReturn($response);
$transport = Transport::fromDsn('api://'.urlencode('u$er').':'.urlencode('pa$s').'@mailgun?region=us', $dispatcher, $client, $logger);
$transport->send($message);
$this->expectException(LogicException::class);
Transport::fromDsn('foo://mailgun');
}

View File

@ -64,6 +64,10 @@ class Transport
throw new InvalidArgumentException(sprintf('The "%s" mailer DSN is invalid.', $dsn));
}
if (!isset($parsedDsn['scheme'])) {
throw new InvalidArgumentException(sprintf('The "%s" mailer DSN must contain a transport scheme.', $dsn));
}
if (!isset($parsedDsn['host'])) {
throw new InvalidArgumentException(sprintf('The "%s" mailer DSN must contain a mailer name.', $dsn));
}

View File

@ -17,7 +17,7 @@ namespace Symfony\Component\Messenger\Exception;
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class DelayedMessageHandlingException extends \RuntimeException implements ExceptionInterface
class DelayedMessageHandlingException extends RuntimeException
{
private $exceptions;

View File

@ -16,6 +16,6 @@ namespace Symfony\Component\Messenger\Exception;
*
* @experimental in 4.3
*/
class MessageDecodingFailedException extends \InvalidArgumentException implements ExceptionInterface
class MessageDecodingFailedException extends InvalidArgumentException
{
}

View File

@ -16,6 +16,6 @@ namespace Symfony\Component\Messenger\Exception;
*
* @experimental in 4.3
*/
class NoHandlerForMessageException extends \LogicException implements ExceptionInterface
class NoHandlerForMessageException extends LogicException
{
}

View File

@ -16,6 +16,6 @@ namespace Symfony\Component\Messenger\Exception;
*
* @experimental in 4.3
*/
class UnknownSenderException extends \InvalidArgumentException implements ExceptionInterface
class UnknownSenderException extends InvalidArgumentException
{
}

View File

@ -0,0 +1,26 @@
<?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\Messenger\Exception;
/**
* Marker interface for exceptions to indicate that handling a message will continue to fail.
*
* If something goes wrong while handling a message that's received from a transport
* and the message should not be retried, a handler can throw such an exception.
*
* @author Tobias Schultze <http://tobion.de>
*
* @experimental in 4.3
*/
interface UnrecoverableExceptionInterface extends \Throwable
{
}

View File

@ -12,15 +12,12 @@
namespace Symfony\Component\Messenger\Exception;
/**
* Thrown while handling a message to indicate that handling will continue to fail.
*
* If something goes wrong while handling a message that's received from a transport
* and the message should not be retried, a handler can throw this exception.
* A concrete implementation of UnrecoverableExceptionInterface that can be used directly.
*
* @author Frederic Bouchery <frederic@bouchery.fr>
*
* @experimental in 4.3
*/
class UnrecoverableMessageHandlingException extends RuntimeException
class UnrecoverableMessageHandlingException extends RuntimeException implements UnrecoverableExceptionInterface
{
}

View File

@ -18,7 +18,7 @@ use Symfony\Component\Validator\ConstraintViolationListInterface;
*
* @experimental in 4.3
*/
class ValidationFailedException extends \RuntimeException implements ExceptionInterface
class ValidationFailedException extends RuntimeException
{
private $violations;
private $violatingMessage;

View File

@ -112,7 +112,7 @@ final class QueuedEnvelope
public function __construct(Envelope $envelope, StackInterface $stack)
{
$this->envelope = $envelope;
$this->envelope = $envelope->withoutAll(DispatchAfterCurrentBusStamp::class);
$this->stack = $stack;
}

View File

@ -99,6 +99,53 @@ class DispatchAfterCurrentBusMiddlewareTest extends TestCase
$messageBus->dispatch($message);
}
public function testHandleDelayedEventFromQueue()
{
$message = new DummyMessage('Hello');
$event = new DummyEvent('Event on queue');
$middleware = new DispatchAfterCurrentBusMiddleware();
$commandHandlingMiddleware = $this->createMock(MiddlewareInterface::class);
$eventHandlingMiddleware = $this->createMock(MiddlewareInterface::class);
// This bus simulates the bus that are used when messages come back form the queue
$messageBusAfterQueue = new MessageBus([
// Create a new middleware
new DispatchAfterCurrentBusMiddleware(),
$eventHandlingMiddleware,
]);
$fakePutMessageOnQueue = $this->createMock(MiddlewareInterface::class);
$fakePutMessageOnQueue->expects($this->any())
->method('handle')
->with($this->callback(function ($envelope) use ($messageBusAfterQueue) {
// Fake putting the message on the queue
// Fake reading the queue
// Now, we add the message back to a new bus.
$messageBusAfterQueue->dispatch($envelope);
return true;
}))
->willReturnArgument(0);
$eventBus = new MessageBus([
$middleware,
$fakePutMessageOnQueue,
]);
$messageBus = new MessageBus([
$middleware,
new DispatchingMiddleware($eventBus, [
new Envelope($event, [new DispatchAfterCurrentBusStamp()]),
]),
$commandHandlingMiddleware,
]);
$this->expectHandledMessage($commandHandlingMiddleware, 0, $message);
$this->expectHandledMessage($eventHandlingMiddleware, 0, $event);
$messageBus->dispatch($message);
}
/**
* @param MiddlewareInterface|MockObject $handlingMiddleware
*/

View File

@ -34,9 +34,9 @@ class RetryIntegrationTest extends TestCase
$senderAndReceiver = new DummySenderAndReceiver();
$senderLocator = $this->createMock(ContainerInterface::class);
$senderLocator->method('has')->with('sender_alias')->willReturn(true);
$senderLocator->method('get')->with('sender_alias')->willReturn($senderAndReceiver);
$senderLocator = new SendersLocator([DummyMessage::class => ['sender_alias']], $senderLocator);
$senderLocator->method('has')->with('transportName')->willReturn(true);
$senderLocator->method('get')->with('transportName')->willReturn($senderAndReceiver);
$senderLocator = new SendersLocator([DummyMessage::class => ['transportName']], $senderLocator);
$handler = new DummyMessageHandlerFailingFirstTimes(0);
$throwingHandler = new DummyMessageHandlerFailingFirstTimes(1);
@ -52,7 +52,7 @@ class RetryIntegrationTest extends TestCase
$envelope = new Envelope(new DummyMessage('API'));
$bus->dispatch($envelope);
$worker = new Worker(['receiverName' => $senderAndReceiver], $bus, ['receiverName' => new MultiplierRetryStrategy()]);
$worker = new Worker(['transportName' => $senderAndReceiver], $bus, ['transportName' => new MultiplierRetryStrategy()]);
$worker->run([], function (?Envelope $envelope) use ($worker) {
if (null === $envelope) {
$worker->stop();

View File

@ -230,4 +230,90 @@ class ConnectionTest extends TestCase
{
Connection::buildConfiguration('doctrine://default?new_option=woops');
}
public function testFind()
{
$queryBuilder = $this->getQueryBuilderMock();
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
$id = 1;
$stmt = $this->getStatementMock([
'id' => $id,
'body' => '{"message":"Hi"}',
'headers' => json_encode(['type' => DummyMessage::class]),
]);
$driverConnection
->method('createQueryBuilder')
->willReturn($queryBuilder);
$queryBuilder
->method('where')
->willReturn($queryBuilder);
$queryBuilder
->method('getSQL')
->willReturn('');
$queryBuilder
->method('getParameters')
->willReturn([]);
$driverConnection
->method('prepare')
->willReturn($stmt);
$connection = new Connection([], $driverConnection, $schemaSynchronizer);
$doctrineEnvelope = $connection->find($id);
$this->assertEquals(1, $doctrineEnvelope['id']);
$this->assertEquals('{"message":"Hi"}', $doctrineEnvelope['body']);
$this->assertEquals(['type' => DummyMessage::class], $doctrineEnvelope['headers']);
}
public function testFindAll()
{
$queryBuilder = $this->getQueryBuilderMock();
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
$message1 = [
'id' => 1,
'body' => '{"message":"Hi"}',
'headers' => json_encode(['type' => DummyMessage::class]),
];
$message2 = [
'id' => 2,
'body' => '{"message":"Hi again"}',
'headers' => json_encode(['type' => DummyMessage::class]),
];
$stmt = $this->getMockBuilder(Statement::class)
->disableOriginalConstructor()
->getMock();
$stmt->expects($this->once())
->method('fetchAll')
->willReturn([$message1, $message2]);
$driverConnection
->method('createQueryBuilder')
->willReturn($queryBuilder);
$queryBuilder
->method('where')
->willReturn($queryBuilder);
$queryBuilder
->method('getSQL')
->willReturn('');
$queryBuilder
->method('getParameters')
->willReturn([]);
$driverConnection
->method('prepare')
->willReturn($stmt);
$connection = new Connection([], $driverConnection, $schemaSynchronizer);
$doctrineEnvelopes = $connection->findAll();
$this->assertEquals(1, $doctrineEnvelopes[0]['id']);
$this->assertEquals('{"message":"Hi"}', $doctrineEnvelopes[0]['body']);
$this->assertEquals(['type' => DummyMessage::class], $doctrineEnvelopes[0]['headers']);
$this->assertEquals(2, $doctrineEnvelopes[1]['id']);
$this->assertEquals('{"message":"Hi again"}', $doctrineEnvelopes[1]['body']);
$this->assertEquals(['type' => DummyMessage::class], $doctrineEnvelopes[1]['headers']);
}
}

View File

@ -84,7 +84,7 @@ class WorkerTest extends TestCase
public function testDispatchCausesRetry()
{
$receiver = new DummyReceiver([
[new Envelope(new DummyMessage('Hello'), [new SentStamp('Some\Sender', 'sender_alias')])],
[new Envelope(new DummyMessage('Hello'), [new SentStamp('Some\Sender', 'transport1')])],
]);
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
@ -97,7 +97,7 @@ class WorkerTest extends TestCase
$this->assertNotNull($redeliveryStamp);
// retry count now at 1
$this->assertSame(1, $redeliveryStamp->getRetryCount());
$this->assertSame('sender_alias', $redeliveryStamp->getSenderClassOrAlias());
$this->assertSame('transport1', $redeliveryStamp->getSenderClassOrAlias());
// received stamp is removed
$this->assertNull($envelope->last(ReceivedStamp::class));
@ -108,7 +108,7 @@ class WorkerTest extends TestCase
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
$retryStrategy->expects($this->once())->method('isRetryable')->willReturn(true);
$worker = new Worker(['receiver1' => $receiver], $bus, ['receiver1' => $retryStrategy]);
$worker = new Worker(['transport1' => $receiver], $bus, ['transport1' => $retryStrategy]);
$worker->run([], function (?Envelope $envelope) use ($worker) {
// stop after the messages finish
if (null === $envelope) {
@ -123,7 +123,7 @@ class WorkerTest extends TestCase
public function testDispatchCausesRejectWhenNoRetry()
{
$receiver = new DummyReceiver([
[new Envelope(new DummyMessage('Hello'), [new SentStamp('Some\Sender', 'sender_alias')])],
[new Envelope(new DummyMessage('Hello'), [new SentStamp('Some\Sender', 'transport1')])],
]);
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
@ -132,7 +132,7 @@ class WorkerTest extends TestCase
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
$retryStrategy->expects($this->once())->method('isRetryable')->willReturn(false);
$worker = new Worker(['receiver1' => $receiver], $bus, ['receiver1' => $retryStrategy]);
$worker = new Worker(['transport1' => $receiver], $bus, ['transport1' => $retryStrategy]);
$worker->run([], function (?Envelope $envelope) use ($worker) {
// stop after the messages finish
if (null === $envelope) {
@ -155,7 +155,7 @@ class WorkerTest extends TestCase
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
$retryStrategy->expects($this->never())->method('isRetryable');
$worker = new Worker(['receiver1' => $receiver], $bus, ['receiver1' => $retryStrategy]);
$worker = new Worker(['transport1' => $receiver], $bus, ['transport1' => $retryStrategy]);
$worker->run([], function (?Envelope $envelope) use ($worker) {
// stop after the messages finish
if (null === $envelope) {

View File

@ -155,7 +155,7 @@ class Connection
return null;
}
$doctrineEnvelope['headers'] = json_decode($doctrineEnvelope['headers'], true);
$doctrineEnvelope = $this->decodeEnvelopeHeaders($doctrineEnvelope);
$queryBuilder = $this->driverConnection->createQueryBuilder()
->update($this->configuration['table_name'])
@ -238,7 +238,11 @@ class Connection
$queryBuilder->setMaxResults($limit);
}
return $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters())->fetchAll();
$data = $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters())->fetchAll();
return array_map(function ($doctrineEnvelope) {
return $this->decodeEnvelopeHeaders($doctrineEnvelope);
}, $data);
}
public function find($id): ?array
@ -254,7 +258,7 @@ class Connection
'id' => $id,
])->fetch();
return false === $data ? null : $data;
return false === $data ? null : $this->decodeEnvelopeHeaders($data);
}
private function createAvailableMessagesQueryBuilder(): QueryBuilder
@ -332,4 +336,11 @@ class Connection
{
return $dateTime->format('Y-m-d\TH:i:s');
}
private function decodeEnvelopeHeaders(array $doctrineEnvelope): array
{
$doctrineEnvelope['headers'] = json_decode($doctrineEnvelope['headers'], true);
return $doctrineEnvelope;
}
}

View File

@ -17,13 +17,11 @@ use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent;
use Symfony\Component\Messenger\Event\WorkerStoppedEvent;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\Exception\LogicException;
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
use Symfony\Component\Messenger\Exception\UnrecoverableExceptionInterface;
use Symfony\Component\Messenger\Retry\RetryStrategyInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Messenger\Stamp\SentStamp;
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
@ -150,7 +148,7 @@ class Worker implements WorkerInterface
// add the delay and retry stamp info + remove ReceivedStamp
$retryEnvelope = $envelope->with(new DelayStamp($delay))
->with(new RedeliveryStamp($retryCount, $this->getSenderClassOrAlias($envelope)))
->with(new RedeliveryStamp($retryCount, $transportName))
->withoutAll(ReceivedStamp::class);
// re-send the message
@ -193,32 +191,10 @@ class Worker implements WorkerInterface
private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInterface $retryStrategy): bool
{
if ($e instanceof UnrecoverableMessageHandlingException) {
return false;
}
$sentStamp = $envelope->last(SentStamp::class);
if (null === $sentStamp) {
if (null !== $this->logger) {
$this->logger->warning('Message will not be retried because the SentStamp is missing and so the target sender cannot be determined.');
}
if ($e instanceof UnrecoverableExceptionInterface) {
return false;
}
return $retryStrategy->isRetryable($envelope);
}
private function getSenderClassOrAlias(Envelope $envelope): string
{
/** @var SentStamp|null $sentStamp */
$sentStamp = $envelope->last(SentStamp::class);
if (null === $sentStamp) {
// should not happen, because of the check in shouldRetry()
throw new LogicException('Could not find SentStamp.');
}
return $sentStamp->getSenderAlias() ?: $sentStamp->getSenderClass();
}
}

View File

@ -24,13 +24,6 @@ class PropertyInfoCacheExtractor implements PropertyInfoExtractorInterface, Prop
{
private $propertyInfoExtractor;
private $cacheItemPool;
/**
* A cache of property information, first keyed by the method called and
* then by the serialized method arguments.
*
* @var array
*/
private $arrayCache = [];
public function __construct(PropertyInfoExtractorInterface $propertyInfoExtractor, CacheItemPoolInterface $cacheItemPool)
@ -110,34 +103,22 @@ class PropertyInfoCacheExtractor implements PropertyInfoExtractorInterface, Prop
}
// Calling rawurlencode escapes special characters not allowed in PSR-6's keys
$encodedMethod = rawurlencode($method);
if (\array_key_exists($encodedMethod, $this->arrayCache) && \array_key_exists($serializedArguments, $this->arrayCache[$encodedMethod])) {
return $this->arrayCache[$encodedMethod][$serializedArguments];
$key = rawurlencode($method.'.'.$serializedArguments);
if (\array_key_exists($key, $this->arrayCache)) {
return $this->arrayCache[$key];
}
$item = $this->cacheItemPool->getItem($encodedMethod);
$item = $this->cacheItemPool->getItem($key);
$data = $item->get();
if ($item->isHit()) {
$this->arrayCache[$encodedMethod] = $data[$encodedMethod];
// Only match if the specific arguments have been cached.
if (\array_key_exists($serializedArguments, $data[$encodedMethod])) {
return $this->arrayCache[$encodedMethod][$serializedArguments];
}
}
// It's possible that the method has been called, but with different
// arguments, in which case $data will already be initialized.
if (!$data) {
$data = [];
return $this->arrayCache[$key] = $item->get();
}
$value = $this->propertyInfoExtractor->{$method}(...$arguments);
$data[$encodedMethod][$serializedArguments] = $value;
$this->arrayCache[$encodedMethod][$serializedArguments] = $value;
$item->set($data);
$item->set($value);
$this->cacheItemPool->save($item);
return $this->arrayCache[$encodedMethod][$serializedArguments];
return $this->arrayCache[$key] = $value;
}
}

View File

@ -51,10 +51,6 @@ class Argon2iPasswordEncoder extends BasePasswordEncoder implements SelfSaltingE
return true;
}
if (class_exists('ParagonIE_Sodium_Compat') && method_exists('ParagonIE_Sodium_Compat', 'crypto_pwhash_is_available')) {
return \ParagonIE_Sodium_Compat::crypto_pwhash_is_available();
}
return \function_exists('sodium_crypto_pwhash_str') || \extension_loaded('libsodium');
}

View File

@ -33,8 +33,8 @@ final class NativePasswordEncoder implements PasswordEncoderInterface, SelfSalti
$opsLimit = $opsLimit ?? max(6, \defined('SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE') ? \SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE : 6);
$memLimit = $memLimit ?? max(64 * 1024 * 1024, \defined('SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE : 64 * 1024 * 1024);
if (2 > $opsLimit) {
throw new \InvalidArgumentException('$opsLimit must be 2 or greater.');
if (3 > $opsLimit) {
throw new \InvalidArgumentException('$opsLimit must be 3 or greater.');
}
if (10 * 1024 > $memLimit) {

View File

@ -37,8 +37,8 @@ final class SodiumPasswordEncoder implements PasswordEncoderInterface, SelfSalti
$this->opsLimit = $opsLimit ?? max(6, \defined('SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE') ? \SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE : 6);
$this->memLimit = $memLimit ?? max(64 * 1024 * 1024, \defined('SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE : 64 * 1024 * 2014);
if (2 > $this->opsLimit) {
throw new \InvalidArgumentException('$opsLimit must be 2 or greater.');
if (3 > $this->opsLimit) {
throw new \InvalidArgumentException('$opsLimit must be 3 or greater.');
}
if (10 * 1024 > $this->memLimit) {
@ -48,15 +48,7 @@ final class SodiumPasswordEncoder implements PasswordEncoderInterface, SelfSalti
public static function isSupported(): bool
{
if (\extension_loaded('libsodium') || \function_exists('sodium_crypto_pwhash_str')) {
return true;
}
if (class_exists('ParagonIE_Sodium_Compat') && method_exists('ParagonIE_Sodium_Compat', 'crypto_pwhash_is_available')) {
return \ParagonIE_Sodium_Compat::crypto_pwhash_is_available();
}
return false;
return \function_exists('sodium_crypto_pwhash_str_needs_rehash') || \function_exists('Sodium\crypto_pwhash_str_needs_rehash');
}
/**

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\Validator\Constraints;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;
/**
* Used for the comparison of values.
@ -46,7 +47,7 @@ abstract class AbstractComparison extends Constraint
}
if (isset($options['propertyPath']) && !class_exists(PropertyAccess::class)) {
throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires the Symfony PropertyAccess component to use the "propertyPath" option.', \get_class($this)));
throw new LogicException(sprintf('The "%s" constraint requires the Symfony PropertyAccess component to use the "propertyPath" option.', \get_class($this)));
}
}

View File

@ -31,7 +31,7 @@ It also provides a few improvements over `var_export()`/`serialize()`:
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/var_exporter/introduction.html)
* [Documentation](https://symfony.com/doc/current/components/var_exporter.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)

View File

@ -41,6 +41,17 @@ switch ($vars['REQUEST_URI']) {
ob_start('ob_gzhandler');
break;
case '/103':
header('HTTP/1.1 103 Early Hints');
header('Link: </style.css>; rel=preload; as=style', false);
header('Link: </script.js>; rel=preload; as=script', false);
echo "HTTP/1.1 200 OK\r\n";
echo "Date: Fri, 26 May 2017 10:02:11 GMT\r\n";
echo "Content-Length: 13\r\n";
echo "\r\n";
echo 'Here the body';
exit;
case '/404':
header('Content-Type: application/json', true, 404);
break;

View File

@ -721,6 +721,15 @@ abstract class HttpClientTestCase extends TestCase
$this->assertSame('/?a=a&b=b', $body['REQUEST_URI']);
}
public function testInformationalResponse()
{
$client = $this->getHttpClient(__FUNCTION__);
$response = $client->request('GET', 'http://localhost:8057/103');
$this->assertSame('Here the body', $response->getContent());
$this->assertSame(200, $response->getStatusCode());
}
/**
* @requires extension zlib
*/