diff --git a/link b/link index b70c06dda7..2b76a466c3 100755 --- a/link +++ b/link @@ -23,14 +23,12 @@ use Symfony\Component\Filesystem\Filesystem; * @author Kévin Dunglas */ -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); } diff --git a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php index 66b99ecf62..4496d3ac9a 100644 --- a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php +++ b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php @@ -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; + } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php index 2e97edb1d2..b3fb8bc3ac 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php @@ -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')); } } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index 984e2e2a1b..097014c98e 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -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], ]; diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index e91590e574..16d6cac4ba 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -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\\": "" }, diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php index f211e0f52e..2b66d2bea0 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -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__])) { diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php index cee80ac196..3a5904b107 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php @@ -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.', diff --git a/src/Symfony/Component/Cache/Traits/ContractsTrait.php b/src/Symfony/Component/Cache/Traits/ContractsTrait.php index c91c14d90f..d9514d3965 100644 --- a/src/Symfony/Component/Cache/Traits/ContractsTrait.php +++ b/src/Symfony/Component/Cache/Traits/ContractsTrait.php @@ -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); } diff --git a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php index 16cdb6ac0c..940b24d79b 100644 --- a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php +++ b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php @@ -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); diff --git a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php index abc461cd7c..e22933245d 100644 --- a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php @@ -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 = []; diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index 6ea0328a12..2f68dfc21b 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -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": { diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php index 30c5665e60..a0e2f770f0 100644 --- a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php +++ b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -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)) { diff --git a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php index 34316b54c3..3991c0165f 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php +++ b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php @@ -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); diff --git a/src/Symfony/Component/Form/Resources/translations/validators.da.xlf b/src/Symfony/Component/Form/Resources/translations/validators.da.xlf index f52f4e0a30..346e7cf574 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.da.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.da.xlf @@ -14,6 +14,122 @@ The CSRF token is invalid. Please try to resubmit the form. CSRF-token er ugyldig. + + This value is not a valid currency. + Denne værdi er ikke en gyldig valuta. + + + This value should be equal to {{ compared_value }}. + Denne værdi skal være lig med {{ compared_value }}. + + + This value should be greater than {{ compared_value }}. + Denne værdi skal være større end {{ compared_value }}. + + + This value should be greater than or equal to {{ compared_value }}. + Denne værdi skal være større end eller lig med {{ compared_value }}. + + + This value should be identical to {{ compared_value_type }} {{ compared_value }}. + Denne værdi skal være identisk med {{ compared_value_type }} {{ compared_value }}. + + + This value should be less than {{ compared_value }}. + Denne værdi skal være mindre end {{ compared_value }}. + + + This value should be less than or equal to {{ compared_value }}. + Denne værdi skal være mindre end eller lig med {{ compared_value }}. + + + This value should not be equal to {{ compared_value }}. + Denne værdi bør ikke være lig med {{ compared_value }}. + + + This value should not be identical to {{ compared_value_type }} {{ compared_value }}. + Denne værdi bør ikke være identisk med {{ compared_value_type }} {{ compared_value }}. + + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + Billedforholdet er for stort ({{ratio}}). Tilladt maksimumsforhold er {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + Billedforholdet er for lille ({{ ratio }}). Minimumsforventet forventet er {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + Billedet er firkantet ({{ width }} x {{ height }} px). Firkantede billeder er ikke tilladt. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + Billedet er landskabsorienteret ({{width}} x {{height}} px). Landskabsorienterede billeder er ikke tilladt + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + Billedet er portrætorienteret ({{ width }}x{{ height }}px). Portrætorienterede billeder er ikke tilladt. + + + An empty file is not allowed. + En tom fil er ikke tilladt. + + + The host could not be resolved. + Værten kunne ikke løses. + + + This value does not match the expected {{ charset }} charset. + Denne værdi stemmer ikke overens med den forventede {{ charset }} charset. + + + This is not a valid Business Identifier Code (BIC). + Dette er ikke en gyldig Business Identifier Code (BIC).a + + + This is not a valid UUID. + Dette er ikke en gyldig UUID. + + + This value should be a multiple of {{ compared_value }}. + Denne værdi skal være et flertal af {{ compared_value }}. + + + This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. + Denne Business Identifier Code (BIC) er ikke forbundet med IBAN {{ iban }}. + + + This value should be valid JSON. + Denne værdi skal være gyldig JSON. + + + This collection should contain only unique elements. + Denne samling bør kun indeholde unikke elementer. + + + This value should be positive. + Denne værdi skal være positiv. + + + This value should be either positive or zero. + Denne værdi skal være enten positiv eller nul. + + + This value should be negative. + Denne værdi skal være negativ. + + + This value should be either negative or zero. + Denne værdi skal være enten negativ eller nul. + + + This value is not a valid timezone. + Denne værdi er ikke en gyldig tidszone. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Denne adgangskode er blevet lækket i et databrud, det må ikke bruges. Brug venligst en anden adgangskode. + diff --git a/src/Symfony/Component/HttpClient/ScopingHttpClient.php b/src/Symfony/Component/HttpClient/ScopingHttpClient.php index 5b1da579bc..cc5872b3e5 100644 --- a/src/Symfony/Component/HttpClient/ScopingHttpClient.php +++ b/src/Symfony/Component/HttpClient/ScopingHttpClient.php @@ -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 diff --git a/src/Symfony/Component/HttpFoundation/Cookie.php b/src/Symfony/Component/HttpFoundation/Cookie.php index 0ba6037d7c..e6b8b798f2 100644 --- a/src/Symfony/Component/HttpFoundation/Cookie.php +++ b/src/Symfony/Component/HttpFoundation/Cookie.php @@ -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.'); } diff --git a/src/Symfony/Component/HttpFoundation/FileBag.php b/src/Symfony/Component/HttpFoundation/FileBag.php index 14fbc7aa9d..efd83ffeb8 100644 --- a/src/Symfony/Component/HttpFoundation/FileBag.php +++ b/src/Symfony/Component/HttpFoundation/FileBag.php @@ -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); diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php index 2bd1d62bdb..99e8074214 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php +++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php @@ -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. diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/EntryManagerTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/EntryManagerTest.php new file mode 100644 index 0000000000..0617762ed6 --- /dev/null +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/EntryManagerTest.php @@ -0,0 +1,35 @@ + + * + * 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); + } +} diff --git a/src/Symfony/Component/Mailer/README.md b/src/Symfony/Component/Mailer/README.md index 0f70cc30d7..d6fc297909 100644 --- a/src/Symfony/Component/Mailer/README.md +++ b/src/Symfony/Component/Mailer/README.md @@ -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 --------- diff --git a/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php b/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php index fae72fa781..c2654d326d 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php @@ -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'); } } diff --git a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php index 66dc9fe9d6..566e3d6c62 100644 --- a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php @@ -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 [ diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 891cc5e75e..7f8ce1790f 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -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) { diff --git a/src/Symfony/Component/PropertyInfo/README.md b/src/Symfony/Component/PropertyInfo/README.md index 5ac21f384e..1cf30318de 100644 --- a/src/Symfony/Component/PropertyInfo/README.md +++ b/src/Symfony/Component/PropertyInfo/README.md @@ -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) diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index ae834bf4ba..65470664e5 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -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; } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/EncodingClass.php b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/EncodingClass.php new file mode 100644 index 0000000000..dac72e3ddc --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/EncodingClass.php @@ -0,0 +1,10 @@ +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); diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php index ac25493481..9465ef05df 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php @@ -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 diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 44eb42bdf4..cf7e908107 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -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]; diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/VariadicConstructorTypedArgsDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/VariadicConstructorTypedArgsDummy.php new file mode 100644 index 0000000000..9c6fd980a1 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/VariadicConstructorTypedArgsDummy.php @@ -0,0 +1,27 @@ + + * + * 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; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php index 72633cee5f..65da3aa3db 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php @@ -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); + } + } } diff --git a/src/Symfony/Component/Validator/DependencyInjection/AddValidatorInitializersPass.php b/src/Symfony/Component/Validator/DependencyInjection/AddValidatorInitializersPass.php index dcc02ca694..d7bea0fb73 100644 --- a/src/Symfony/Component/Validator/DependencyInjection/AddValidatorInitializersPass.php +++ b/src/Symfony/Component/Validator/DependencyInjection/AddValidatorInitializersPass.php @@ -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)) { diff --git a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php index 3e6294b0fa..a2341aadbd 100644 --- a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php +++ b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php @@ -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. diff --git a/src/Symfony/Component/Workflow/StateMachine.php b/src/Symfony/Component/Workflow/StateMachine.php index c9f88a022b..05f4c291da 100644 --- a/src/Symfony/Component/Workflow/StateMachine.php +++ b/src/Symfony/Component/Workflow/StateMachine.php @@ -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); } }