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:
commit
55c0b02587
@ -138,6 +138,9 @@ class DoctrineDataCollector extends DataCollector
|
|||||||
if (!\is_array($query['params'])) {
|
if (!\is_array($query['params'])) {
|
||||||
$query['params'] = [$query['params']];
|
$query['params'] = [$query['params']];
|
||||||
}
|
}
|
||||||
|
if (!\is_array($query['types'])) {
|
||||||
|
$query['types'] = [];
|
||||||
|
}
|
||||||
foreach ($query['params'] as $j => $param) {
|
foreach ($query['params'] as $j => $param) {
|
||||||
if (isset($query['types'][$j])) {
|
if (isset($query['types'][$j])) {
|
||||||
// Transform the param according to the type
|
// Transform the param according to the type
|
||||||
|
@ -13,7 +13,9 @@ namespace Symfony\Bridge\Doctrine\Messenger;
|
|||||||
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\Messenger\Envelope;
|
use Symfony\Component\Messenger\Envelope;
|
||||||
|
use Symfony\Component\Messenger\Exception\HandlerFailedException;
|
||||||
use Symfony\Component\Messenger\Middleware\StackInterface;
|
use Symfony\Component\Messenger\Middleware\StackInterface;
|
||||||
|
use Symfony\Component\Messenger\Stamp\HandledStamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps all handlers in a single doctrine transaction.
|
* Wraps all handlers in a single doctrine transaction.
|
||||||
@ -36,6 +38,12 @@ class DoctrineTransactionMiddleware extends AbstractDoctrineMiddleware
|
|||||||
} catch (\Throwable $exception) {
|
} catch (\Throwable $exception) {
|
||||||
$entityManager->getConnection()->rollBack();
|
$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;
|
throw $exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,18 @@ class DoctrineDataCollectorTest extends TestCase
|
|||||||
$this->assertTrue($collectedQueries['default'][1]['explainable']);
|
$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()
|
public function testReset()
|
||||||
{
|
{
|
||||||
$queries = [
|
$queries = [
|
||||||
|
@ -139,10 +139,13 @@ class DeprecationErrorHandler
|
|||||||
$group = 'unsilenced';
|
$group = 'unsilenced';
|
||||||
} elseif ($deprecation->isLegacy(self::$utilPrefix)) {
|
} elseif ($deprecation->isLegacy(self::$utilPrefix)) {
|
||||||
$group = 'legacy';
|
$group = 'legacy';
|
||||||
} elseif (!$deprecation->isSelf()) {
|
|
||||||
$group = $deprecation->isIndirect() ? 'remaining indirect' : 'remaining direct';
|
|
||||||
} else {
|
} 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)) {
|
if ($this->getConfiguration()->shouldDisplayStackTrace($msg)) {
|
||||||
@ -216,7 +219,13 @@ class DeprecationErrorHandler
|
|||||||
return $this->configuration;
|
return $this->configuration;
|
||||||
}
|
}
|
||||||
if (false === $mode = $this->mode) {
|
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) {
|
if ('strict' === $mode) {
|
||||||
return $this->configuration = Configuration::inStrictMode();
|
return $this->configuration = Configuration::inStrictMode();
|
||||||
|
@ -18,6 +18,15 @@ use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor;
|
|||||||
*/
|
*/
|
||||||
class Deprecation
|
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
|
* @var array
|
||||||
*/
|
*/
|
||||||
@ -39,13 +48,21 @@ class Deprecation
|
|||||||
private $originMethod;
|
private $originMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var string one of the PATH_TYPE_* constants
|
||||||
*/
|
*/
|
||||||
private $self;
|
private $triggeringFilePathType;
|
||||||
|
|
||||||
/** @var string[] absolute paths to vendor directories */
|
/** @var string[] absolute paths to vendor directories */
|
||||||
private static $vendors;
|
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 $message
|
||||||
* @param string $file
|
* @param string $file
|
||||||
@ -59,7 +76,7 @@ class Deprecation
|
|||||||
// No-op
|
// No-op
|
||||||
}
|
}
|
||||||
$line = $trace[$i];
|
$line = $trace[$i];
|
||||||
$this->self = !$this->pathOriginatesFromVendor($file);
|
$this->trigerringFilePathType = $this->getPathType($file);
|
||||||
if (isset($line['object']) || isset($line['class'])) {
|
if (isset($line['object']) || isset($line['class'])) {
|
||||||
if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) {
|
if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) {
|
||||||
$parsedMsg = unserialize($this->message);
|
$parsedMsg = unserialize($this->message);
|
||||||
@ -70,8 +87,9 @@ class Deprecation
|
|||||||
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
|
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
|
||||||
// then we need to use the serialized information to determine
|
// then we need to use the serialized information to determine
|
||||||
// if the error has been triggered from vendor code.
|
// if the error has been triggered from vendor code.
|
||||||
$this->self = isset($parsedMsg['triggering_file'])
|
if (isset($parsedMsg['triggering_file'])) {
|
||||||
&& $this->pathOriginatesFromVendor($parsedMsg['triggering_file']);
|
$this->trigerringFilePathType = $this->getPathType($parsedMsg['triggering_file']);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -101,14 +119,6 @@ class Deprecation
|
|||||||
return isset($this->originClass);
|
return isset($this->originClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isSelf()
|
|
||||||
{
|
|
||||||
return $this->self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@ -163,10 +173,16 @@ class Deprecation
|
|||||||
* Tells whether both the calling package and the called package are vendor
|
* Tells whether both the calling package and the called package are vendor
|
||||||
* packages.
|
* 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;
|
$erroringFile = $erroringPackage = null;
|
||||||
foreach ($this->trace as $line) {
|
foreach ($this->trace as $line) {
|
||||||
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
|
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)) {
|
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!$this->pathOriginatesFromVendor($file)) {
|
if (self::PATH_TYPE_SELF === $this->getPathType($file)) {
|
||||||
return false;
|
return self::TYPE_DIRECT;
|
||||||
|
}
|
||||||
|
if (self::PATH_TYPE_UNDETERMINED === $this->getPathType($file)) {
|
||||||
|
return self::TYPE_UNDETERMINED;
|
||||||
}
|
}
|
||||||
if (null !== $erroringFile && null !== $erroringPackage) {
|
if (null !== $erroringFile && null !== $erroringPackage) {
|
||||||
$package = $this->getPackage($file);
|
$package = $this->getPackage($file);
|
||||||
if ('composer' !== $package && $package !== $erroringPackage) {
|
if ('composer' !== $package && $package !== $erroringPackage) {
|
||||||
return true;
|
return self::TYPE_INDIRECT;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -193,11 +212,11 @@ class Deprecation
|
|||||||
$erroringPackage = $this->getPackage($file);
|
$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
|
* @param string $path
|
||||||
*
|
*
|
||||||
@ -237,6 +256,15 @@ class Deprecation
|
|||||||
$v = \dirname(\dirname($r->getFileName()));
|
$v = \dirname(\dirname($r->getFileName()));
|
||||||
if (file_exists($v.'/composer/installed.json')) {
|
if (file_exists($v.'/composer/installed.json')) {
|
||||||
self::$vendors[] = $v;
|
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;
|
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
|
* @param string $path
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function pathOriginatesFromVendor($path)
|
private function getPathType($path)
|
||||||
{
|
{
|
||||||
$realPath = realpath($path);
|
$realPath = realpath($path);
|
||||||
if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) {
|
if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) {
|
||||||
return true;
|
return self::PATH_TYPE_UNDETERMINED;
|
||||||
}
|
}
|
||||||
foreach (self::getVendors() as $vendor) {
|
foreach (self::getVendors() as $vendor) {
|
||||||
if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
|
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".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $exception->getTraceAsString()).
|
||||||
"\n";
|
"\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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ class DeprecationTest extends TestCase
|
|||||||
public function testItCanTellWhetherItIsInternal()
|
public function testItCanTellWhetherItIsInternal()
|
||||||
{
|
{
|
||||||
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||||
$this->assertTrue($deprecation->isSelf());
|
$this->assertSame(Deprecation::TYPE_SELF, $deprecation->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLegacyTestMethodIsDetectedAsSuch()
|
public function testLegacyTestMethodIsDetectedAsSuch()
|
||||||
@ -46,7 +46,7 @@ class DeprecationTest extends TestCase
|
|||||||
public function testItRulesOutFilesOutsideVendorsAsIndirect()
|
public function testItRulesOutFilesOutsideVendorsAsIndirect()
|
||||||
{
|
{
|
||||||
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||||
$this->assertFalse($deprecation->isIndirect());
|
$this->assertNotSame(Deprecation::TYPE_INDIRECT, $deprecation->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,15 +73,13 @@ Unsilenced deprecation notices (3)
|
|||||||
1x: unsilenced bar deprecation
|
1x: unsilenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
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: silenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
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.
|
I get precedence over any exit statements inside the deprecation error handler.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
--TEST--
|
--TEST--
|
||||||
Test DeprecationErrorHandler in weak mode
|
Test DeprecationErrorHandler in disabled mode
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
putenv('SYMFONY_DEPRECATIONS_HELPER=disabled');
|
$_SERVER['SYMFONY_DEPRECATIONS_HELPER'] = 'disabled';
|
||||||
putenv('ANSICON');
|
putenv('ANSICON');
|
||||||
putenv('ConEmuANSI');
|
putenv('ConEmuANSI');
|
||||||
putenv('TERM');
|
putenv('TERM');
|
||||||
|
@ -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
|
@ -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
|
@ -1,3 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once __DIR__.'/composer/autoload_real.php';
|
require_once __DIR__.'/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInitFake::getLoader();
|
||||||
|
@ -1,5 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
class ComposerLoaderFake
|
||||||
|
{
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ComposerAutoloaderInitFake
|
class ComposerAutoloaderInitFake
|
||||||
{
|
{
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
return new ComposerLoaderFake();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,14 +61,12 @@ Unsilenced deprecation notices (3)
|
|||||||
1x: unsilenced bar deprecation
|
1x: unsilenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
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: silenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
Legacy deprecation notices (1)
|
|
||||||
|
|
||||||
Other deprecation notices (1)
|
|
||||||
|
|
||||||
1x: root deprecation
|
|
||||||
|
|
||||||
|
@ -73,17 +73,15 @@ Unsilenced deprecation notices (3)
|
|||||||
1x: unsilenced bar deprecation
|
1x: unsilenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
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: silenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
Legacy deprecation notices (1)
|
|
||||||
|
|
||||||
Other deprecation notices (1)
|
|
||||||
|
|
||||||
1x: root deprecation
|
|
||||||
|
|
||||||
Shutdown-time deprecations:
|
Shutdown-time deprecations:
|
||||||
|
|
||||||
Other deprecation notices (1)
|
Other deprecation notices (1)
|
||||||
|
@ -61,14 +61,13 @@ Unsilenced deprecation notices (3)
|
|||||||
1x: unsilenced bar deprecation
|
1x: unsilenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
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: silenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
Legacy deprecation notices (1)
|
|
||||||
|
|
||||||
Other deprecation notices (1)
|
|
||||||
|
|
||||||
1x: root deprecation
|
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@
|
|||||||
<div class="form-group{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
|
<div class="form-group{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
|
||||||
{{- form_label(form) }} {# -#}
|
{{- form_label(form) }} {# -#}
|
||||||
{{ form_widget(form, widget_attr) }} {# -#}
|
{{ form_widget(form, widget_attr) }} {# -#}
|
||||||
|
{{- form_help(form) -}}
|
||||||
{{ form_errors(form) }} {# -#}
|
{{ form_errors(form) }} {# -#}
|
||||||
</div> {# -#}
|
</div> {# -#}
|
||||||
{%- endblock form_row %}
|
{%- endblock form_row %}
|
||||||
|
@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|||||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
|
||||||
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
||||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||||
|
|
||||||
@ -43,9 +44,9 @@ class DebugExtension extends Extension
|
|||||||
->addMethodCall('setMinDepth', [$config['min_depth']])
|
->addMethodCall('setMinDepth', [$config['min_depth']])
|
||||||
->addMethodCall('setMaxString', [$config['max_string_length']]);
|
->addMethodCall('setMaxString', [$config['max_string_length']]);
|
||||||
|
|
||||||
if (method_exists(ReflectionClass::class, 'unsetClosureFileInfo')) {
|
if (method_exists(ReflectionCaster::class, 'unsetClosureFileInfo')) {
|
||||||
$container->getDefinition('var_dumper.cloner')
|
$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']) {
|
if (method_exists(HtmlDumper::class, 'setTheme') && 'dark' !== $config['theme']) {
|
||||||
|
@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use Symfony\Bundle\DebugBundle\DependencyInjection\DebugExtension;
|
use Symfony\Bundle\DebugBundle\DependencyInjection\DebugExtension;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||||
|
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
|
||||||
|
|
||||||
class DebugExtensionTest extends TestCase
|
class DebugExtensionTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -36,6 +37,39 @@ class DebugExtensionTest extends TestCase
|
|||||||
$this->assertSame($expectedTags, $container->getDefinition('data_collector.dump')->getTag('data_collector'));
|
$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()
|
private function createContainer()
|
||||||
{
|
{
|
||||||
$container = new ContainerBuilder(new ParameterBag([
|
$container = new ContainerBuilder(new ParameterBag([
|
||||||
|
@ -23,7 +23,7 @@ CHANGELOG
|
|||||||
* [BC Break] When using Messenger, the default transport changed from
|
* [BC Break] When using Messenger, the default transport changed from
|
||||||
using Symfony's serializer service to use `PhpSerializer`, which uses
|
using Symfony's serializer service to use `PhpSerializer`, which uses
|
||||||
PHP's native `serialize()` and `unserialize()` functions. To use the
|
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
|
config option to `messenger.transport.symfony_serializer`. Or set the
|
||||||
`serializer` option under one specific `transport`.
|
`serializer` option under one specific `transport`.
|
||||||
* [BC Break] The `framework.messenger.serializer` config key changed to
|
* [BC Break] The `framework.messenger.serializer` config key changed to
|
||||||
|
@ -66,7 +66,7 @@ class Client extends HttpKernelBrowser
|
|||||||
*/
|
*/
|
||||||
public function getProfile()
|
public function getProfile()
|
||||||
{
|
{
|
||||||
if (!$this->kernel->getContainer()->has('profiler')) {
|
if (null === $this->response || !$this->kernel->getContainer()->has('profiler')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,6 +652,10 @@ class FrameworkExtension extends Extension
|
|||||||
$definitionDefinition->addArgument($transitions);
|
$definitionDefinition->addArgument($transitions);
|
||||||
$definitionDefinition->addArgument($initialMarking);
|
$definitionDefinition->addArgument($initialMarking);
|
||||||
$definitionDefinition->addArgument($metadataStoreDefinition);
|
$definitionDefinition->addArgument($metadataStoreDefinition);
|
||||||
|
$definitionDefinition->addTag('workflow.definition', [
|
||||||
|
'name' => $name,
|
||||||
|
'type' => $type,
|
||||||
|
]);
|
||||||
|
|
||||||
// Create MarkingStore
|
// Create MarkingStore
|
||||||
if (isset($workflow['marking_store']['type'])) {
|
if (isset($workflow['marking_store']['type'])) {
|
||||||
|
@ -311,6 +311,8 @@ abstract class FrameworkExtensionTest extends TestCase
|
|||||||
$workflowDefinition->getArgument(0),
|
$workflowDefinition->getArgument(0),
|
||||||
'Places are passed to the workflow definition'
|
'Places are passed to the workflow definition'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->assertSame(['workflow.definition' => [['name' => 'legacy', 'type' => 'state_machine']]], $workflowDefinition->getTags());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,9 +28,9 @@ class ProfilerTest extends WebTestCase
|
|||||||
|
|
||||||
// enable the profiler for the next request
|
// enable the profiler for the next request
|
||||||
$client->enableProfiler();
|
$client->enableProfiler();
|
||||||
$crawler = $client->request('GET', '/profiler');
|
$this->assertFalse($client->getProfile());
|
||||||
$profile = $client->getProfile();
|
$client->request('GET', '/profiler');
|
||||||
$this->assertInternalType('object', $profile);
|
$this->assertInternalType('object', $client->getProfile());
|
||||||
|
|
||||||
$client->request('GET', '/profiler');
|
$client->request('GET', '/profiler');
|
||||||
$this->assertFalse($client->getProfile());
|
$this->assertFalse($client->getProfile());
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
"symfony/security-http": "^3.4|^4.0|^5.0",
|
"symfony/security-http": "^3.4|^4.0|^5.0",
|
||||||
"symfony/serializer": "^4.3|^5.0",
|
"symfony/serializer": "^4.3|^5.0",
|
||||||
"symfony/stopwatch": "^3.4|^4.0|^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/templating": "^3.4|^4.0|^5.0",
|
||||||
"symfony/twig-bundle": "^3.4|^4.0|^5.0",
|
"symfony/twig-bundle": "^3.4|^4.0|^5.0",
|
||||||
"symfony/validator": "^4.1|^5.0",
|
"symfony/validator": "^4.1|^5.0",
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||||
|
|
||||||
use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
|
use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
|
||||||
use Symfony\Component\Cache\Simple\FilesystemCache;
|
|
||||||
use Symfony\Component\Cache\Simple\ArrayCache;
|
use Symfony\Component\Cache\Simple\ArrayCache;
|
||||||
|
use Symfony\Component\Cache\Simple\FilesystemCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group time-sensitive
|
* @group time-sensitive
|
||||||
|
@ -314,7 +314,7 @@ class DotenvTest extends TestCase
|
|||||||
$dotenv->load(__DIR__);
|
$dotenv->load(__DIR__);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testServerSuperglobalIsNotOverriden()
|
public function testServerSuperglobalIsNotOverridden()
|
||||||
{
|
{
|
||||||
$originalValue = $_SERVER['argc'];
|
$originalValue = $_SERVER['argc'];
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ class DotenvTest extends TestCase
|
|||||||
$this->assertSame($originalValue, $_SERVER['argc']);
|
$this->assertSame($originalValue, $_SERVER['argc']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEnvVarIsNotOverriden()
|
public function testEnvVarIsNotOverridden()
|
||||||
{
|
{
|
||||||
putenv('TEST_ENV_VAR=original_value');
|
putenv('TEST_ENV_VAR=original_value');
|
||||||
$_SERVER['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'));
|
$this->assertSame('original_value', getenv('TEST_ENV_VAR'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHttpVarIsPartiallyOverriden()
|
public function testHttpVarIsPartiallyOverridden()
|
||||||
{
|
{
|
||||||
$_SERVER['HTTP_TEST_ENV_VAR'] = 'http_value';
|
$_SERVER['HTTP_TEST_ENV_VAR'] = 'http_value';
|
||||||
|
|
||||||
|
@ -569,14 +569,15 @@ class Filesystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->mkdir($targetDir);
|
$this->mkdir($targetDir);
|
||||||
$targetDirInfo = new \SplFileInfo($targetDir);
|
$filesCreatedWhileMirroring = [];
|
||||||
|
|
||||||
foreach ($iterator as $file) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$target = $targetDir.substr($file->getPathname(), $originDirLen);
|
$target = $targetDir.substr($file->getPathname(), $originDirLen);
|
||||||
|
$filesCreatedWhileMirroring[$target] = true;
|
||||||
|
|
||||||
if (!$copyOnWindows && is_link($file)) {
|
if (!$copyOnWindows && is_link($file)) {
|
||||||
$this->symlink($file->getLinkTarget(), $target);
|
$this->symlink($file->getLinkTarget(), $target);
|
||||||
|
@ -1362,6 +1362,22 @@ class FilesystemTest extends FilesystemTestCase
|
|||||||
$this->assertFalse($this->filesystem->exists($targetPath.'target'));
|
$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
|
* @dataProvider providePathsForIsAbsolutePath
|
||||||
*/
|
*/
|
||||||
|
@ -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>
|
@ -98,9 +98,9 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
|
|||||||
'http_code' => 0,
|
'http_code' => 0,
|
||||||
'redirect_count' => 0,
|
'redirect_count' => 0,
|
||||||
'start_time' => 0.0,
|
'start_time' => 0.0,
|
||||||
'fopen_time' => 0.0,
|
|
||||||
'connect_time' => 0.0,
|
'connect_time' => 0.0,
|
||||||
'redirect_time' => 0.0,
|
'redirect_time' => 0.0,
|
||||||
|
'pretransfer_time' => 0.0,
|
||||||
'starttransfer_time' => 0.0,
|
'starttransfer_time' => 0.0,
|
||||||
'total_time' => 0.0,
|
'total_time' => 0.0,
|
||||||
'namelookup_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) {
|
$onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info) {
|
||||||
$progressInfo = $info;
|
$progressInfo = $info;
|
||||||
$progressInfo['url'] = implode('', $info['url']);
|
$progressInfo['url'] = implode('', $info['url']);
|
||||||
unset($progressInfo['fopen_time'], $progressInfo['size_body']);
|
unset($progressInfo['size_body']);
|
||||||
|
|
||||||
if ($progress && -1 === $progress[0]) {
|
if ($progress && -1 === $progress[0]) {
|
||||||
// Response completed
|
// Response completed
|
||||||
@ -133,14 +133,14 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
|
|||||||
|
|
||||||
// Always register a notification callback to compute live stats about the response
|
// 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) {
|
$notification = static function (int $code, int $severity, ?string $msg, int $msgCode, int $dlNow, int $dlSize) use ($onProgress, &$info) {
|
||||||
$now = microtime(true);
|
$info['total_time'] = microtime(true) - $info['start_time'];
|
||||||
$info['total_time'] = $now - $info['start_time'];
|
|
||||||
|
|
||||||
if (STREAM_NOTIFY_PROGRESS === $code) {
|
if (STREAM_NOTIFY_PROGRESS === $code) {
|
||||||
|
$info['starttransfer_time'] = $info['starttransfer_time'] ?: $info['total_time'];
|
||||||
$info['size_upload'] += $dlNow ? 0 : $info['size_body'];
|
$info['size_upload'] += $dlNow ? 0 : $info['size_body'];
|
||||||
$info['size_download'] = $dlNow;
|
$info['size_download'] = $dlNow;
|
||||||
} elseif (STREAM_NOTIFY_CONNECT === $code) {
|
} elseif (STREAM_NOTIFY_CONNECT === $code) {
|
||||||
$info['connect_time'] += $now - $info['fopen_time'];
|
$info['connect_time'] = $info['total_time'];
|
||||||
$info['debug'] .= $info['request_header'];
|
$info['debug'] .= $info['request_header'];
|
||||||
unset($info['request_header']);
|
unset($info['request_header']);
|
||||||
} else {
|
} else {
|
||||||
@ -310,7 +310,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
|
|||||||
throw new TransportException(sprintf('Could not resolve host "%s".', $host));
|
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];
|
$multi->dnsCache[$host] = $ip = $ip[0];
|
||||||
$info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
|
$info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
|
||||||
} else {
|
} else {
|
||||||
@ -368,10 +368,9 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$now = microtime(true);
|
|
||||||
$info['url'] = $url;
|
$info['url'] = $url;
|
||||||
++$info['redirect_count'];
|
++$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
|
// Do like curl and browsers: turn POST to GET on 301, 302 and 303
|
||||||
if (\in_array($info['http_code'], [301, 302, 303], true)) {
|
if (\in_array($info['http_code'], [301, 302, 303], true)) {
|
||||||
|
@ -55,6 +55,7 @@ final class CurlResponse implements ResponseInterface
|
|||||||
$this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
|
$this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
|
||||||
$info = &$this->info;
|
$info = &$this->info;
|
||||||
$headers = &$this->headers;
|
$headers = &$this->headers;
|
||||||
|
$debugBuffer = $this->debugBuffer;
|
||||||
|
|
||||||
if (!$info['response_headers']) {
|
if (!$info['response_headers']) {
|
||||||
// Used to keep track of what we're waiting for
|
// Used to keep track of what we're waiting for
|
||||||
@ -88,9 +89,11 @@ final class CurlResponse implements ResponseInterface
|
|||||||
if ($onProgress = $options['on_progress']) {
|
if ($onProgress = $options['on_progress']) {
|
||||||
$url = isset($info['url']) ? ['url' => $info['url']] : [];
|
$url = isset($info['url']) ? ['url' => $info['url']] : [];
|
||||||
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
|
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 {
|
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) {
|
} catch (\Throwable $e) {
|
||||||
$multi->handlesActivity[(int) $ch][] = null;
|
$multi->handlesActivity[(int) $ch][] = null;
|
||||||
$multi->handlesActivity[(int) $ch][] = $e;
|
$multi->handlesActivity[(int) $ch][] = $e;
|
||||||
@ -148,12 +151,6 @@ final class CurlResponse implements ResponseInterface
|
|||||||
if (!$info = $this->finalInfo) {
|
if (!$info = $this->finalInfo) {
|
||||||
self::perform($this->multi);
|
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 = array_merge($this->info, curl_getinfo($this->handle));
|
||||||
$info['url'] = $this->info['url'] ?? $info['url'];
|
$info['url'] = $this->info['url'] ?? $info['url'];
|
||||||
$info['redirect_url'] = $this->info['redirect_url'] ?? null;
|
$info['redirect_url'] = $this->info['redirect_url'] ?? null;
|
||||||
@ -164,9 +161,10 @@ final class CurlResponse implements ResponseInterface
|
|||||||
$info['starttransfer_time'] = 0.0;
|
$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)) {
|
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);
|
curl_setopt($this->handle, CURLOPT_VERBOSE, false);
|
||||||
rewind($this->debugBuffer);
|
rewind($this->debugBuffer);
|
||||||
ftruncate($this->debugBuffer, 0);
|
ftruncate($this->debugBuffer, 0);
|
||||||
@ -289,7 +287,19 @@ final class CurlResponse implements ResponseInterface
|
|||||||
// Regular header line: add it to the list
|
// Regular header line: add it to the list
|
||||||
self::addResponseHeaders([substr($data, 0, -2)], $info, $headers);
|
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']) {
|
if (curl_getinfo($ch, CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) {
|
||||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
||||||
} elseif (303 === $info['http_code'] || ('POST' === $info['http_method'] && \in_array($info['http_code'], [301, 302], true))) {
|
} 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);
|
return \strlen($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// End of headers: handle redirects and add to the activity list
|
// 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;
|
$info['redirect_url'] = null;
|
||||||
|
|
||||||
if (300 <= $statusCode && $statusCode < 400 && null !== $location) {
|
if (300 <= $statusCode && $statusCode < 400 && null !== $location) {
|
||||||
@ -336,10 +345,6 @@ final class CurlResponse implements ResponseInterface
|
|||||||
return 0;
|
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');
|
curl_setopt($ch, CURLOPT_PRIVATE, 'content');
|
||||||
} elseif (null !== $info['redirect_url'] && $logger) {
|
} elseif (null !== $info['redirect_url'] && $logger) {
|
||||||
$logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));
|
$logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));
|
||||||
|
@ -78,18 +78,12 @@ final class NativeResponse implements ResponseInterface
|
|||||||
if (!$info = $this->finalInfo) {
|
if (!$info = $this->finalInfo) {
|
||||||
self::perform($this->multi);
|
self::perform($this->multi);
|
||||||
|
|
||||||
if ('debug' === $type) {
|
|
||||||
return $this->info['debug'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$info = $this->info;
|
$info = $this->info;
|
||||||
$info['url'] = implode('', $info['url']);
|
$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) {
|
if (null === $this->buffer) {
|
||||||
$this->finalInfo = $info;
|
$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";
|
$this->info['request_header'] .= implode("\r\n", $context['http']['header'])."\r\n\r\n";
|
||||||
|
|
||||||
// Send request and follow redirects when needed
|
// Send request and follow redirects when needed
|
||||||
$this->info['fopen_time'] = microtime(true);
|
|
||||||
$this->handle = $h = fopen($url, 'r', false, $this->context);
|
$this->handle = $h = fopen($url, 'r', false, $this->context);
|
||||||
self::addResponseHeaders($http_response_header, $this->info, $this->headers, $this->info['debug']);
|
self::addResponseHeaders($http_response_header, $this->info, $this->headers, $this->info['debug']);
|
||||||
$url = ($this->resolveRedirect)($this->multi, $this->headers['location'][0] ?? null, $this->context);
|
$url = ($this->resolveRedirect)($this->multi, $this->headers['location'][0] ?? null, $this->context);
|
||||||
@ -152,7 +145,7 @@ final class NativeResponse implements ResponseInterface
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
} finally {
|
} 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();
|
restore_error_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,6 +266,7 @@ final class NativeResponse implements ResponseInterface
|
|||||||
if (null !== $e || !$remaining || feof($h)) {
|
if (null !== $e || !$remaining || feof($h)) {
|
||||||
// Stream completed
|
// Stream completed
|
||||||
$info['total_time'] = microtime(true) - $info['start_time'];
|
$info['total_time'] = microtime(true) - $info['start_time'];
|
||||||
|
$info['starttransfer_time'] = $info['starttransfer_time'] ?: $info['total_time'];
|
||||||
|
|
||||||
if ($onProgress) {
|
if ($onProgress) {
|
||||||
try {
|
try {
|
||||||
|
@ -253,7 +253,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
|||||||
*/
|
*/
|
||||||
public function getBag($name)
|
public function getBag($name)
|
||||||
{
|
{
|
||||||
return $this->storage->getBag($name)->getBag();
|
$bag = $this->storage->getBag($name);
|
||||||
|
|
||||||
|
return method_exists($bag, 'getBag') ? $bag->getBag() : $bag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\SessionBagProxy;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -260,4 +261,28 @@ class SessionTest extends TestCase
|
|||||||
$flash->get('hello');
|
$flash->get('hello');
|
||||||
$this->assertTrue($this->session->isEmpty());
|
$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'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,9 @@ class RedisStore implements StoreInterface
|
|||||||
$this->checkNotExpired($key);
|
$this->checkNotExpired($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function waitAndSave(Key $key)
|
public function waitAndSave(Key $key)
|
||||||
{
|
{
|
||||||
throw new InvalidArgumentException(sprintf('The store "%s" does not supports blocking locks.', \get_class($this)));
|
throw new InvalidArgumentException(sprintf('The store "%s" does not supports blocking locks.', \get_class($this)));
|
||||||
|
@ -68,7 +68,7 @@ class MailgunTransport extends AbstractApiTransport
|
|||||||
{
|
{
|
||||||
$headers = $email->getHeaders();
|
$headers = $email->getHeaders();
|
||||||
$html = $email->getHtmlBody();
|
$html = $email->getHtmlBody();
|
||||||
if (null !== $html) {
|
if (null !== $html && \is_resource($html)) {
|
||||||
if (stream_get_meta_data($html)['seekable'] ?? false) {
|
if (stream_get_meta_data($html)['seekable'] ?? false) {
|
||||||
rewind($html);
|
rewind($html);
|
||||||
}
|
}
|
||||||
|
@ -71,11 +71,18 @@ class TransportTest extends TestCase
|
|||||||
Transport::fromDsn('some://');
|
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()
|
public function testFromInvalidDsnNoHost()
|
||||||
{
|
{
|
||||||
$this->expectException(InvalidArgumentException::class);
|
$this->expectException(InvalidArgumentException::class);
|
||||||
$this->expectExceptionMessage('The "?!" mailer DSN must contain a mailer name.');
|
$this->expectExceptionMessage('The "file:///some/path" mailer DSN must contain a mailer name.');
|
||||||
Transport::fromDsn('?!');
|
Transport::fromDsn('file:///some/path');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFromInvalidTransportName()
|
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 = Transport::fromDsn('api://'.urlencode('u$er').':'.urlencode('pa$s').'@mailgun?region=us', $dispatcher, $client, $logger);
|
||||||
$transport->send($message);
|
$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);
|
$this->expectException(LogicException::class);
|
||||||
Transport::fromDsn('foo://mailgun');
|
Transport::fromDsn('foo://mailgun');
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,10 @@ class Transport
|
|||||||
throw new InvalidArgumentException(sprintf('The "%s" mailer DSN is invalid.', $dsn));
|
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'])) {
|
if (!isset($parsedDsn['host'])) {
|
||||||
throw new InvalidArgumentException(sprintf('The "%s" mailer DSN must contain a mailer name.', $dsn));
|
throw new InvalidArgumentException(sprintf('The "%s" mailer DSN must contain a mailer name.', $dsn));
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ namespace Symfony\Component\Messenger\Exception;
|
|||||||
*
|
*
|
||||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
*/
|
*/
|
||||||
class DelayedMessageHandlingException extends \RuntimeException implements ExceptionInterface
|
class DelayedMessageHandlingException extends RuntimeException
|
||||||
{
|
{
|
||||||
private $exceptions;
|
private $exceptions;
|
||||||
|
|
||||||
|
@ -16,6 +16,6 @@ namespace Symfony\Component\Messenger\Exception;
|
|||||||
*
|
*
|
||||||
* @experimental in 4.3
|
* @experimental in 4.3
|
||||||
*/
|
*/
|
||||||
class MessageDecodingFailedException extends \InvalidArgumentException implements ExceptionInterface
|
class MessageDecodingFailedException extends InvalidArgumentException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,6 @@ namespace Symfony\Component\Messenger\Exception;
|
|||||||
*
|
*
|
||||||
* @experimental in 4.3
|
* @experimental in 4.3
|
||||||
*/
|
*/
|
||||||
class NoHandlerForMessageException extends \LogicException implements ExceptionInterface
|
class NoHandlerForMessageException extends LogicException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,6 @@ namespace Symfony\Component\Messenger\Exception;
|
|||||||
*
|
*
|
||||||
* @experimental in 4.3
|
* @experimental in 4.3
|
||||||
*/
|
*/
|
||||||
class UnknownSenderException extends \InvalidArgumentException implements ExceptionInterface
|
class UnknownSenderException extends InvalidArgumentException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
{
|
||||||
|
}
|
@ -12,15 +12,12 @@
|
|||||||
namespace Symfony\Component\Messenger\Exception;
|
namespace Symfony\Component\Messenger\Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown while handling a message to indicate that handling will continue to fail.
|
* A concrete implementation of UnrecoverableExceptionInterface that can be used directly.
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* @author Frederic Bouchery <frederic@bouchery.fr>
|
* @author Frederic Bouchery <frederic@bouchery.fr>
|
||||||
*
|
*
|
||||||
* @experimental in 4.3
|
* @experimental in 4.3
|
||||||
*/
|
*/
|
||||||
class UnrecoverableMessageHandlingException extends RuntimeException
|
class UnrecoverableMessageHandlingException extends RuntimeException implements UnrecoverableExceptionInterface
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ use Symfony\Component\Validator\ConstraintViolationListInterface;
|
|||||||
*
|
*
|
||||||
* @experimental in 4.3
|
* @experimental in 4.3
|
||||||
*/
|
*/
|
||||||
class ValidationFailedException extends \RuntimeException implements ExceptionInterface
|
class ValidationFailedException extends RuntimeException
|
||||||
{
|
{
|
||||||
private $violations;
|
private $violations;
|
||||||
private $violatingMessage;
|
private $violatingMessage;
|
||||||
|
@ -112,7 +112,7 @@ final class QueuedEnvelope
|
|||||||
|
|
||||||
public function __construct(Envelope $envelope, StackInterface $stack)
|
public function __construct(Envelope $envelope, StackInterface $stack)
|
||||||
{
|
{
|
||||||
$this->envelope = $envelope;
|
$this->envelope = $envelope->withoutAll(DispatchAfterCurrentBusStamp::class);
|
||||||
$this->stack = $stack;
|
$this->stack = $stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +99,53 @@ class DispatchAfterCurrentBusMiddlewareTest extends TestCase
|
|||||||
$messageBus->dispatch($message);
|
$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
|
* @param MiddlewareInterface|MockObject $handlingMiddleware
|
||||||
*/
|
*/
|
||||||
|
@ -34,9 +34,9 @@ class RetryIntegrationTest extends TestCase
|
|||||||
$senderAndReceiver = new DummySenderAndReceiver();
|
$senderAndReceiver = new DummySenderAndReceiver();
|
||||||
|
|
||||||
$senderLocator = $this->createMock(ContainerInterface::class);
|
$senderLocator = $this->createMock(ContainerInterface::class);
|
||||||
$senderLocator->method('has')->with('sender_alias')->willReturn(true);
|
$senderLocator->method('has')->with('transportName')->willReturn(true);
|
||||||
$senderLocator->method('get')->with('sender_alias')->willReturn($senderAndReceiver);
|
$senderLocator->method('get')->with('transportName')->willReturn($senderAndReceiver);
|
||||||
$senderLocator = new SendersLocator([DummyMessage::class => ['sender_alias']], $senderLocator);
|
$senderLocator = new SendersLocator([DummyMessage::class => ['transportName']], $senderLocator);
|
||||||
|
|
||||||
$handler = new DummyMessageHandlerFailingFirstTimes(0);
|
$handler = new DummyMessageHandlerFailingFirstTimes(0);
|
||||||
$throwingHandler = new DummyMessageHandlerFailingFirstTimes(1);
|
$throwingHandler = new DummyMessageHandlerFailingFirstTimes(1);
|
||||||
@ -52,7 +52,7 @@ class RetryIntegrationTest extends TestCase
|
|||||||
$envelope = new Envelope(new DummyMessage('API'));
|
$envelope = new Envelope(new DummyMessage('API'));
|
||||||
$bus->dispatch($envelope);
|
$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) {
|
$worker->run([], function (?Envelope $envelope) use ($worker) {
|
||||||
if (null === $envelope) {
|
if (null === $envelope) {
|
||||||
$worker->stop();
|
$worker->stop();
|
||||||
|
@ -230,4 +230,90 @@ class ConnectionTest extends TestCase
|
|||||||
{
|
{
|
||||||
Connection::buildConfiguration('doctrine://default?new_option=woops');
|
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']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ class WorkerTest extends TestCase
|
|||||||
public function testDispatchCausesRetry()
|
public function testDispatchCausesRetry()
|
||||||
{
|
{
|
||||||
$receiver = new DummyReceiver([
|
$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();
|
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
|
||||||
@ -97,7 +97,7 @@ class WorkerTest extends TestCase
|
|||||||
$this->assertNotNull($redeliveryStamp);
|
$this->assertNotNull($redeliveryStamp);
|
||||||
// retry count now at 1
|
// retry count now at 1
|
||||||
$this->assertSame(1, $redeliveryStamp->getRetryCount());
|
$this->assertSame(1, $redeliveryStamp->getRetryCount());
|
||||||
$this->assertSame('sender_alias', $redeliveryStamp->getSenderClassOrAlias());
|
$this->assertSame('transport1', $redeliveryStamp->getSenderClassOrAlias());
|
||||||
|
|
||||||
// received stamp is removed
|
// received stamp is removed
|
||||||
$this->assertNull($envelope->last(ReceivedStamp::class));
|
$this->assertNull($envelope->last(ReceivedStamp::class));
|
||||||
@ -108,7 +108,7 @@ class WorkerTest extends TestCase
|
|||||||
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
|
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
|
||||||
$retryStrategy->expects($this->once())->method('isRetryable')->willReturn(true);
|
$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) {
|
$worker->run([], function (?Envelope $envelope) use ($worker) {
|
||||||
// stop after the messages finish
|
// stop after the messages finish
|
||||||
if (null === $envelope) {
|
if (null === $envelope) {
|
||||||
@ -123,7 +123,7 @@ class WorkerTest extends TestCase
|
|||||||
public function testDispatchCausesRejectWhenNoRetry()
|
public function testDispatchCausesRejectWhenNoRetry()
|
||||||
{
|
{
|
||||||
$receiver = new DummyReceiver([
|
$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();
|
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
|
||||||
@ -132,7 +132,7 @@ class WorkerTest extends TestCase
|
|||||||
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
|
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
|
||||||
$retryStrategy->expects($this->once())->method('isRetryable')->willReturn(false);
|
$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) {
|
$worker->run([], function (?Envelope $envelope) use ($worker) {
|
||||||
// stop after the messages finish
|
// stop after the messages finish
|
||||||
if (null === $envelope) {
|
if (null === $envelope) {
|
||||||
@ -155,7 +155,7 @@ class WorkerTest extends TestCase
|
|||||||
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
|
$retryStrategy = $this->getMockBuilder(RetryStrategyInterface::class)->getMock();
|
||||||
$retryStrategy->expects($this->never())->method('isRetryable');
|
$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) {
|
$worker->run([], function (?Envelope $envelope) use ($worker) {
|
||||||
// stop after the messages finish
|
// stop after the messages finish
|
||||||
if (null === $envelope) {
|
if (null === $envelope) {
|
||||||
|
@ -155,7 +155,7 @@ class Connection
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$doctrineEnvelope['headers'] = json_decode($doctrineEnvelope['headers'], true);
|
$doctrineEnvelope = $this->decodeEnvelopeHeaders($doctrineEnvelope);
|
||||||
|
|
||||||
$queryBuilder = $this->driverConnection->createQueryBuilder()
|
$queryBuilder = $this->driverConnection->createQueryBuilder()
|
||||||
->update($this->configuration['table_name'])
|
->update($this->configuration['table_name'])
|
||||||
@ -238,7 +238,11 @@ class Connection
|
|||||||
$queryBuilder->setMaxResults($limit);
|
$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
|
public function find($id): ?array
|
||||||
@ -254,7 +258,7 @@ class Connection
|
|||||||
'id' => $id,
|
'id' => $id,
|
||||||
])->fetch();
|
])->fetch();
|
||||||
|
|
||||||
return false === $data ? null : $data;
|
return false === $data ? null : $this->decodeEnvelopeHeaders($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createAvailableMessagesQueryBuilder(): QueryBuilder
|
private function createAvailableMessagesQueryBuilder(): QueryBuilder
|
||||||
@ -332,4 +336,11 @@ class Connection
|
|||||||
{
|
{
|
||||||
return $dateTime->format('Y-m-d\TH:i:s');
|
return $dateTime->format('Y-m-d\TH:i:s');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function decodeEnvelopeHeaders(array $doctrineEnvelope): array
|
||||||
|
{
|
||||||
|
$doctrineEnvelope['headers'] = json_decode($doctrineEnvelope['headers'], true);
|
||||||
|
|
||||||
|
return $doctrineEnvelope;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,11 @@ use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
|
|||||||
use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent;
|
use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent;
|
||||||
use Symfony\Component\Messenger\Event\WorkerStoppedEvent;
|
use Symfony\Component\Messenger\Event\WorkerStoppedEvent;
|
||||||
use Symfony\Component\Messenger\Exception\HandlerFailedException;
|
use Symfony\Component\Messenger\Exception\HandlerFailedException;
|
||||||
use Symfony\Component\Messenger\Exception\LogicException;
|
use Symfony\Component\Messenger\Exception\UnrecoverableExceptionInterface;
|
||||||
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
|
|
||||||
use Symfony\Component\Messenger\Retry\RetryStrategyInterface;
|
use Symfony\Component\Messenger\Retry\RetryStrategyInterface;
|
||||||
use Symfony\Component\Messenger\Stamp\DelayStamp;
|
use Symfony\Component\Messenger\Stamp\DelayStamp;
|
||||||
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
|
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
|
||||||
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
|
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
|
||||||
use Symfony\Component\Messenger\Stamp\SentStamp;
|
|
||||||
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
|
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
|
||||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||||
|
|
||||||
@ -150,7 +148,7 @@ class Worker implements WorkerInterface
|
|||||||
|
|
||||||
// add the delay and retry stamp info + remove ReceivedStamp
|
// add the delay and retry stamp info + remove ReceivedStamp
|
||||||
$retryEnvelope = $envelope->with(new DelayStamp($delay))
|
$retryEnvelope = $envelope->with(new DelayStamp($delay))
|
||||||
->with(new RedeliveryStamp($retryCount, $this->getSenderClassOrAlias($envelope)))
|
->with(new RedeliveryStamp($retryCount, $transportName))
|
||||||
->withoutAll(ReceivedStamp::class);
|
->withoutAll(ReceivedStamp::class);
|
||||||
|
|
||||||
// re-send the message
|
// re-send the message
|
||||||
@ -193,32 +191,10 @@ class Worker implements WorkerInterface
|
|||||||
|
|
||||||
private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInterface $retryStrategy): bool
|
private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInterface $retryStrategy): bool
|
||||||
{
|
{
|
||||||
if ($e instanceof UnrecoverableMessageHandlingException) {
|
if ($e instanceof UnrecoverableExceptionInterface) {
|
||||||
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.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $retryStrategy->isRetryable($envelope);
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,6 @@ class PropertyInfoCacheExtractor implements PropertyInfoExtractorInterface, Prop
|
|||||||
{
|
{
|
||||||
private $propertyInfoExtractor;
|
private $propertyInfoExtractor;
|
||||||
private $cacheItemPool;
|
private $cacheItemPool;
|
||||||
|
|
||||||
/**
|
|
||||||
* A cache of property information, first keyed by the method called and
|
|
||||||
* then by the serialized method arguments.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $arrayCache = [];
|
private $arrayCache = [];
|
||||||
|
|
||||||
public function __construct(PropertyInfoExtractorInterface $propertyInfoExtractor, CacheItemPoolInterface $cacheItemPool)
|
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
|
// Calling rawurlencode escapes special characters not allowed in PSR-6's keys
|
||||||
$encodedMethod = rawurlencode($method);
|
$key = rawurlencode($method.'.'.$serializedArguments);
|
||||||
if (\array_key_exists($encodedMethod, $this->arrayCache) && \array_key_exists($serializedArguments, $this->arrayCache[$encodedMethod])) {
|
|
||||||
return $this->arrayCache[$encodedMethod][$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()) {
|
if ($item->isHit()) {
|
||||||
$this->arrayCache[$encodedMethod] = $data[$encodedMethod];
|
return $this->arrayCache[$key] = $item->get();
|
||||||
// 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 = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = $this->propertyInfoExtractor->{$method}(...$arguments);
|
$value = $this->propertyInfoExtractor->{$method}(...$arguments);
|
||||||
$data[$encodedMethod][$serializedArguments] = $value;
|
$item->set($value);
|
||||||
$this->arrayCache[$encodedMethod][$serializedArguments] = $value;
|
|
||||||
$item->set($data);
|
|
||||||
$this->cacheItemPool->save($item);
|
$this->cacheItemPool->save($item);
|
||||||
|
|
||||||
return $this->arrayCache[$encodedMethod][$serializedArguments];
|
return $this->arrayCache[$key] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,6 @@ class Argon2iPasswordEncoder extends BasePasswordEncoder implements SelfSaltingE
|
|||||||
return true;
|
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');
|
return \function_exists('sodium_crypto_pwhash_str') || \extension_loaded('libsodium');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
$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);
|
$memLimit = $memLimit ?? max(64 * 1024 * 1024, \defined('SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE : 64 * 1024 * 1024);
|
||||||
|
|
||||||
if (2 > $opsLimit) {
|
if (3 > $opsLimit) {
|
||||||
throw new \InvalidArgumentException('$opsLimit must be 2 or greater.');
|
throw new \InvalidArgumentException('$opsLimit must be 3 or greater.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (10 * 1024 > $memLimit) {
|
if (10 * 1024 > $memLimit) {
|
||||||
|
@ -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->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);
|
$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) {
|
if (3 > $this->opsLimit) {
|
||||||
throw new \InvalidArgumentException('$opsLimit must be 2 or greater.');
|
throw new \InvalidArgumentException('$opsLimit must be 3 or greater.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (10 * 1024 > $this->memLimit) {
|
if (10 * 1024 > $this->memLimit) {
|
||||||
@ -48,15 +48,7 @@ final class SodiumPasswordEncoder implements PasswordEncoderInterface, SelfSalti
|
|||||||
|
|
||||||
public static function isSupported(): bool
|
public static function isSupported(): bool
|
||||||
{
|
{
|
||||||
if (\extension_loaded('libsodium') || \function_exists('sodium_crypto_pwhash_str')) {
|
return \function_exists('sodium_crypto_pwhash_str_needs_rehash') || \function_exists('Sodium\crypto_pwhash_str_needs_rehash');
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Validator\Constraints;
|
|||||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||||
|
use Symfony\Component\Validator\Exception\LogicException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for the comparison of values.
|
* Used for the comparison of values.
|
||||||
@ -46,7 +47,7 @@ abstract class AbstractComparison extends Constraint
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($options['propertyPath']) && !class_exists(PropertyAccess::class)) {
|
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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ It also provides a few improvements over `var_export()`/`serialize()`:
|
|||||||
Resources
|
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)
|
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||||
|
@ -41,6 +41,17 @@ switch ($vars['REQUEST_URI']) {
|
|||||||
ob_start('ob_gzhandler');
|
ob_start('ob_gzhandler');
|
||||||
break;
|
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':
|
case '/404':
|
||||||
header('Content-Type: application/json', true, 404);
|
header('Content-Type: application/json', true, 404);
|
||||||
break;
|
break;
|
||||||
|
@ -721,6 +721,15 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
$this->assertSame('/?a=a&b=b', $body['REQUEST_URI']);
|
$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
|
* @requires extension zlib
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user