Merge branch '4.3'

* 4.3: (23 commits)
  minor: add some test in the ldap component
  [Workflow] Update MethodMarkingStore
  [Bridge\ProxyManager] isProxyCandidate() does not take into account interfaces
  adding experimental note
  [HttpClient] add missing argument check
  [Messenger] Fix undefined index on read timeout
  [Workflow] use method marking store
  [Routing][AnnotationClassLoader] fix utf-8 encoding in default route name
  fixed a phpdoc
  [Debug] Wrap call to require_once in a try/catch
  [EventDispatcher] Removed "callable" type hint from WrappedListener constructor
  prevent deprecation when filesize matches error code
  [PropertyInfo] Add missing documentation link in Readme
  Use the current working dir as default first arg in 'link' binary
  Respect parent class contract in ContainerAwareDoctrineEventManager
  [WebProfilerBundle][Form] The form data collector return serialized object when profiler bundle attends object
  [Validator] Add the missing translations for the Danish ("da") locale
  [PropertyAccess] Add missing property to PropertyAccessor
  [Cache] fix saving unrelated keys in recursive callback calls
  [Serializer] Fix denormalization of object with variadic constructor typed argument
  ...
This commit is contained in:
Nicolas Grekas 2019-05-20 18:28:43 +02:00
commit beb603694d
34 changed files with 479 additions and 89 deletions

16
link
View File

