Merge branch '5.0'

* 5.0:
  [Debug] fix ClassNotFoundFatalErrorHandler
  [FrameworkBundle] Document the router.cache_class_prefix parameter removal
  [Routing] Fix using a custom matcher & generator dumper class
  [Notifier] Add more specific types at documentation level when php engine can't
  [Serializer] Fix cache in MetadataAwareNameConverter
  [Dotenv] Fixed infinite loop with missing quote followed by quoted value
  [HttpClient] Added missing sprintf
  [TwigBridge] button_widget now has its title attr translated even if its label = null or false
  [PhpUnitBridge] When using phpenv + phpenv-composer plugin, composer executable is wrapped into a bash script
  [Messenger] Added check if json_encode succeeded
  [Messenger] Added check if json_encode succeeded
  [FrameworkBundle][ContainerLintCommand] Only skip .errored. services
  [HttpClient] fix exception in case of PSR17 discovery failure
  [DependencyInjection] Handle ServiceClosureArgument for callable in container linting
  fix processing chain adapter based cache pool
  [HttpKernel] release lock explicitly
  [Security] Prevent canceled remember-me cookie from being accepted
  [FrameworkBundle][TranslationUpdateCommand] Do not output positive feedback on stderr
  [Security\Guard] Fix missing typehints
  do not render preferred choices as selected
This commit is contained in:
Nicolas Grekas 2020-01-08 18:33:57 +01:00
commit 8acfd3a9d2
35 changed files with 225 additions and 48 deletions

View File

@ -192,15 +192,15 @@ HttpKernel
* The `Kernel::getRootDir()` and the `kernel.root_dir` parameter have been deprecated
* The `KernelInterface::getName()` and the `kernel.name` parameter have been deprecated
* Deprecated the first and second constructor argument of `ConfigDataCollector`
* Deprecated `ConfigDataCollector::getApplicationName()`
* Deprecated the first and second constructor argument of `ConfigDataCollector`
* Deprecated `ConfigDataCollector::getApplicationName()`
* Deprecated `ConfigDataCollector::getApplicationVersion()`
Messenger
---------
* The `MiddlewareInterface::handle()` and `SenderInterface::send()` methods must now return an `Envelope` instance.
* The return value of handlers isn't forwarded anymore by middleware and buses.
* The return value of handlers isn't forwarded anymore by middleware and buses.
If you used to return a value, e.g in query bus handlers, you can either:
- get the result from the `HandledStamp` in the envelope returned by the bus.
- use the `HandleTrait` to leverage a message bus, expecting a single, synchronous message handling and returning its result.
@ -214,15 +214,15 @@ Messenger
$query->setResult($yourResult);
```
* The `EnvelopeAwareInterface` was removed and the `MiddlewareInterface::handle()` method now requires an `Envelope` object
as first argument. When using built-in middleware with the provided `MessageBus`, you will not have to do anything.
If you use your own `MessageBusInterface` implementation, you must wrap the message in an `Envelope` before passing it to middleware.
as first argument. When using built-in middleware with the provided `MessageBus`, you will not have to do anything.
If you use your own `MessageBusInterface` implementation, you must wrap the message in an `Envelope` before passing it to middleware.
If you created your own middleware, you must change the signature to always expect an `Envelope`.
* The `MiddlewareInterface::handle()` second argument (`callable $next`) has changed in favor of a `StackInterface` instance.
When using built-in middleware with the provided `MessageBus`, you will not have to do anything.
If you use your own `MessageBusInterface` implementation, you can use the `StackMiddleware` implementation.
When using built-in middleware with the provided `MessageBus`, you will not have to do anything.
If you use your own `MessageBusInterface` implementation, you can use the `StackMiddleware` implementation.
If you created your own middleware, you must change the signature to always expect an `StackInterface` instance
and call `$stack->next()->handle($envelope, $stack)` instead of `$next` to call the next middleware:
Before:
```php
public function handle($message, callable $next): Envelope
@ -230,7 +230,7 @@ Messenger
// do something before
$message = $next($message);
// do something after
return $message;
}
```
@ -242,7 +242,7 @@ Messenger
// do something before
$envelope = $stack->next()->handle($envelope, $stack);
// do something after
return $envelope;
}
```
@ -251,7 +251,7 @@ Messenger
respectively `ReceivedStamp`, `ValidationStamp`, `SerializerStamp` and moved to the `Stamp` namespace.
* `AllowNoHandlerMiddleware` has been removed in favor of a new constructor argument on `HandleMessageMiddleware`
* The `ConsumeMessagesCommand` class now takes an instance of `Psr\Container\ContainerInterface`
as first constructor argument, i.e a message bus locator. The CLI command now expects a mandatory
as first constructor argument, i.e a message bus locator. The CLI command now expects a mandatory
`--bus` option value if there is more than one bus in the locator.
* `MessageSubscriberInterface::getHandledMessages()` return value has changed. The value of an array item
needs to be an associative array or the method name.

View File

@ -243,6 +243,7 @@ FrameworkBundle
* Removed `routing.loader.service`.
* Added support for PHPUnit 8. A `void` return-type was added to the `KernelTestCase::tearDown()` and `WebTestCase::tearDown()` method.
* Removed the `lock.store.flock`, `lock.store.semaphore`, `lock.store.memcached.abstract` and `lock.store.redis.abstract` services.
* Removed the `router.cache_class_prefix` parameter.
HttpClient
----------

View File

@ -106,7 +106,7 @@ $COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar')
|| ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`))
|| ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer`) : `which composer 2> /dev/null`))
|| file_exists($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? `git rev-parse --show-toplevel 2> NUL` : `git rev-parse --show-toplevel 2> /dev/null`).DIRECTORY_SEPARATOR.'composer.phar')
? $PHP.' '.escapeshellarg($COMPOSER)
? (file_get_contents($COMPOSER, null, 0, 18) === '#!/usr/bin/env php' ? $PHP : '').' '.escapeshellarg($COMPOSER) // detect shell wrappers by looking at the shebang
: 'composer';
$SYMFONY_PHPUNIT_REMOVE = $getEnvVar('SYMFONY_PHPUNIT_REMOVE', 'phpspec/prophecy'.($PHPUNIT_VERSION < 6.0 ? ' symfony/yaml': ''));

View File

@ -65,12 +65,14 @@
{%- endif -%}
{%- if preferred_choices|length > 0 -%}
{% set options = preferred_choices %}
{% set render_preferred_choices = true %}
{{- block('choice_widget_options') -}}
{%- if choices|length > 0 and separator is not none -%}
<option disabled="disabled">{{ separator }}</option>
{%- endif -%}
{%- endif -%}
{%- set options = choices -%}
{%- set render_preferred_choices = false -%}
{{- block('choice_widget_options') -}}
</select>
{%- endblock choice_widget_collapsed -%}
@ -83,7 +85,7 @@
{{- block('choice_widget_options') -}}
</optgroup>
{%- else -%}
<option value="{{ choice.value }}"{% if choice.attr %}{% with { attr: choice.attr } %}{{ block('attributes') }}{% endwith %}{% endif %}{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice_translation_domain is same as(false) ? choice.label : choice.label|trans({}, choice_translation_domain) }}</option>
<option value="{{ choice.value }}"{% if choice.attr %}{% with { attr: choice.attr } %}{{ block('attributes') }}{% endwith %}{% endif %}{% if not render_preferred_choices|default(false) and choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice_translation_domain is same as(false) ? choice.label : choice.label|trans({}, choice_translation_domain) }}</option>
{%- endif -%}
{% endfor %}
{%- endblock choice_widget_options -%}
@ -226,13 +228,11 @@
'%name%': name,
'%id%': id,
}) %}
{%- elseif label is same as(false) -%}
{% set translation_domain = false %}
{%- else -%}
{%- elseif label is not same as(false) -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ translation_domain is same as(false) ? label : label|trans(label_translation_parameters, translation_domain) }}</button>
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ translation_domain is same as(false) or label is same as(false) ? label : label|trans(label_translation_parameters, translation_domain) }}</button>
{%- endblock button_widget -%}
{%- block submit_widget -%}