@ -23,14 +23,12 @@ use Symfony\Component\Filesystem\Filesystem;
* @author Kévin Dunglas <dunglas@gmail.com>
*/
if (2 !== $argc) {
echo 'Link dependencies to components to a local clone of the main symfony/symfony GitHub repository.'.PHP_EOL.PHP_EOL;
echo "Usage: $argv[0] /path/to/the/project".PHP_EOL;
exit(1);
}
$pathToProject = $argv[1] ?? getcwd();
if (!is_dir("$argv[1]/vendor/symfony")) {
echo "The directory \"$argv[1]\" does not exist or the dependencies are not installed, did you forget to run \"composer install\" in your project?".PHP_EOL;
if (!is_dir("$pathToProject/vendor/symfony")) {
echo 'Link dependencies to components to a local clone of the main symfony/symfony GitHub repository.'.PHP_EOL.PHP_EOL;
echo "Usage: $argv[0] /path/to/the/project".PHP_EOL.PHP_EOL;
echo "The directory \"$pathToProject\" does not exist or the dependencies are not installed, did you forget to run \"composer install\" in your project?".PHP_EOL;
exit(1);
}
@ -50,7 +48,7 @@ foreach ($directories as $dir) {
}
}
foreach (glob("$argv[1]/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
foreach (glob("$pathToProject/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$package = 'symfony/'.basename($dir);
if (is_link($dir)) {
echo "\"$package\" is already a symlink, skipping.".PHP_EOL;
@ -68,6 +66,6 @@ foreach (glob("$argv[1]/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir)
echo "\"$package\" has been linked to \"$sfPackages[$package]\".".PHP_EOL;
}
foreach (glob("$argv[1]/var/cache/*") as $cacheDir) {
foreach (glob("$pathToProject/var/cache/*") as $cacheDir) {
$filesystem->remove($cacheDir);
}

View File

@ -37,51 +37,49 @@ class ContainerAwareEventManager extends EventManager
}
/**
* Dispatches an event to all registered listeners.
*
* @param string $eventName The name of the event to dispatch. The name of the event is
* the name of the method that is invoked on listeners.
* @param EventArgs $eventArgs The event arguments to pass to the event handlers/listeners.
* If not supplied, the single empty EventArgs instance is used.
*
* @return bool
* {@inheritdoc}
*/
public function dispatchEvent($eventName, EventArgs $eventArgs = null)
{
if (isset($this->listeners[$eventName])) {
$eventArgs = null === $eventArgs ? EventArgs::getEmptyInstance() : $eventArgs;
if (!isset($this->listeners[$eventName])) {
return;
}
$initialized = isset($this->initialized[$eventName]);
$eventArgs = null === $eventArgs ? EventArgs::getEmptyInstance() : $eventArgs;
foreach ($this->listeners[$eventName] as $hash => $listener) {
if (!$initialized && \is_string($listener)) {
$this->listeners[$eventName][$hash] = $listener = $this->container->get($listener);
}
if (!isset($this->initialized[$eventName])) {
$this->initializeListeners($eventName);
}
$listener->$eventName($eventArgs);
}
$this->initialized[$eventName] = true;
foreach ($this->listeners[$eventName] as $hash => $listener) {
$listener->$eventName($eventArgs);
}
}
/**
* Gets the listeners of a specific event or all listeners.
*
* @param string $event The name of the event
*
* @return array The event listeners for the specified event, or all event listeners
* {@inheritdoc}
*/
public function getListeners($event = null)
{
return $event ? $this->listeners[$event] : $this->listeners;
if (null !== $event) {
if (!isset($this->initialized[$event])) {
$this->initializeListeners($event);
}
return $this->listeners[$event];
}
foreach ($this->listeners as $event => $listeners) {
if (!isset($this->initialized[$event])) {
$this->initializeListeners($event);
}
}
return $this->listeners;
}
/**
* Checks whether an event has any registered listeners.
*
* @param string $event
*
* @return bool TRUE if the specified event has any listeners, FALSE otherwise
* {@inheritdoc}
*/
public function hasListeners($event)
{
@ -89,20 +87,11 @@ class ContainerAwareEventManager extends EventManager
}
/**
* Adds an event listener that listens on the specified events.
*
* @param string|array $events The event(s) to listen on
* @param object|string $listener The listener object
*
* @throws \RuntimeException
* {@inheritdoc}
*/
public function addEventListener($events, $listener)
{
if (\is_string($listener)) {
if ($this->initialized) {
throw new \RuntimeException('Adding lazy-loading listeners after construction is not supported.');
}
$hash = '_service_'.$listener;
} else {
// Picks the hash code related to that listener
@ -113,14 +102,15 @@ class ContainerAwareEventManager extends EventManager
// Overrides listener if a previous one was associated already
// Prevents duplicate listeners on same event (same instance only)
$this->listeners[$event][$hash] = $listener;
if (\is_string($listener)) {
unset($this->initialized[$event]);
}
}
}
/**
* Removes an event listener from the specified events.
*
* @param string|array $events
* @param object|string $listener
* {@inheritdoc}
*/
public function removeEventListener($events, $listener)
{
@ -138,4 +128,17 @@ class ContainerAwareEventManager extends EventManager
}
}
}
/**
* @param string $eventName
*/
private function initializeListeners($eventName)
{
foreach ($this->listeners[$eventName] as $hash => $listener) {
if (\is_string($listener)) {
$this->listeners[$eventName][$hash] = $this->container->get($listener);
}
}
$this->initialized[$eventName] = true;
}
}

View File

@ -28,8 +28,8 @@ class ContainerAwareEventManagerTest extends TestCase
public function testDispatchEvent()
{
$this->container->set('foobar', $listener1 = new MyListener());
$this->evm->addEventListener('foo', 'foobar');
$this->container->set('lazy', $listener1 = new MyListener());
$this->evm->addEventListener('foo', 'lazy');
$this->evm->addEventListener('foo', $listener2 = new MyListener());
$this->evm->dispatchEvent('foo');
@ -38,19 +38,69 @@ class ContainerAwareEventManagerTest extends TestCase
$this->assertTrue($listener2->called);
}
public function testAddEventListenerAfterDispatchEvent()
{
$this->container->set('lazy1', $listener1 = new MyListener());
$this->evm->addEventListener('foo', 'lazy1');
$this->evm->addEventListener('foo', $listener2 = new MyListener());
$this->evm->dispatchEvent('foo');
$this->container->set('lazy2', $listener3 = new MyListener());
$this->evm->addEventListener('foo', 'lazy2');
$this->evm->addEventListener('foo', $listener4 = new MyListener());
$this->evm->dispatchEvent('foo');
$this->assertTrue($listener1->called);
$this->assertTrue($listener2->called);
$this->assertTrue($listener3->called);
$this->assertTrue($listener4->called);
}
public function testGetListenersForEvent()
{
$this->container->set('lazy', $listener1 = new MyListener());
$this->evm->addEventListener('foo', 'lazy');
$this->evm->addEventListener('foo', $listener2 = new MyListener());
$this->assertSame([$listener1, $listener2], array_values($this->evm->getListeners('foo')));
}
public function testGetListeners()
{
$this->container->set('lazy', $listener1 = new MyListener());
$this->evm->addEventListener('foo', 'lazy');
$this->evm->addEventListener('foo', $listener2 = new MyListener());
$this->assertSame([$listener1, $listener2], array_values($this->evm->getListeners()['foo']));
}
public function testRemoveEventListener()
{
$this->evm->addEventListener('foo', 'bar');
$this->evm->addEventListener('foo', $listener = new MyListener());
$this->container->set('lazy', $listener1 = new MyListener());
$this->evm->addEventListener('foo', 'lazy');
$this->evm->addEventListener('foo', $listener2 = new MyListener());
$listeners = ['foo' => ['_service_bar' => 'bar', spl_object_hash($listener) => $listener]];
$this->assertSame($listeners, $this->evm->getListeners());
$this->assertSame($listeners['foo'], $this->evm->getListeners('foo'));
$this->evm->removeEventListener('foo', $listener2);
$this->assertSame([$listener1], array_values($this->evm->getListeners('foo')));
$this->evm->removeEventListener('foo', $listener);
$this->assertSame(['_service_bar' => 'bar'], $this->evm->getListeners('foo'));
$this->evm->removeEventListener('foo', 'lazy');
$this->assertSame([], $this->evm->getListeners('foo'));
}
$this->evm->removeEventListener('foo', 'bar');
public function testRemoveEventListenerAfterDispatchEvent()
{
$this->container->set('lazy', $listener1 = new MyListener());
$this->evm->addEventListener('foo', 'lazy');
$this->evm->addEventListener('foo', $listener2 = new MyListener());
$this->evm->dispatchEvent('foo');
$this->evm->removeEventListener('foo', $listener2);
$this->assertSame([$listener1], array_values($this->evm->getListeners('foo')));
$this->evm->removeEventListener('foo', 'lazy');
$this->assertSame([], $this->evm->getListeners('foo'));
}
}

View File

@ -14,6 +14,7 @@ namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy\PhpDumper;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface;
/**
* Tests for {@see \Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper}.
@ -182,6 +183,7 @@ EOPHP;
$definitions = [
[new Definition(__CLASS__), true],
[new Definition('stdClass'), true],
[new Definition(DumperInterface::class), true],
[new Definition(uniqid('foo', true)), false],
[new Definition(), false],
];

View File

@ -32,7 +32,8 @@
"conflict": {
"symfony/dependency-injection": "<3.4",
"symfony/messenger": "<4.2",
"symfony/var-dumper": "<3.4"
"symfony/var-dumper": "<3.4",
"symfony/form": "<4.3"
},
"autoload": {
"psr-4": { "Symfony\\Bundle\\WebProfilerBundle\\": "" },

View File

@ -59,6 +59,26 @@ abstract class AdapterTestCase extends CachePoolTest
$this->assertFalse($isHit);
}
public function testRecursiveGet()
{
if (isset($this->skippedTests[__FUNCTION__])) {
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
}
$cache = $this->createCachePool(0, __FUNCTION__);
$v = $cache->get('k1', function () use (&$counter, $cache) {
$v = $cache->get('k2', function () use (&$counter) { return ++$counter; });
$v = $cache->get('k2', function () use (&$counter) { return ++$counter; });
return $v;
});
$this->assertSame(1, $counter);
$this->assertSame(1, $v);
$this->assertSame(1, $cache->get('k2', function () { return 2; }));
}
public function testGetMetadata()
{
if (isset($this->skippedTests[__FUNCTION__])) {

View File

@ -23,6 +23,7 @@ class PhpArrayAdapterTest extends AdapterTestCase
{
protected $skippedTests = [
'testGet' => 'PhpArrayAdapter is read-only.',
'testRecursiveGet' => 'PhpArrayAdapter is read-only.',
'testBasicUsage' => 'PhpArrayAdapter is read-only.',
'testBasicUsageWithLongKey' => 'PhpArrayAdapter is read-only.',
'testClear' => 'PhpArrayAdapter is read-only.',

View File

@ -32,6 +32,7 @@ trait ContractsTrait
}
private $callbackWrapper = [LockRegistry::class, 'compute'];
private $computing = [];
/**
* Wraps the callback passed to ->get() in a callable.
@ -69,26 +70,27 @@ trait ContractsTrait
CacheItem::class
);
return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use ($pool, $callback, $setMetadata, &$metadata) {
return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use ($pool, $callback, $setMetadata, &$metadata, $key) {
// don't wrap nor save recursive calls
if (null === $callbackWrapper = $this->callbackWrapper) {
if (isset($this->computing[$key])) {
$value = $callback($item, $save);
$save = false;
return $value;
}
$this->callbackWrapper = null;
$this->computing[$key] = $key;
$startTime = microtime(true);
try {
$value = $callbackWrapper($callback, $item, $save, $pool, function (CacheItem $item) use ($setMetadata, $startTime, &$metadata) {
$value = ($this->callbackWrapper)($callback, $item, $save, $pool, function (CacheItem $item) use ($setMetadata, $startTime, &$metadata) {
$setMetadata($item, $startTime, $metadata);
}, $this->logger ?? null);
$setMetadata($item, $startTime, $metadata);
return $value;
} finally {
$this->callbackWrapper = $callbackWrapper;
unset($this->computing[$key]);
}
}, $beta, $metadata, $this->logger ?? null);
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\Config\Resource;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface as LegacyServiceSubscriberInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
/**
@ -158,6 +159,13 @@ class ReflectionClassResource implements SelfCheckingResourceInterface
yield print_r($class->name::getSubscribedEvents(), true);
}
if (interface_exists(MessageSubscriberInterface::class, false) && $class->isSubclassOf(MessageSubscriberInterface::class)) {
yield MessageSubscriberInterface::class;
foreach ($class->name::getHandledMessages() as $key => $value) {
yield $key.print_r($value, true);
}
}
if (interface_exists(LegacyServiceSubscriberInterface::class, false) && $class->isSubclassOf(LegacyServiceSubscriberInterface::class)) {
yield LegacyServiceSubscriberInterface::class;
yield print_r([$class->name, 'getSubscribedServices'](), true);

View File

@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\ReflectionClassResource;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
class ReflectionClassResourceTest extends TestCase
{
@ -147,6 +148,24 @@ EOPHP;
$this->assertTrue($res->isFresh(0));
}
public function testMessageSubscriber()
{
$res = new ReflectionClassResource(new \ReflectionClass(TestMessageSubscriber::class));
$this->assertTrue($res->isFresh(0));
TestMessageSubscriberConfigHolder::$handledMessages = ['SomeMessageClass' => []];
$this->assertFalse($res->isFresh(0));
$res = new ReflectionClassResource(new \ReflectionClass(TestMessageSubscriber::class));
$this->assertTrue($res->isFresh(0));
TestMessageSubscriberConfigHolder::$handledMessages = ['OtherMessageClass' => []];
$this->assertFalse($res->isFresh(0));
$res = new ReflectionClassResource(new \ReflectionClass(TestMessageSubscriber::class));
$this->assertTrue($res->isFresh(0));
}
public function testServiceSubscriber()
{
$res = new ReflectionClassResource(new \ReflectionClass(TestServiceSubscriber::class));
@ -174,6 +193,20 @@ class TestEventSubscriber implements EventSubscriberInterface
}
}
class TestMessageSubscriber implements MessageSubscriberInterface
{
public static function getHandledMessages(): iterable
{
foreach (TestMessageSubscriberConfigHolder::$handledMessages as $key => $subscribedMessage) {
yield $key => $subscribedMessage;
}
}
}
class TestMessageSubscriberConfigHolder
{
public static $handledMessages = [];
}
class TestServiceSubscriber implements ServiceSubscriberInterface
{
public static $subscribedServices = [];

View File

@ -24,6 +24,7 @@
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/event-dispatcher": "~3.4|~4.0",
"symfony/finder": "~3.4|~4.0",
"symfony/messenger": "~4.1",
"symfony/yaml": "~3.4|~4.0"
},
"conflict": {

View File

@ -171,7 +171,11 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
}
}
require_once $file;
try {
require_once $file;
} catch (\Throwable $e) {
return null;
}
foreach ($candidates as $candidate) {
if ($this->classExists($candidate)) {

View File

@ -38,7 +38,7 @@ class WrappedListener
private $priority;
private static $hasClassStub;
public function __construct(callable $listener, ?string $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
public function __construct($listener, ?string $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
{
$this->listener = $listener;
$this->optimizedListener = $listener instanceof \Closure ? $listener : \Closure::fromCallable($listener);

View File

@ -14,6 +14,122 @@
<source>The CSRF token is invalid. Please try to resubmit the form.</source>
<target>CSRF-token er ugyldig.</target>
</trans-unit>
<trans-unit id="64">
<source>This value is not a valid currency.</source>
<target>Denne værdi er ikke en gyldig valuta.</target>
</trans-unit>
<trans-unit id="65">
<source>This value should be equal to {{ compared_value }}.</source>
<target>Denne værdi skal være lig med {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="66">
<source>This value should be greater than {{ compared_value }}.</source>
<target>Denne værdi skal være større end {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="67">
<source>This value should be greater than or equal to {{ compared_value }}.</source>
<target>Denne værdi skal være større end eller lig med {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="68">
<source>This value should be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Denne værdi skal være identisk med {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="69">
<source>This value should be less than {{ compared_value }}.</source>
<target>Denne værdi skal være mindre end {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="70">
<source>This value should be less than or equal to {{ compared_value }}.</source>
<target>Denne værdi skal være mindre end eller lig med {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="71">
<source>This value should not be equal to {{ compared_value }}.</source>
<target>Denne værdi bør ikke være lig med {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="72">
<source>This value should not be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Denne værdi bør ikke være identisk med {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="73">
<source>The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.</source>
<target>Billedforholdet er for stort ({{ratio}}). Tilladt maksimumsforhold er {{ max_ratio }}.</target>
</trans-unit>
<trans-unit id="74">
<source>The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.</source>
<target>Billedforholdet er for lille ({{ ratio }}). Minimumsforventet forventet er {{ min_ratio }}.</target>
</trans-unit>
<trans-unit id="75">
<source>The image is square ({{ width }}x{{ height }}px). Square images are not allowed.</source>
<target>Billedet er firkantet ({{ width }} x {{ height }} px). Firkantede billeder er ikke tilladt.</target>
</trans-unit>
<trans-unit id="76">
<source>The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.</source>
<target>Billedet er landskabsorienteret ({{width}} x {{height}} px). Landskabsorienterede billeder er ikke tilladt</target>
</trans-unit>
<trans-unit id="77">
<source>The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.</source>
<target>Billedet er portrætorienteret ({{ width }}x{{ height }}px). Portrætorienterede billeder er ikke tilladt.</target>
</trans-unit>
<trans-unit id="78">
<source>An empty file is not allowed.</source>
<target>En tom fil er ikke tilladt.</target>
</trans-unit>
<trans-unit id="79">
<source>The host could not be resolved.</source>
<target>Værten kunne ikke løses.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Denne værdi stemmer ikke overens med den forventede {{ charset }} charset.</target>
</trans-unit>
<trans-unit id="81">
<source>This is not a valid Business Identifier Code (BIC).</source>
<target>Dette er ikke en gyldig Business Identifier Code (BIC).a</target>
</trans-unit>
<trans-unit id="83">
<source>This is not a valid UUID.</source>
<target>Dette er ikke en gyldig UUID.</target>
</trans-unit>
<trans-unit id="84">
<source>This value should be a multiple of {{ compared_value }}.</source>
<target>Denne værdi skal være et flertal af {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>Denne Business Identifier Code (BIC) er ikke forbundet med IBAN {{ iban }}.</target>
</trans-unit>
<trans-unit id="86">
<source>This value should be valid JSON.</source>
<target>Denne værdi skal være gyldig JSON.</target>
</trans-unit>
<trans-unit id="87">
<source>This collection should contain only unique elements.</source>
<target>Denne samling bør kun indeholde unikke elementer.</target>
</trans-unit>
<trans-unit id="88">
<source>This value should be positive.</source>
<target>Denne værdi skal være positiv.</target>
</trans-unit>
<trans-unit id="89">
<source>This value should be either positive or zero.</source>
<target>Denne værdi skal være enten positiv eller nul.</target>
</trans-unit>
<trans-unit id="90">
<source>This value should be negative.</source>
<target>Denne værdi skal være negativ.</target>
</trans-unit>
<trans-unit id="91">
<source>This value should be either negative or zero.</source>
<target>Denne værdi skal være enten negativ eller nul.</target>
</trans-unit>
<trans-unit id="92">
<source>This value is not a valid timezone.</source>
<target>Denne værdi er ikke en gyldig tidszone.</target>
</trans-unit>
<trans-unit id="93">
<source>This password has been leaked in a data breach, it must not be used. Please use another password.</source>
<target>Denne adgangskode er blevet lækket i et databrud, det må ikke bruges. Brug venligst en anden adgangskode.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -36,6 +36,10 @@ class ScopingHttpClient implements HttpClientInterface
$this->client = $client;
$this->defaultOptionsByRegexp = $defaultOptionsByRegexp;
$this->defaultRegexp = $defaultRegexp;
if (null !== $defaultRegexp && !isset($defaultOptionsByRegexp[$defaultRegexp])) {
throw new InvalidArgumentException(sprintf('No options are mapped to the provided "%s" default regexp.', $defaultRegexp));
}
}
public static function forBaseUri(HttpClientInterface $client, string $baseUri, array $defaultOptions = [], $regexp = null): self

View File

@ -29,6 +29,7 @@ class Cookie
private $sameSite;
private $secureDefault = false;
const SAMESITE_NONE = 'none';
const SAMESITE_LAX = 'lax';
const SAMESITE_STRICT = 'strict';
@ -126,7 +127,7 @@ class Cookie
$sameSite = strtolower($sameSite);
}
if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, null], true)) {
if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE, null], true)) {
throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
}

View File

@ -84,7 +84,7 @@ class FileBag extends ParameterBag
if (UPLOAD_ERR_NO_FILE == $file['error']) {
$file = null;
} else {
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error']);
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error'], false);
}
} else {
$file = array_map([$this, 'convertFileInformation'], $file);

View File

@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
interface FlashBagInterface extends SessionBagInterface
{
/**
* Adds a flash message for type.
* Adds a flash message for the given type.
*
* @param string $type
* @param mixed $message
@ -29,12 +29,12 @@ interface FlashBagInterface extends SessionBagInterface
public function add($type, $message);
/**
* Registers a message for a given type.
* Registers one or more messages for a given type.
*
* @param string $type
* @param string|array $message
* @param string|array $messages
*/
public function set($type, $message);
public function set($type, $messages);
/**
* Gets flash messages for a given type.

View File

@ -0,0 +1,35 @@
<?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\Ldap\Tests\Adapter\ExtLdap;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Ldap\Adapter\ExtLdap\Connection;
use Symfony\Component\Ldap\Adapter\ExtLdap\EntryManager;
use Symfony\Component\Ldap\Entry;
class EntryManagerTest extends TestCase
{
/**
* @expectedException \Symfony\Component\Ldap\Exception\NotBoundException
* @expectedExceptionMessage Query execution is not possible without binding the connection first.
*/
public function testGetResources()
{
$connection = $this->getMockBuilder(Connection::class)->getMock();
$connection
->expects($this->once())
->method('isBound')->willReturn(false);
$entry = new Entry('$$$$$$');
$entryManager = new EntryManager($connection);
$entryManager->update($entry);
}
}

View File

@ -3,6 +3,11 @@ Mailer Component
The Mailer component helps sending emails.
**This Component is experimental**.
[Experimental features](https://symfony.com/doc/current/contributing/code/experimental.html)
are not covered by Symfony's
[Backward Compatibility Promise](https://symfony.com/doc/current/contributing/code/bc.html).
Resources
---------

View File

@ -115,7 +115,8 @@ class ConnectionTest extends TestCase
public function testGetAfterReject()
{
$connection = Connection::fromDsn('redis://localhost/messenger-rejectthenget');
$redis = new \Redis();
$connection = Connection::fromDsn('redis://localhost/messenger-rejectthenget', [], $redis);
try {
$connection->setup();
} catch (TransportException $e) {
@ -129,5 +130,20 @@ class ConnectionTest extends TestCase
$connection = Connection::fromDsn('redis://localhost/messenger-rejectthenget');
$this->assertNotNull($connection->get());
$redis->del('messenger-rejectthenget');
}
public function testBlockingTimeout()
{
$redis = new \Redis();
$connection = Connection::fromDsn('redis://localhost/messenger-blockingtimeout', ['blocking_timeout' => 1], $redis);
try {
$connection->setup();
} catch (TransportException $e) {
}
$this->assertNull($connection->get());
$redis->del('messenger-blockingtimeout');
}
}

View File

@ -102,7 +102,7 @@ class Connection
return $this->get();
}
foreach ($messages[$this->stream] as $key => $message) {
foreach ($messages[$this->stream] ?? [] as $key => $message) {
$redisEnvelope = \json_decode($message['message'], true);
return [

View File

@ -63,6 +63,7 @@ class PropertyAccessor implements PropertyAccessorInterface
*/
private $cacheItemPool;
private $propertyPathCache = [];
private $readPropertyCache = [];
private $writePropertyCache = [];
private static $resultProto = [self::VALUE => null];
@ -823,7 +824,7 @@ class PropertyAccessor implements PropertyAccessorInterface
*
* @return AdapterInterface
*
* @throws RuntimeException When the Cache Component isn't available
* @throws \LogicException When the Cache Component isn't available
*/
public static function createCache($namespace, $defaultLifetime, $version, LoggerInterface $logger = null)
{

View File

@ -7,6 +7,7 @@ of popular sources.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/property_info.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)

View File

@ -248,7 +248,8 @@ abstract class AnnotationClassLoader implements LoaderInterface
*/
protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method)
{
$name = strtolower(str_replace('\\', '_', $class->name).'_'.$method->name);
$name = str_replace('\\', '_', $class->name).'_'.$method->name;
$name = \function_exists('mb_strtolower') && preg_match('//u', $name) ? mb_strtolower($name, 'UTF-8') : strtolower($name);
if ($this->defaultRouteIndex > 0) {
$name .= '_'.$this->defaultRouteIndex;
}

View File

@ -0,0 +1,10 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
class EncodingClass
{
public function routeÀction()
{
}
}

View File

@ -306,6 +306,34 @@ class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
$this->assertEquals('/nl/suffix', $routes->get('action.nl')->getPath());
}
/**
* @requires function mb_strtolower
*/
public function testDefaultRouteName()
{
$methodRouteData = [
'name' => null,
];
$reader = $this->getReader();
$reader
->expects($this->once())
->method('getMethodAnnotations')
->will($this->returnValue([new RouteAnnotation($methodRouteData)]))
;
$loader = new class($reader) extends AnnotationClassLoader {
protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot)
{
}
};
$routeCollection = $loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\EncodingClass');
$defaultName = array_keys($routeCollection->all())[0];
$this->assertSame($defaultName, 'symfony_component_routing_tests_fixtures_annotatedclasses_encodingclass_routeàction');
}
public function testLoadingRouteWithPrefix()
{
$routes = $this->loader->load(RouteWithPrefixController::class);

View File

@ -29,7 +29,7 @@ class AnnotationDirectoryLoaderTest extends AbstractAnnotationLoaderTest
public function testLoad()
{
$this->reader->expects($this->exactly(3))->method('getClassAnnotation');
$this->reader->expects($this->exactly(4))->method('getClassAnnotation');
$this->reader
->expects($this->any())
@ -52,6 +52,7 @@ class AnnotationDirectoryLoaderTest extends AbstractAnnotationLoaderTest
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass',
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass',
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\FooClass',
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\EncodingClass',
]);
$this->reader

View File

@ -486,7 +486,13 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
throw new RuntimeException(sprintf('Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.', $class, $constructorParameter->name));
}
$params = array_merge($params, $data[$paramName]);
$variadicParameters = [];
foreach ($data[$paramName] as $parameterData) {
$variadicParameters[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
}
$params = array_merge($params, $variadicParameters);
unset($data[$key]);
}
} elseif ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) {
$parameterData = $data[$key];

View File

@ -0,0 +1,27 @@
<?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\Serializer\Tests\Fixtures;
class VariadicConstructorTypedArgsDummy
{
private $foo;
public function __construct(Dummy ...$foo)
{
$this->foo = $foo;
}
public function getFoo()
{
return $this->foo;
}
}

View File

@ -8,10 +8,14 @@ use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractNormalizerDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Dummy;
use Symfony\Component\Serializer\Tests\Fixtures\NullableConstructorArgumentDummy;
use Symfony\Component\Serializer\Tests\Fixtures\StaticConstructorDummy;
use Symfony\Component\Serializer\Tests\Fixtures\StaticConstructorNormalizer;
use Symfony\Component\Serializer\Tests\Fixtures\VariadicConstructorTypedArgsDummy;
/**
* Provides a dummy Normalizer which extends the AbstractNormalizer.
@ -108,9 +112,6 @@ class AbstractNormalizerTest extends TestCase
$this->assertNull($dummy->foo);
}
/**
* @requires PHP 7.1
*/
public function testObjectWithNullableConstructorArgument()
{
$normalizer = new ObjectNormalizer();
@ -118,4 +119,18 @@ class AbstractNormalizerTest extends TestCase
$this->assertNull($dummy->getFoo());
}
public function testObjectWithVariadicConstructorTypedArguments()
{
$normalizer = new PropertyNormalizer();
$normalizer->setSerializer(new Serializer([$normalizer]));
$data = ['foo' => [['foo' => 'Foo', 'bar' => 'Bar', 'baz' => 'Baz', 'qux' => 'Qux'], ['foo' => 'FOO', 'bar' => 'BAR', 'baz' => 'BAZ', 'qux' => 'QUX']]];
$dummy = $normalizer->denormalize($data, VariadicConstructorTypedArgsDummy::class);
$this->assertInstanceOf(VariadicConstructorTypedArgsDummy::class, $dummy);
$this->assertCount(2, $dummy->getFoo());
foreach ($dummy->getFoo() as $foo) {
$this->assertInstanceOf(Dummy::class, $foo);
}
}
}

View File

@ -62,7 +62,7 @@ class AddValidatorInitializersPass implements CompilerPassInterface
}
while (!($class = $translator->getClass()) && $translator instanceof ChildDefinition) {
$translator = $translator->getParent();
$translator = $container->findDefinition($translator->getParent());
}
if (!is_subclass_of($class, LegacyTranslatorInterface::class)) {

View File

@ -20,7 +20,7 @@ use Symfony\Component\Workflow\Marking;
* This store deals with a "single state" or "multiple state" Marking.
*
* "single state" Marking means a subject can be in one and only one state at
* the same time. Use it with state machine or specific workflow.
* the same time. Use it with state machine.
*
* "multiple state" Marking means a subject can be in many states at the same
* time. Use it with workflow.

View File

@ -12,7 +12,7 @@
namespace Symfony\Component\Workflow;
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore;
use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
@ -22,6 +22,6 @@ class StateMachine extends Workflow
{
public function __construct(Definition $definition, MarkingStoreInterface $markingStore = null, EventDispatcherInterface $dispatcher = null, string $name = 'unnamed')
{
parent::__construct($definition, $markingStore ?: new SingleStateMarkingStore(), $dispatcher, $name);
parent::__construct($definition, $markingStore ?: new MethodMarkingStore(true, 'marking'), $dispatcher, $name);
}
}