View File

@ -160,12 +160,14 @@
{%- endif %}
{%- if preferred_choices|length > 0 -%}
{% set options = preferred_choices %}
{% set render_preferred_choices = true %}
{{- block('choice_widget_options') -}}
{% if choices|length > 0 and separator is not none -%}
<option disabled="disabled">{{ separator }}</option>
{%- endif %}
{%- endif -%}
{% set options = choices -%}
{%- set render_preferred_choices = false -%}
{{- block('choice_widget_options') -}}
</select>
{%- endblock choice_widget_collapsed %}

View File

@ -546,6 +546,31 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
);
}
public function testSingleChoiceWithSelectedPreferred()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', [
'choices' => ['Choice&A' => '&a', 'Choice&B' => '&b'],
'preferred_choices' => ['&a'],
'multiple' => false,
'expanded' => false,
]);
$this->assertWidgetMatchesXpath($form->createView(), ['separator' => '-- sep --', 'attr' => ['class' => 'my&class']],
'/select
[@name="name"]
[@class="my&class form-control"]
[not(@required)]
[
./option[@value="&a"][not(@selected)][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"]
/following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="&b"][.="[trans]Choice&B[/trans]"]
]
[count(./option)=4]
'
);
}
public function testSingleChoiceWithPreferredAndNoSeparator()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', [

View File

@ -29,6 +29,7 @@ CHANGELOG
* Service route loaders must be tagged with `routing.route_loader`.
* Added `slugger` service and `SluggerInterface` alias
* Removed the `lock.store.flock`, `lock.store.semaphore`, `lock.store.memcached.abstract` and `lock.store.redis.abstract` services.
* Removed the `router.cache_class_prefix` parameter.
4.4.0
-----

View File

@ -102,11 +102,12 @@ final class ContainerLintCommand extends Command
$refl->setAccessible(true);
$refl->setValue($parameterBag, true);
$passConfig = $container->getCompilerPassConfig();
$passConfig->setRemovingPasses([]);
$passConfig->setAfterRemovingPasses([]);
$skippedIds = $kernelContainer->getRemovedIds();
$skippedIds = [];
foreach ($container->getServiceIds() as $serviceId) {
if (0 === strpos($serviceId, '.errored.')) {
$skippedIds[$serviceId] = true;
}
}
}
$container->setParameter('container.build_hash', 'lint_container');

View File

@ -175,12 +175,12 @@ EOF
}
}
$errorIo->title('Translation Messages Extractor and Dumper');
$errorIo->comment(sprintf('Generating "<info>%s</info>" translation files for "<info>%s</info>"', $input->getArgument('locale'), $currentName));
$io->title('Translation Messages Extractor and Dumper');
$io->comment(sprintf('Generating "<info>%s</info>" translation files for "<info>%s</info>"', $input->getArgument('locale'), $currentName));
// load any messages from templates
$extractedCatalogue = new MessageCatalogue($input->getArgument('locale'));
$errorIo->comment('Parsing templates...');
$io->comment('Parsing templates...');
$this->extractor->setPrefix($input->getOption('prefix'));
foreach ($viewsPaths as $path) {
if (is_dir($path) || is_file($path)) {
@ -190,7 +190,7 @@ EOF
// load any existing messages from the translation files
$currentCatalogue = new MessageCatalogue($input->getArgument('locale'));
$errorIo->comment('Loading translation files...');
$io->comment('Loading translation files...');
foreach ($transPaths as $path) {
if (is_dir($path)) {
$this->reader->read($path, $currentCatalogue);
@ -258,7 +258,7 @@ EOF
}
if ('xlf' === $input->getOption('output-format')) {
$errorIo->comment(sprintf('Xliff output version is <info>%s</info>', $input->getOption('xliff-version')));
$io->comment(sprintf('Xliff output version is <info>%s</info>', $input->getOption('xliff-version')));
}
$resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was');
@ -270,7 +270,7 @@ EOF
// save the files
if (true === $input->getOption('force')) {
$errorIo->comment('Writing files...');
$io->comment('Writing files...');
$bundleTransPath = false;
foreach ($transPaths as $path) {
@ -290,7 +290,7 @@ EOF
}
}
$errorIo->success($resultMessage.'.');
$io->success($resultMessage.'.');
return 0;
}

View File

@ -33,7 +33,7 @@ class ClearRememberMeTest extends AbstractWebTestCase
$this->assertNotNull($cookieJar->get('REMEMBERME'));
$client->request('GET', '/foo');
$this->assertSame(200, $client->getResponse()->getStatusCode());
$this->assertRedirect($client->getResponse(), '/login');
$this->assertNull($cookieJar->get('REMEMBERME'));
}
}

View File

@ -103,7 +103,12 @@ class CachePoolPass implements CompilerPassInterface
if (ChainAdapter::class === $class) {
$adapters = [];
foreach ($adapter->getArgument(0) as $provider => $adapter) {
$chainedPool = $adapter = new ChildDefinition($adapter);
if ($adapter instanceof ChildDefinition) {
$chainedPool = $adapter;
} else {
$chainedPool = $adapter = new ChildDefinition($adapter);
}
$chainedTags = [\is_int($provider) ? [] : ['provider' => $provider]];
$chainedClass = '';

View File

@ -12,7 +12,9 @@
namespace Symfony\Component\Cache\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
use Symfony\Component\DependencyInjection\ChildDefinition;
@ -174,4 +176,42 @@ class CachePoolPassTest extends TestCase
$this->cachePoolPass->process($container);
}
public function testChainAdapterPool()
{
$container = new ContainerBuilder();
$container->setParameter('kernel.container_class', 'app');
$container->setParameter('kernel.project_dir', 'foo');
$container->register('cache.adapter.array', ArrayAdapter::class)
->addTag('cache.pool');
$container->register('cache.adapter.apcu', ApcuAdapter::class)
->setArguments([null, 0, null])
->addTag('cache.pool');
$container->register('cache.chain', ChainAdapter::class)
->addArgument(['cache.adapter.array', 'cache.adapter.apcu'])
->addTag('cache.pool');
$container->setDefinition('cache.app', new ChildDefinition('cache.chain'))
->addTag('cache.pool');
$container->setDefinition('doctrine.result_cache_pool', new ChildDefinition('cache.app'))
->addTag('cache.pool');
$this->cachePoolPass->process($container);
$appCachePool = $container->getDefinition('cache.app');
$this->assertInstanceOf(ChildDefinition::class, $appCachePool);
$this->assertSame('cache.chain', $appCachePool->getParent());
$chainCachePool = $container->getDefinition('cache.chain');
$this->assertNotInstanceOf(ChildDefinition::class, $chainCachePool);
$this->assertCount(2, $chainCachePool->getArgument(0));
$this->assertInstanceOf(ChildDefinition::class, $chainCachePool->getArgument(0)[0]);
$this->assertSame('cache.adapter.array', $chainCachePool->getArgument(0)[0]->getParent());
$this->assertInstanceOf(ChildDefinition::class, $chainCachePool->getArgument(0)[1]);
$this->assertSame('cache.adapter.apcu', $chainCachePool->getArgument(0)[1]->getParent());
$doctrineCachePool = $container->getDefinition('doctrine.result_cache_pool');
$this->assertInstanceOf(ChildDefinition::class, $doctrineCachePool);
$this->assertSame('cache.app', $doctrineCachePool->getParent());
}
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
@ -219,6 +220,10 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
return;
}
if (\in_array($type, ['callable', 'Closure'], true) && $value instanceof ServiceClosureArgument) {
return;
}
if ('iterable' === $type && (\is_array($value) || $value instanceof \Traversable || $value instanceof IteratorArgument)) {
return;
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -697,4 +698,28 @@ class CheckTypeDeclarationsPassTest extends TestCase
$this->addToAssertionCount(1);
}
public function testProcessSuccessWhenPassingServiceClosureArgumentToCallable()
{
$container = new ContainerBuilder();
$container->register('bar', BarMethodCall::class)
->addMethodCall('setCallable', [new ServiceClosureArgument(new Reference('foo'))]);
(new CheckTypeDeclarationsPass(true))->process($container);
$this->addToAssertionCount(1);
}
public function testProcessSuccessWhenPassingServiceClosureArgumentToClosure()
{
$container = new ContainerBuilder();
$container->register('bar', BarMethodCall::class)
->addMethodCall('setClosure', [new ServiceClosureArgument(new Reference('foo'))]);
(new CheckTypeDeclarationsPass(true))->process($container);
$this->addToAssertionCount(1);
}
}

View File

@ -40,4 +40,8 @@ class BarMethodCall
public function setCallable(callable $callable): void
{
}
public function setClosure(\Closure $closure): void
{
}
}

View File

@ -272,7 +272,10 @@ final class Dotenv
$this->cursor += 1 + $len;
} elseif ('"' === $this->data[$this->cursor]) {
$value = '';
++$this->cursor;
if (++$this->cursor === $this->end) {
throw $this->createFormatException('Missing quote to end the value');
}
while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
$value .= $this->data[$this->cursor];

View File

@ -40,6 +40,7 @@ class DotenvTest extends TestCase
['FOO', "Missing = in the environment variable declaration in \".env\" at line 1.\n...FOO...\n ^ line 1 offset 3"],
['FOO="foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO=\"foo...\n ^ line 1 offset 8"],
['FOO=\'foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo...\n ^ line 1 offset 8"],
["FOO=\"foo\nBAR=\"bar\"", "Missing quote to end the value in \".env\" at line 1.\n...FOO=\"foo\\nBAR=\"bar\"...\n ^ line 1 offset 18"],
['FOO=\'foo'."\n", "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo\\n...\n ^ line 1 offset 9"],
['export FOO', "Unable to unset an environment variable in \".env\" at line 1.\n...export FOO...\n ^ line 1 offset 10"],
['FOO=${FOO', "Unclosed braces on variable expansion in \".env\" at line 1.\n...FOO=\${FOO...\n ^ line 1 offset 9"],

View File

@ -30,6 +30,10 @@ class ClassNotFoundErrorEnhancerTest extends TestCase
// get class loaders wrapped by DebugClassLoader
if ($function[0] instanceof DebugClassLoader) {
$function = $function[0]->getClassLoader();
if (!\is_array($function)) {
continue;
}
}
if ($function[0] instanceof ComposerClassLoader) {

View File

@ -16,6 +16,7 @@ use Http\Client\Exception\NetworkException;
use Http\Client\Exception\RequestException;
use Http\Client\HttpAsyncClient;
use Http\Client\HttpClient as HttplugInterface;
use Http\Discovery\Exception\NotFoundException;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Message\RequestFactory;
use Http\Message\StreamFactory;
@ -75,9 +76,13 @@ final class HttplugClient implements HttplugInterface, HttpAsyncClient, RequestF
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been provided. Try running "composer require nyholm/psr7".');
}
$psr17Factory = class_exists(Psr17Factory::class, false) ? new Psr17Factory() : null;
$this->responseFactory = $this->responseFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findResponseFactory();
$this->streamFactory = $this->streamFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findStreamFactory();
try {
$psr17Factory = class_exists(Psr17Factory::class, false) ? new Psr17Factory() : null;
$this->responseFactory = $this->responseFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findResponseFactory();
$this->streamFactory = $this->streamFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findStreamFactory();
} catch (NotFoundException $e) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been found. Try running "composer require nyholm/psr7".', 0, $e);
}
}
$this->waitLoop = new HttplugWaitLoop($this->client, $this->promisePool, $this->responseFactory, $this->streamFactory);

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\HttpClient;
use Http\Discovery\Exception\NotFoundException;
use Http\Discovery\Psr17FactoryDiscovery;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7\Request;
@ -68,9 +69,13 @@ final class Psr18Client implements ClientInterface, RequestFactoryInterface, Str
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as no PSR-17 factories have been provided. Try running "composer require nyholm/psr7".');
}
$psr17Factory = class_exists(Psr17Factory::class, false) ? new Psr17Factory() : null;
$this->responseFactory = $this->responseFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findResponseFactory();
$this->streamFactory = $this->streamFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findStreamFactory();
try {
$psr17Factory = class_exists(Psr17Factory::class, false) ? new Psr17Factory() : null;
$this->responseFactory = $this->responseFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findResponseFactory();
$this->streamFactory = $this->streamFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findStreamFactory();
} catch (NotFoundException $e) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been found. Try running "composer require nyholm/psr7".', 0, $e);
}
}
/**

View File

@ -353,7 +353,7 @@ trait ResponseTrait
}
if ('' !== $chunk && null !== $response->content && \strlen($chunk) !== fwrite($response->content, $chunk)) {
$multi->handlesActivity[$j] = [null, new TransportException('Failed writing %d bytes to the response buffer.', \strlen($chunk))];
$multi->handlesActivity[$j] = [null, new TransportException(sprintf('Failed writing %d bytes to the response buffer.', \strlen($chunk)))];
continue;
}

View File

@ -475,7 +475,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
}
}
public function __destruct()
public function release()
{
flock($this->lock, LOCK_UN);
fclose($this->lock);
@ -553,7 +553,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
}
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
unset($cache);
$cache->release();
$this->container = require $cachePath;
$this->container->set('kernel', $this);

View File

@ -186,6 +186,18 @@ class ConnectionTest extends TestCase
$redis->del('messenger-getnonblocking');
}
public function testJsonError()
{
$redis = new \Redis();
$connection = Connection::fromDsn('redis://localhost/json-error', [], $redis);
try {
$connection->add("\xB1\x31", []);
} catch (TransportException $e) {
}
$this->assertSame('Malformed UTF-8 characters, possibly incorrectly encoded', $e->getMessage());
}
public function testMaxEntries()
{
$redis = $this->getMockBuilder(\Redis::class)->disableOriginalConstructor()->getMock();

View File

@ -248,6 +248,10 @@ class Connection
'uniqid' => uniqid('', true),
]);
if (false === $message) {
throw new TransportException(json_last_error_msg());
}
$score = (int) ($this->getCurrentTimeInMilliseconds() + $delayInMs);
$added = $this->connection->zadd($this->queue, ['NX'], $score, $message);
} else {
@ -256,6 +260,10 @@ class Connection
'headers' => $headers,
]);
if (false === $message) {
throw new TransportException(json_last_error_msg());
}
if ($this->maxEntries) {
$added = $this->connection->xadd($this->stream, '*', ['message' => $message], $this->maxEntries, true);
} else {

View File

@ -23,6 +23,9 @@ use Symfony\Component\Notifier\Transport\TransportInterface;
*/
final class NexmoTransportFactory extends AbstractTransportFactory
{
/**
* @return NexmoTransport
*/
public function create(Dsn $dsn): TransportInterface
{
$scheme = $dsn->getScheme();

View File

@ -23,6 +23,9 @@ use Symfony\Component\Notifier\Transport\TransportInterface;
*/
final class SlackTransportFactory extends AbstractTransportFactory
{
/**
* @return SlackTransport
*/
public function create(Dsn $dsn): TransportInterface
{
$scheme = $dsn->getScheme();

View File

@ -24,6 +24,9 @@ use Symfony\Component\Notifier\Transport\TransportInterface;
*/
final class TelegramTransportFactory extends AbstractTransportFactory
{
/**
* @return TelegramTransport
*/
public function create(Dsn $dsn): TransportInterface
{
$scheme = $dsn->getScheme();

View File

@ -23,6 +23,9 @@ use Symfony\Component\Notifier\Transport\TransportInterface;
*/
final class TwilioTransportFactory extends AbstractTransportFactory
{
/**
* @return TwilioTransport
*/
public function create(Dsn $dsn): TransportInterface
{
$scheme = $dsn->getScheme();

View File

@ -58,7 +58,7 @@ final class SmsMessage implements MessageInterface
return $this->phone;
}
public function getRecipientId(): ?string
public function getRecipientId(): string
{
return $this->phone;
}

View File

@ -20,6 +20,9 @@ use Symfony\Component\Notifier\Exception\UnsupportedSchemeException;
*/
final class NullTransportFactory extends AbstractTransportFactory
{
/**
* @return NullTransport
*/
public function create(Dsn $dsn): TransportInterface
{
if ('null' === $dsn->getScheme()) {

View File

@ -45,7 +45,7 @@ class GuardAuthenticationListener extends AbstractListener
* @param string $providerKey The provider (i.e. firewall) key
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
*/
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, $guardAuthenticators, LoggerInterface $logger = null)
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, iterable $guardAuthenticators, LoggerInterface $logger = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');

View File

@ -48,7 +48,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
* @param string $providerKey The provider (i.e. firewall) key
*/
public function __construct($guardAuthenticators, UserProviderInterface $userProvider, string $providerKey, UserCheckerInterface $userChecker, UserPasswordEncoderInterface $passwordEncoder = null)
public function __construct(iterable $guardAuthenticators, UserProviderInterface $userProvider, string $providerKey, UserCheckerInterface $userChecker, UserPasswordEncoderInterface $passwordEncoder = null)
{
$this->guardAuthenticators = $guardAuthenticators;
$this->userProvider = $userProvider;

View File

@ -97,6 +97,10 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
*/
final public function autoLogin(Request $request): ?TokenInterface
{
if (($cookie = $request->attributes->get(self::COOKIE_ATTR_NAME)) && null === $cookie->getValue()) {
return null;
}
if (null === $cookie = $request->cookies->get($this->options['name'])) {
return null;
}

View File

@ -39,6 +39,17 @@ class AbstractRememberMeServicesTest extends TestCase
$this->assertNull($service->autoLogin(new Request()));
}
public function testAutoLoginReturnsNullAfterLoginFail()
{
$service = $this->getService(null, ['name' => 'foo', 'path' => null, 'domain' => null]);
$request = new Request();
$request->cookies->set('foo', 'foo');
$service->loginFail($request);
$this->assertNull($service->autoLogin($request));
}
public function testAutoLoginThrowsExceptionWhenImplementationDoesNotReturnUserInterface()
{
$this->expectException('RuntimeException');

View File

@ -47,7 +47,7 @@ final class MetadataAwareNameConverter implements AdvancedNameConverterInterface
return $this->normalizeFallback($propertyName, $class, $format, $context);
}
if (!isset(self::$normalizeCache[$class][$propertyName])) {
if (!\array_key_exists($class, self::$normalizeCache) || !\array_key_exists($propertyName, self::$normalizeCache[$class])) {
self::$normalizeCache[$class][$propertyName] = $this->getCacheValueForNormalization($propertyName, $class);
}
@ -64,7 +64,7 @@ final class MetadataAwareNameConverter implements AdvancedNameConverterInterface
}
$cacheKey = $this->getCacheKey($class, $context);
if (!isset(self::$denormalizeCache[$cacheKey][$propertyName])) {
if (!\array_key_exists($cacheKey, self::$denormalizeCache) || !\array_key_exists($propertyName, self::$denormalizeCache[$cacheKey])) {
self::$denormalizeCache[$cacheKey][$propertyName] = $this->getCacheValueForDenormalization($propertyName, $class, $context);
}
@ -78,7 +78,7 @@ final class MetadataAwareNameConverter implements AdvancedNameConverterInterface
}
$attributesMetadata = $this->metadataFactory->getMetadataFor($class)->getAttributesMetadata();
if (!isset($attributesMetadata[$propertyName])) {
if (!\array_key_exists($propertyName, $attributesMetadata)) {
return null;
}
@ -93,7 +93,7 @@ final class MetadataAwareNameConverter implements AdvancedNameConverterInterface
private function getCacheValueForDenormalization(string $propertyName, string $class, array $context): ?string
{
$cacheKey = $this->getCacheKey($class, $context);
if (!isset(self::$attributesMetadataCache[$cacheKey])) {
if (!\array_key_exists($cacheKey, self::$attributesMetadataCache)) {
self::$attributesMetadataCache[$cacheKey] = $this->getCacheValueForAttributesMetadata($class, $context);
}