diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 00a24cbcfc13c..f749de5e0d82a 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
| Q | A
| ------------- | ---
-| Branch? | 7.1 for features / 5.4, 6.4, or 7.0 for bug fixes
+| Branch? | 7.2 for features / 5.4, 6.4, 7.0, and 7.1 for bug fixes
| Bug fix? | yes/no
| New feature? | yes/no
| Deprecations? | yes/no
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 258c37b37d388..30f4bfdd9ade6 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -105,11 +105,12 @@ jobs:
ports:
- 9092:9092
env:
- KAFKA_AUTO_CREATE_TOPICS_ENABLE: false
- KAFKA_CREATE_TOPICS: 'test-topic:1:1:compact'
- KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1
- KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
- KAFKA_ADVERTISED_PORT: 9092
+ KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: false
+ ALLOW_PLAINTEXT_LISTENER: 'yes'
+ KAFKA_CFG_ADVERTISED_LISTENERS: 'PLAINTEXT://127.0.0.1:9092'
+ KAFKA_CFG_LISTENERS: 'PLAINTEXT://:9092'
+ KAFKA_CFG_ZOOKEEPER_CONNECT: 'zookeeper:2181'
+ options: --name=kafka
frankenphp:
image: dunglas/frankenphp:1.1.0
ports:
@@ -127,6 +128,10 @@ jobs:
with:
fetch-depth: 0
+ - name: Init Kafka topics
+ run: |
+ docker exec kafka /opt/bitnami/kafka/bin/kafka-topics.sh --create --topic test-topic --bootstrap-server kafka:9092
+
- name: Install system dependencies
run: |
echo "::group::apt-get update"
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 0753dc03e2789..796590882f30f 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -6,7 +6,7 @@ on:
schedule:
- cron: '34 4 * * 6'
push:
- branches: [ "7.1" ]
+ branches: [ "7.2" ]
# Declare default permissions as read only.
permissions: read-all
diff --git a/CHANGELOG-7.1.md b/CHANGELOG-7.1.md
index 78fc0fb2f693d..3f492b322276e 100644
--- a/CHANGELOG-7.1.md
+++ b/CHANGELOG-7.1.md
@@ -7,6 +7,21 @@ in 7.1 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v7.1.0...v7.1.1
+* 7.1.0 (2024-05-31)
+
+ * bug #57248 [DoctrineBridge] Revert deprecating by-{id} mapping of entities (nicolas-grekas)
+ * bug #54572 [Mailer] Fix sendmail transport failure handling and interactive mode (bobvandevijver)
+ * bug #57228 [Mime] fix PHP 7 compatibility (xabbuh)
+ * bug #57065 [Mime] Fixed `Mime\Message::ensureValidity()` when a required header is set, but has an empty body (rhertogh)
+ * bug #57069 [Config] gracefully handle cases when no resolver is set (xabbuh)
+ * bug #57109 [Notifier] keep boolean options when their value is false (xabbuh)
+ * bug #56848 [DoctrineBridge] Undeprecate DoctrineExtractor::getTypes() (derrabus)
+ * bug #54971 [Serializer] Cache readability/writability computation (mtarld)
+ * bug #54979 [Cache] Fix irrelevant deprecation when connecting to Couchbase (derrabus)
+ * bug #56827 Fix CharsetValidator with string encoding (alamirault)
+ * bug #54977 [DependencyInjection] Fix prepending strategy for php config loader (yceruto)
+ * bug #56488 [VarExporter] Fix exporting default values involving global constants (kylekatarnls)
+
* 7.1.0-RC1 (2024-05-17)
* bug #54970 [DependencyInjection] Process PHP configs using the ContainerConfigurator (MatTheCat)
diff --git a/UPGRADE-7.1.md b/UPGRADE-7.1.md
index 51b5e994b3861..9d516fdbe01f6 100644
--- a/UPGRADE-7.1.md
+++ b/UPGRADE-7.1.md
@@ -30,7 +30,9 @@ Components
* [Form](#Form)
* [Intl](#Intl)
* [HttpClient](#HttpClient)
- * [PropertyInfo](#PropertyInfo)
+ * [HttpKernel](#HttpKernel)
+ * [Security](#Security)
+ * [Serializer](#Serializer)
* [Translation](#Translation)
* [Workflow](#Workflow)
@@ -50,10 +52,47 @@ DependencyInjection
* [BC BREAK] When used in the `prependExtension()` method, the `ContainerConfigurator::import()` method now prepends the configuration instead of appending it
* Deprecate `#[TaggedIterator]` and `#[TaggedLocator]` attributes, use `#[AutowireIterator]` and `#[AutowireLocator]` instead
+ *Before*
+ ```php
+ use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
+ use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;
+
+ class HandlerCollection
+ {
+ public function __construct(
+ #[TaggedIterator('app.handler', indexAttribute: 'key')]
+ iterable $handlers,
+
+ #[TaggedLocator('app.handler')]
+ private ContainerInterface $locator,
+ ) {
+ }
+ }
+ ```
+
+ *After*
+ ```php
+ use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;
+ use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
+
+ class HandlerCollection
+ {
+ public function __construct(
+ #[AutowireIterator('app.handler', indexAttribute: 'key')]
+ iterable $handlers,
+
+ #[AutowireLocator('app.handler')]
+ private ContainerInterface $locator,
+ ) {
+ }
+ }
+ ```
+
DoctrineBridge
--------------
- * Deprecated `DoctrineExtractor::getTypes()`, use `DoctrineExtractor::getType()` instead
+ * Deprecate `DoctrineExtractor::getTypes()`, use `DoctrineExtractor::getType()` instead
+ * Mark class `ProxyCacheWarmer` as `final`
ExpressionLanguage
------------------
@@ -61,6 +100,18 @@ ExpressionLanguage
* Deprecate passing `null` as the allowed variable names to `ExpressionLanguage::lint()` and `Parser::lint()`,
pass the `IGNORE_UNKNOWN_VARIABLES` flag instead to ignore unknown variables during linting
+ *Before*
+ ```php
+ $expressionLanguage->lint('a + 1', null);
+ ```
+
+ *After*
+ ```php
+ use Symfony\Component\ExpressionLanguage\Parser;
+
+ $expressionLanguage->lint('a + 1', [], Parser::IGNORE_UNKNOWN_VARIABLES);
+ ```
+
Form
----
@@ -69,6 +120,7 @@ Form
FrameworkBundle
---------------
+ * [BC BREAK] Enabling `framework.rate_limiter` requires `symfony/rate-limiter` 7.1 or higher
* Mark classes `ConfigBuilderCacheWarmer`, `Router`, `SerializerCacheWarmer`, `TranslationsCacheWarmer`, `Translator` and `ValidatorCacheWarmer` as `final`
* Deprecate the `router.cache_dir` config option, the Router will always use the `kernel.build_dir` parameter
* Reset env vars when resetting the container
@@ -78,6 +130,37 @@ HttpClient
* Deprecate the `setLogger()` methods of the `NoPrivateNetworkHttpClient`, `TraceableHttpClient` and `ScopingHttpClient` classes, configure the logger of the wrapped clients directly instead
+ *Before*
+ ```php
+ // ...
+ use Symfony\Component\HttpClient\HttpClient;
+ use Symfony\Component\HttpClient\NoPrivateNetworkHttpClient;
+
+ $publicClient = new NoPrivateNetworkHttpClient(HttpClient::create());
+ $publicClient->setLogger(new Logger());
+ ```
+
+ *After*
+ ```php
+ // ...
+ use Symfony\Component\HttpClient\HttpClient;
+ use Symfony\Component\HttpClient\NoPrivateNetworkHttpClient;
+
+ $client = HttpClient::create();
+ $client->setLogger(new Logger());
+
+ $publicClient = new NoPrivateNetworkHttpClient($client);
+ ```
+
+HttpKernel
+----------
+
+ * The `Extension` class is marked as internal, extend the `Extension` class from the DependencyInjection component instead
+ * Deprecate `Extension::addAnnotatedClassesToCompile()`
+ * Deprecate `AddAnnotatedClassesToCachePass`
+ * Deprecate the `setAnnotatedClassCache()` and `getAnnotatedClassesToCompile()` methods of the `Kernel` class
+ * Deprecate the `addAnnotatedClassesToCompile()` and `getAnnotatedClassesToCompile()` methods of the `Extension` class
+
Intl
----
@@ -89,15 +172,49 @@ Mailer
* Postmark's "406 - Inactive recipient" API error code now results in a `PostmarkDeliveryEvent` instead of throwing a `HttpTransportException`
-HttpKernel
-----------
+Security
+--------
- * Deprecate `Extension::addAnnotatedClassesToCompile()` and related code infrastructure
+ * Change the first and second argument of `OidcTokenHandler` to `Jose\Component\Core\AlgorithmManager` and `Jose\Component\Core\JWKSet` respectively
SecurityBundle
--------------
* Mark class `ExpressionCacheWarmer` as `final`
+ * Deprecate options `algorithm` and `key` of `oidc` token handler, use
+ `algorithms` and `keyset` instead
+
+ *Before*
+ ```yaml
+ security:
+ firewalls:
+ main:
+ access_token:
+ token_handler:
+ oidc:
+ algorithm: 'ES256'
+ key: '{"kty":"...","k":"..."}'
+ # ...
+ ```
+
+ *After*
+ ```yaml
+ security:
+ firewalls:
+ main:
+ access_token:
+ token_handler:
+ oidc:
+ algorithms: ['ES256']
+ keyset: '{"keys":[{"kty":"...","k":"..."}]}'
+ # ...
+ ```
+ * Deprecate the `security.access_token_handler.oidc.jwk` service, use `security.access_token_handler.oidc.jwkset` instead
+
+Serializer
+----------
+
+ * Deprecate the `withDefaultContructorArguments()` method of `AbstractNormalizerContextBuilder`, use `withDefaultContructorArguments()` instead (note the typo in the old method name)
Translation
-----------
@@ -108,12 +225,13 @@ TwigBundle
----------
* Mark class `TemplateCacheWarmer` as `final`
+ * Deprecate the `base_template_class` config option, this option is no-op when using Twig 3+
Validator
---------
* Deprecate not passing a value for the `requireTld` option to the `Url` constraint (the default value will become `true` in 8.0)
- * Deprecate `Bic::INVALID_BANK_CODE_ERROR`
+ * Deprecate `Bic::INVALID_BANK_CODE_ERROR`, as ISO 9362 defines no restrictions on BIC bank code characters
Workflow
--------
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 594b5fe37de12..e33df0fa31081 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -25,7 +25,7 @@
-
+
diff --git a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php
index d2ee05b42ce1d..5cbf1a088dbb2 100644
--- a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php
+++ b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php
@@ -156,9 +156,8 @@ private function getIdentifier(Request $request, MapEntity $options, ArgumentMet
return $id ?? ($options->stripNull ? false : null);
}
- if ($request->attributes->has('id')) {
- trigger_deprecation('symfony/doctrine-bridge', '7.1', 'Relying on auto-mapping for Doctrine entities is deprecated for argument $%s of "%s": declare the mapping using either the #[MapEntity] attribute or mapped route parameters.', $argument->getName(), method_exists($argument, 'getControllerName') ? $argument->getControllerName() : 'n/a');
+ if ($request->attributes->has('id')) {
return $request->attributes->get('id') ?? ($options->stripNull ? false : null);
}
diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md
index f4ca310235228..4c6e029b5d33c 100644
--- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md
+++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md
@@ -4,7 +4,6 @@ CHANGELOG
7.1
---
- * Deprecate the `DoctrineExtractor::getTypes()` method, use `DoctrineExtractor::getType()` instead
* Allow `EntityValueResolver` to return a list of entities
* Add support for auto-closing idle connections
* Allow validating every class against `UniqueEntity` constraint
diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php
index 02b29ae9942d9..cf32c6c537b02 100644
--- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php
+++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php
@@ -161,13 +161,8 @@ public function getType(string $class, string $property, array $context = []): ?
};
}
- /**
- * @deprecated since Symfony 7.1, use "getType" instead
- */
public function getTypes(string $class, string $property, array $context = []): ?array
{
- trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated, use "%s::getType()" instead.', __METHOD__, self::class);
-
if (null === $metadata = $this->getMetadata($class)) {
return null;
}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php
index c0ab3e1c63783..90cd6b97b9237 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php
@@ -108,8 +108,6 @@ public function testTestGetPropertiesWithEmbedded()
}
/**
- * @group legacy
- *
* @dataProvider legacyTypesProvider
*/
public function testExtractLegacy(string $property, ?array $type = null)
@@ -117,9 +115,6 @@ public function testExtractLegacy(string $property, ?array $type = null)
$this->assertEquals($type, $this->createExtractor()->getTypes(DoctrineDummy::class, $property, []));
}
- /**
- * @group legacy
- */
public function testExtractWithEmbeddedLegacy()
{
$expectedTypes = [new LegacyType(
@@ -137,9 +132,6 @@ public function testExtractWithEmbeddedLegacy()
$this->assertEquals($expectedTypes, $actualTypes);
}
- /**
- * @group legacy
- */
public function testExtractEnumLegacy()
{
$this->assertEquals([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, EnumString::class)], $this->createExtractor()->getTypes(DoctrineEnum::class, 'enumString', []));
@@ -149,9 +141,6 @@ public function testExtractEnumLegacy()
$this->assertNull($this->createExtractor()->getTypes(DoctrineEnum::class, 'enumCustom', []));
}
- /**
- * @group legacy
- */
public static function legacyTypesProvider(): array
{
// DBAL 4 has a special fallback strategy for BINGINT (int -> string)
@@ -251,9 +240,6 @@ public function testGetPropertiesCatchException()
$this->assertNull($this->createExtractor()->getProperties('Not\Exist'));
}
- /**
- * @group legacy
- */
public function testGetTypesCatchExceptionLegacy()
{
$this->assertNull($this->createExtractor()->getTypes('Not\Exist', 'baz'));
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php
index b62720bfd80d8..242affefd58d7 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php
@@ -76,7 +76,7 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
$container->register($config['limiter'] = 'security.login_throttling.'.$firewallName.'.limiter', DefaultLoginRateLimiter::class)
->addArgument(new Reference('limiter.'.$globalId))
->addArgument(new Reference('limiter.'.$localId))
- ->addArgument('%kernel.secret%')
+ ->addArgument('%container.build_hash%')
;
}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SignatureAlgorithmFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SignatureAlgorithmFactory.php
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
index 1d88fbf87ddeb..5d6336eedd73f 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
@@ -118,7 +118,7 @@ public static function createConnection(#[\SensitiveParameter] string $dsn, arra
return MemcachedAdapter::createConnection($dsn, $options);
}
if (str_starts_with($dsn, 'couchbase:')) {
- if (CouchbaseBucketAdapter::isSupported()) {
+ if (class_exists('CouchbaseBucket') && CouchbaseBucketAdapter::isSupported()) {
return CouchbaseBucketAdapter::createConnection($dsn, $options);
}
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseCollectionAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseCollectionAdapterTest.php
index 3ae00e06ae8ed..0e94de2b0d31b 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseCollectionAdapterTest.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseCollectionAdapterTest.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\Cache\Tests\Adapter;
use Psr\Cache\CacheItemPoolInterface;
-use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Cache\Adapter\AbstractAdapter;
use Symfony\Component\Cache\Adapter\CouchbaseCollectionAdapter;
@@ -20,22 +19,18 @@
* @requires extension couchbase <4.0.0
* @requires extension couchbase >=3.0.5
*
- * @group legacy integration
+ * @group integration
*
* @author Antonio Jose Cerezo Aranda
*/
class CouchbaseCollectionAdapterTest extends AdapterTestCase
{
- use ExpectDeprecationTrait;
-
protected $skippedTests = [
'testClearPrefix' => 'Couchbase cannot clear by prefix',
];
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
{
- $this->expectDeprecation('Since symfony/cache 7.1: The "Symfony\Component\Cache\Adapter\CouchbaseBucketAdapter" class is deprecated, use "Symfony\Component\Cache\Adapter\CouchbaseCollectionAdapter" instead.');
-
$client = AbstractAdapter::createConnection('couchbase://'.getenv('COUCHBASE_HOST').'/cache',
['username' => getenv('COUCHBASE_USER'), 'password' => getenv('COUCHBASE_PASS')]
);
diff --git a/src/Symfony/Component/Cache/Traits/RelayProxy.php b/src/Symfony/Component/Cache/Traits/RelayProxy.php
index 90af7a7d48ed4..ac839dfda9ca0 100644
--- a/src/Symfony/Component/Cache/Traits/RelayProxy.php
+++ b/src/Symfony/Component/Cache/Traits/RelayProxy.php
@@ -276,6 +276,11 @@ public function replicaof($host = null, $port = 0): \Relay\Relay|bool
return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->replicaof(...\func_get_args());
}
+ public function waitaof($numlocal, $numremote, $timeout): \Relay\Relay|array|false
+ {
+ return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->waitaof(...\func_get_args());
+ }
+
public function restore($key, $ttl, $value, $options = null): \Relay\Relay|bool
{
return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->restore(...\func_get_args());
diff --git a/src/Symfony/Component/Cache/phpunit.xml.dist b/src/Symfony/Component/Cache/phpunit.xml.dist
index bdccab25ee56b..fb7c080562c45 100644
--- a/src/Symfony/Component/Cache/phpunit.xml.dist
+++ b/src/Symfony/Component/Cache/phpunit.xml.dist
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Symfony/Component/Config/Exception/LogicException.php b/src/Symfony/Component/Config/Exception/LogicException.php
new file mode 100644
index 0000000000000..d227aece4b3b2
--- /dev/null
+++ b/src/Symfony/Component/Config/Exception/LogicException.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Exception;
+
+class LogicException extends \LogicException
+{
+}
diff --git a/src/Symfony/Component/Config/Loader/Loader.php b/src/Symfony/Component/Config/Loader/Loader.php
index dc65ecb528be4..28e5877bf22f0 100644
--- a/src/Symfony/Component/Config/Loader/Loader.php
+++ b/src/Symfony/Component/Config/Loader/Loader.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Config\Loader;
use Symfony\Component\Config\Exception\LoaderLoadException;
+use Symfony\Component\Config\Exception\LogicException;
/**
* Loader is the abstract class used by all built-in loaders.
@@ -20,7 +21,7 @@
*/
abstract class Loader implements LoaderInterface
{
- protected LoaderResolverInterface $resolver;
+ protected ?LoaderResolverInterface $resolver = null;
public function __construct(
protected ?string $env = null,
@@ -29,6 +30,10 @@ public function __construct(
public function getResolver(): LoaderResolverInterface
{
+ if (null === $this->resolver) {
+ throw new LogicException('Cannot get a resolver if none was set.');
+ }
+
return $this->resolver;
}
diff --git a/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php b/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php
index 70bfb8fc15005..263bde26de04a 100644
--- a/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php
+++ b/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Exception\LoaderLoadException;
+use Symfony\Component\Config\Exception\LogicException;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
@@ -29,6 +30,14 @@ public function testGetSetResolver()
$this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader');
}
+ public function testGetResolverWithoutSetResolver()
+ {
+ $this->expectException(LogicException::class);
+
+ $loader = new ProjectLoader1();
+ $loader->getResolver();
+ }
+
public function testResolve()
{
$resolvedLoader = $this->createMock(LoaderInterface::class);
@@ -46,6 +55,14 @@ public function testResolve()
$this->assertSame($resolvedLoader, $loader->resolve('foo.xml'), '->resolve() finds a loader');
}
+ public function testResolveWithoutSetResolver()
+ {
+ $this->expectException(LoaderLoadException::class);
+
+ $loader = new ProjectLoader1();
+ $loader->resolve('foo.xml');
+ }
+
public function testResolveWhenResolverCannotFindLoader()
{
$resolver = $this->createMock(LoaderResolverInterface::class);
diff --git a/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php b/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php
index be492e4fda8f1..637a24e3ba1b6 100644
--- a/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php
+++ b/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php
@@ -20,16 +20,16 @@
class Autoconfigure
{
/**
- * @param array>|null $tags The tags to add to the service
- * @param array>|null $calls The calls to be made when instantiating the service
- * @param array|null $bind The bindings to declare for the service
- * @param bool|string|null $lazy Whether the service is lazy-loaded
- * @param bool|null $public Whether to declare the service as public
- * @param bool|null $shared Whether to declare the service as shared
- * @param bool|null $autowire Whether to declare the service as autowired
- * @param array|null $properties The properties to define when creating the service
- * @param array|string|null $configurator A PHP function, reference or an array containing a class/Reference and a method to call after the service is fully initialized
- * @param string|null $constructor The public static method to use to instantiate the service
+ * @param array>|string[]|null $tags The tags to add to the service
+ * @param array>|null $calls The calls to be made when instantiating the service
+ * @param array|null $bind The bindings to declare for the service
+ * @param bool|string|null $lazy Whether the service is lazy-loaded
+ * @param bool|null $public Whether to declare the service as public
+ * @param bool|null $shared Whether to declare the service as shared
+ * @param bool|null $autowire Whether to declare the service as autowired
+ * @param array|null $properties The properties to define when creating the service
+ * @param array|string|null $configurator A PHP function, reference or an array containing a class/Reference and a method to call after the service is fully initialized
+ * @param string|null $constructor The public static method to use to instantiate the service
*/
public function __construct(
public ?array $tags = null,
diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
index dcdec0ac25788..dcbf1b36f519d 100644
--- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
@@ -880,7 +880,7 @@ public function getAlias(string $id): Alias
/**
* Registers a service definition.
*
- * This methods allows for simple registration of service definition
+ * This method allows for simple registration of service definition
* with a fluid interface.
*/
public function register(string $id, ?string $class = null): Definition
diff --git a/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php
index 35173cf3a031e..f6d032a4a0473 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php
@@ -146,7 +146,7 @@ class_exists(ContainerConfigurator::class);
$callback(...$arguments);
foreach ($configBuilders as $configBuilder) {
- $containerConfigurator->extension($configBuilder->getExtensionAlias(), $configBuilder->toArray(), $this->prepend);
+ $this->loadExtensionConfig($configBuilder->getExtensionAlias(), ContainerConfigurator::processValue($configBuilder->toArray()));
}
$this->loadExtensionConfigs();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/config_builder.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/config_builder.expected.yml
index efe9667c0c7fd..b34e58227de11 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/config_builder.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/config_builder.expected.yml
@@ -1,5 +1,5 @@
parameters:
- acme.configs: [{ color: blue }]
+ acme.configs: [{ color: red }, { color: blue }]
services:
service_container:
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/config_builder.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/config_builder.php
index 02772e64ccb99..4b99622eeff1e 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/config_builder.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/config_builder.php
@@ -1,11 +1,14 @@
import('nested_config_builder.php');
+
$config->color('blue');
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/nested_config_builder.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/nested_config_builder.php
new file mode 100644
index 0000000000000..7b475b200783d
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/nested_config_builder.php
@@ -0,0 +1,11 @@
+color('red');
+};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php
index d3721286cd12a..d7df9b6f11875 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php
@@ -56,6 +56,7 @@ public function testPrependExtensionConfig()
$loader->load('config/config_builder.php');
$expected = [
+ ['color' => 'red'],
['color' => 'blue'],
['foo' => 'bar'],
];
diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php
index 978a068ec6c0b..22112e973a46b 100644
--- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php
+++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php
@@ -101,7 +101,7 @@ public function parse(Expression|string $expression, array $names, int $flags =
public function lint(Expression|string $expression, ?array $names, int $flags = 0): void
{
if (null === $names) {
- trigger_deprecation('symfony/expression-language', '7.1', 'Passing "null" as the second argument of "%s()" is deprecated, pass "self::IGNORE_UNKNOWN_VARIABLES" instead as a third argument.', __METHOD__);
+ trigger_deprecation('symfony/expression-language', '7.1', 'Passing "null" as the second argument of "%s()" is deprecated, pass "%s\Parser::IGNORE_UNKNOWN_VARIABLES" instead as a third argument.', __METHOD__, __NAMESPACE__);
$flags |= Parser::IGNORE_UNKNOWN_VARIABLES;
$names = [];
diff --git a/src/Symfony/Component/ExpressionLanguage/Parser.php b/src/Symfony/Component/ExpressionLanguage/Parser.php
index 1708d18d4e12b..d34e742906dfc 100644
--- a/src/Symfony/Component/ExpressionLanguage/Parser.php
+++ b/src/Symfony/Component/ExpressionLanguage/Parser.php
@@ -112,7 +112,7 @@ public function parse(TokenStream $stream, array $names = [], int $flags = 0): N
public function lint(TokenStream $stream, ?array $names = [], int $flags = 0): void
{
if (null === $names) {
- trigger_deprecation('symfony/expression-language', '7.1', 'Passing "null" as the second argument of "%s()" is deprecated, pass "self::IGNORE_UNKNOWN_VARIABLES" instead as a third argument.', __METHOD__);
+ trigger_deprecation('symfony/expression-language', '7.1', 'Passing "null" as the second argument of "%s()" is deprecated, pass "%s::IGNORE_UNKNOWN_VARIABLES" instead as a third argument.', __METHOD__, __CLASS__);
$flags |= self::IGNORE_UNKNOWN_VARIABLES;
$names = [];
diff --git a/src/Symfony/Component/Form/FormRegistryInterface.php b/src/Symfony/Component/Form/FormRegistryInterface.php
index b1e77898e2234..5c76b5c67843c 100644
--- a/src/Symfony/Component/Form/FormRegistryInterface.php
+++ b/src/Symfony/Component/Form/FormRegistryInterface.php
@@ -21,7 +21,7 @@ interface FormRegistryInterface
/**
* Returns a form type by name.
*
- * This methods registers the type extensions from the form extensions.
+ * This method registers the type extensions from the form extensions.
*
* @throws Exception\InvalidArgumentException if the type cannot be retrieved from any extension
*/
diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php
index 0ebe65f43dc01..22c09a01fc9db 100644
--- a/src/Symfony/Component/HttpFoundation/Response.php
+++ b/src/Symfony/Component/HttpFoundation/Response.php
@@ -777,7 +777,7 @@ public function getMaxAge(): ?int
/**
* Sets the number of seconds after which the response should no longer be considered fresh.
*
- * This methods sets the Cache-Control max-age directive.
+ * This method sets the Cache-Control max-age directive.
*
* @return $this
*
@@ -825,7 +825,7 @@ public function setStaleWhileRevalidate(int $value): static
/**
* Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
*
- * This methods sets the Cache-Control s-maxage directive.
+ * This method sets the Cache-Control s-maxage directive.
*
* @return $this
*
diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md
index 81daf2166f708..eb35d73955e80 100644
--- a/src/Symfony/Component/HttpKernel/CHANGELOG.md
+++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md
@@ -9,7 +9,10 @@ CHANGELOG
* Add `$validationFailedStatusCode` argument to `#[MapQueryParameter]` that allows setting a custom HTTP status code when validation fails
* Add `NearMissValueResolverException` to let value resolvers report when an argument could be under their watch but failed to be resolved
* Add `$type` argument to `#[MapRequestPayload]` that allows mapping a list of items
- * Deprecate `Extension::addAnnotatedClassesToCompile()` and related code infrastructure
+ * The `Extension` class is marked as internal, extend the `Extension` class from the DependencyInjection component instead
+ * Deprecate `Extension::addAnnotatedClassesToCompile()`
+ * Deprecate `AddAnnotatedClassesToCachePass`
+ * Deprecate the `setAnnotatedClassCache()` and `getAnnotatedClassesToCompile()` methods of the `Kernel` class
* Add `#[MapUploadedFile]` attribute to fetch, validate, and inject uploaded files into controller arguments
7.0
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index f50a3b42938de..a3269806200b8 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static array $freshCache = [];
- public const VERSION = '7.1.0-RC1';
+ public const VERSION = '7.1.0';
public const VERSION_ID = 70100;
public const MAJOR_VERSION = 7;
public const MINOR_VERSION = 1;
public const RELEASE_VERSION = 0;
- public const EXTRA_VERSION = 'RC1';
+ public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '01/2025';
public const END_OF_LIFE = '01/2025';
diff --git a/src/Symfony/Component/Mailer/Tests/Transport/Fixtures/fake-failing-sendmail.php b/src/Symfony/Component/Mailer/Tests/Transport/Fixtures/fake-failing-sendmail.php
index 920b980e0f714..1ce987202d3d6 100755
--- a/src/Symfony/Component/Mailer/Tests/Transport/Fixtures/fake-failing-sendmail.php
+++ b/src/Symfony/Component/Mailer/Tests/Transport/Fixtures/fake-failing-sendmail.php
@@ -1,4 +1,8 @@
#!/usr/bin/env php
markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
- }
+ $this->skipOnWindows();
$mail = new Email();
$mail
@@ -68,20 +72,9 @@ public function testToIsUsedWhenRecipientsAreNotSet()
public function testRecipientsAreUsedWhenSet()
{
- if ('\\' === \DIRECTORY_SEPARATOR) {
- $this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
- }
+ $this->skipOnWindows();
- $mail = new Email();
- $mail
- ->from('from@mail.com')
- ->to('to@mail.com')
- ->subject('Subject')
- ->text('Some text')
- ;
-
- $envelope = new DelayedEnvelope($mail);
- $envelope->setRecipients([new Address('recipient@mail.com')]);
+ [$mail, $envelope] = $this->defaultMailAndEnvelope();
$sendmailTransport = new SendmailTransport(self::FAKE_SENDMAIL);
$sendmailTransport->send($mail, $envelope);
@@ -90,11 +83,90 @@ public function testRecipientsAreUsedWhenSet()
}
public function testThrowsTransportExceptionOnFailure()
+ {
+ $this->skipOnWindows();
+
+ [$mail, $envelope] = $this->defaultMailAndEnvelope();
+
+ $sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessage('Process failed with exit code 42: Sending failed');
+ $sendmailTransport->send($mail, $envelope);
+
+ $streamProperty = new \ReflectionProperty(SendmailTransport::class, 'stream');
+ $streamProperty->setAccessible(true);
+ $stream = $streamProperty->getValue($sendmailTransport);
+ $this->assertNull($stream->stream);
+ }
+
+ public function testStreamIsClearedOnFailure()
+ {
+ $this->skipOnWindows();
+
+ [$mail, $envelope] = $this->defaultMailAndEnvelope();
+
+ $sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
+ try {
+ $sendmailTransport->send($mail, $envelope);
+ } catch (TransportException $e) {
+ }
+
+ $streamProperty = new \ReflectionProperty(SendmailTransport::class, 'stream');
+ $streamProperty->setAccessible(true);
+ $stream = $streamProperty->getValue($sendmailTransport);
+ $innerStreamProperty = new \ReflectionProperty(ProcessStream::class, 'stream');
+ $innerStreamProperty->setAccessible(true);
+ $this->assertNull($innerStreamProperty->getValue($stream));
+ }
+
+ public function testDoesNotThrowWhenInteractive()
+ {
+ $this->skipOnWindows();
+
+ [$mail, $envelope] = $this->defaultMailAndEnvelope();
+
+ $sendmailTransport = new SendmailTransport(self::FAKE_INTERACTIVE_SENDMAIL);
+ $transportProperty = new \ReflectionProperty(SendmailTransport::class, 'transport');
+ $transportProperty->setAccessible(true);
+
+ // Replace the transport with an anonymous consumer that trigger the stream methods
+ $transportProperty->setValue($sendmailTransport, new class($transportProperty->getValue($sendmailTransport)->getStream()) implements TransportInterface {
+ private $stream;
+
+ public function __construct(ProcessStream $stream)
+ {
+ $this->stream = $stream;
+ }
+
+ public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
+ {
+ $this->stream->initialize();
+ $this->stream->write('SMTP');
+ $this->stream->terminate();
+
+ return new SentMessage($message, $envelope);
+ }
+
+ public function __toString(): string
+ {
+ return 'Interactive mode test';
+ }
+ });
+
+ $sendmailTransport->send($mail, $envelope);
+
+ $this->assertStringEqualsFile($this->argsPath, __DIR__.'/Fixtures/fake-failing-sendmail.php -bs');
+ }
+
+ private function skipOnWindows()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
}
+ }
+ private function defaultMailAndEnvelope(): array
+ {
$mail = new Email();
$mail
->from('from@mail.com')
@@ -106,9 +178,6 @@ public function testThrowsTransportExceptionOnFailure()
$envelope = new DelayedEnvelope($mail);
$envelope->setRecipients([new Address('recipient@mail.com')]);
- $sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
- $this->expectException(TransportException::class);
- $this->expectExceptionMessage('Process failed with exit code 42: Sending failed');
- $sendmailTransport->send($mail, $envelope);
+ return [$mail, $envelope];
}
}
diff --git a/src/Symfony/Component/Mailer/Transport/SendmailTransport.php b/src/Symfony/Component/Mailer/Transport/SendmailTransport.php
index 36bb93802effb..3add460ebcf89 100644
--- a/src/Symfony/Component/Mailer/Transport/SendmailTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/SendmailTransport.php
@@ -64,6 +64,7 @@ public function __construct(?string $command = null, ?EventDispatcherInterface $
$this->stream = new ProcessStream();
if (str_contains($this->command, ' -bs')) {
$this->stream->setCommand($this->command);
+ $this->stream->setInteractive(true);
$this->transport = new SmtpTransport($this->stream, $dispatcher, $logger);
}
}
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
index 8644d7dad7296..96a6c0fe85b33 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
@@ -24,12 +24,18 @@
final class ProcessStream extends AbstractStream
{
private string $command;
+ private bool $interactive = false;
public function setCommand(string $command): void
{
$this->command = $command;
}
+ public function setInteractive(bool $interactive)
+ {
+ $this->interactive = $interactive;
+ }
+
public function initialize(): void
{
$descriptorSpec = [
@@ -57,11 +63,15 @@ public function terminate(): void
$err = stream_get_contents($this->err);
fclose($this->err);
if (0 !== $exitCode = proc_close($this->stream)) {
- throw new TransportException('Process failed with exit code '.$exitCode.': '.$out.$err);
+ $errorMessage = 'Process failed with exit code '.$exitCode.': '.$out.$err;
}
}
parent::terminate();
+
+ if (!$this->interactive && isset($errorMessage)) {
+ throw new TransportException($errorMessage);
+ }
}
protected function getReadConnectionDescription(): string
diff --git a/src/Symfony/Component/Mime/Message.php b/src/Symfony/Component/Mime/Message.php
index dea746ad42c29..0374ae7a07068 100644
--- a/src/Symfony/Component/Mime/Message.php
+++ b/src/Symfony/Component/Mime/Message.php
@@ -124,11 +124,11 @@ public function toIterable(): iterable
public function ensureValidity(): void
{
- if (!$this->headers->has('To') && !$this->headers->has('Cc') && !$this->headers->has('Bcc')) {
+ if (!$this->headers->get('To')?->getBody() && !$this->headers->get('Cc')?->getBody() && !$this->headers->get('Bcc')?->getBody()) {
throw new LogicException('An email must have a "To", "Cc", or "Bcc" header.');
}
- if (!$this->headers->has('From') && !$this->headers->has('Sender')) {
+ if (!$this->headers->get('From')?->getBody() && !$this->headers->get('Sender')?->getBody()) {
throw new LogicException('An email must have a "From" or a "Sender" header.');
}
diff --git a/src/Symfony/Component/Mime/Tests/MessageTest.php b/src/Symfony/Component/Mime/Tests/MessageTest.php
index 4f7c2a67ce396..1d01fa4519e93 100644
--- a/src/Symfony/Component/Mime/Tests/MessageTest.php
+++ b/src/Symfony/Component/Mime/Tests/MessageTest.php
@@ -276,4 +276,71 @@ public function testSymfonySerialize()
$serialized = $serializer->serialize($e, 'json');
$this->assertStringMatchesFormat($expectedJson, json_encode(json_decode($serialized), \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));
}
+
+ /**
+ * @dataProvider ensureValidityProvider
+ */
+ public function testEnsureValidity(array $headers, ?string $exceptionClass, ?string $exceptionMessage)
+ {
+ if ($exceptionClass) {
+ $this->expectException($exceptionClass);
+ $this->expectExceptionMessage($exceptionMessage);
+ } else {
+ $this->expectNotToPerformAssertions();
+ }
+
+ $m = new Message();
+ foreach ($headers as $headerName => $headerValue) {
+ $m->getHeaders()->addMailboxListHeader($headerName, $headerValue);
+ }
+ $m->ensureValidity();
+ }
+
+ public function ensureValidityProvider()
+ {
+ return [
+ 'Valid address fields' => [
+ [
+ 'To' => ['dummy@symfony.com'],
+ 'From' => ['test@symfony.com'],
+ ],
+ null,
+ null,
+ ],
+
+ 'No destination address fields' => [
+ [
+ 'From' => ['test@symfony.com'],
+ ],
+ LogicException::class,
+ 'An email must have a "To", "Cc", or "Bcc" header.',
+ ],
+
+ 'Empty destination address fields' => [
+ [
+ 'To' => [],
+ 'From' => ['test@symfony.com'],
+ ],
+ LogicException::class,
+ 'An email must have a "To", "Cc", or "Bcc" header.',
+ ],
+
+ 'No originator fields' => [
+ [
+ 'To' => ['dummy@symfony.com'],
+ ],
+ LogicException::class,
+ 'An email must have a "From" or a "Sender" header.',
+ ],
+
+ 'Empty originator fields' => [
+ [
+ 'To' => ['dummy@symfony.com'],
+ 'From' => [],
+ ],
+ LogicException::class,
+ 'An email must have a "From" or a "Sender" header.',
+ ],
+ ];
+ }
}
diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/SlackTransport.php b/src/Symfony/Component/Notifier/Bridge/Slack/SlackTransport.php
index 7b28412434603..18063efadd1cd 100644
--- a/src/Symfony/Component/Notifier/Bridge/Slack/SlackTransport.php
+++ b/src/Symfony/Component/Notifier/Bridge/Slack/SlackTransport.php
@@ -78,7 +78,7 @@ protected function doSend(MessageInterface $message): SlackSentMessage
}
$response = $this->client->request('POST', 'https://'.$this->getEndpoint().'/api/'.$apiMethod, [
- 'json' => array_filter($options),
+ 'json' => array_filter($options, function ($value): bool { return !\in_array($value, ['', [], null], true); }),
'auth_bearer' => $this->accessToken,
'headers' => [
'Content-Type' => 'application/json; charset=utf-8',
diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportTest.php
index 0b9a17fc08493..b0a54d5e0c611 100644
--- a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportTest.php
+++ b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportTest.php
@@ -167,6 +167,56 @@ public function testSendWithNotification()
$this->assertSame('1503435956.000247', $sentMessage->getMessageId());
}
+ /**
+ * @testWith [true]
+ * [false]
+ */
+ public function testSendWithBooleanOptionValue(bool $value)
+ {
+ $channel = 'testChannel';
+ $message = 'testMessage';
+
+ $response = $this->createMock(ResponseInterface::class);
+
+ $response->expects($this->exactly(2))
+ ->method('getStatusCode')
+ ->willReturn(200);
+
+ $response->expects($this->once())
+ ->method('getContent')
+ ->willReturn(json_encode(['ok' => true, 'ts' => '1503435956.000247', 'channel' => 'C123456']));
+
+ $options = new SlackOptions();
+ $options->asUser($value);
+ $options->linkNames($value);
+ $options->mrkdwn($value);
+ $options->unfurlLinks($value);
+ $options->unfurlMedia($value);
+ $notification = new Notification($message);
+ $chatMessage = ChatMessage::fromNotification($notification);
+ $chatMessage->options($options);
+
+ $expectedBody = json_encode([
+ 'as_user' => $value,
+ 'channel' => $channel,
+ 'link_names' => $value,
+ 'mrkdwn' => $value,
+ 'text' => $message,
+ 'unfurl_links' => $value,
+ 'unfurl_media' => $value,
+ ]);
+
+ $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $expectedBody): ResponseInterface {
+ $this->assertJsonStringEqualsJsonString($expectedBody, $options['body']);
+
+ return $response;
+ });
+
+ $transport = self::createTransport($client, $channel);
+
+ $transport->send($chatMessage);
+ }
+
public function testSendWith200ResponseButNotOk()
{
$channel = 'testChannel';
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ConstructorArgumentTypeExtractorInterface.php b/src/Symfony/Component/PropertyInfo/Extractor/ConstructorArgumentTypeExtractorInterface.php
index 17ec478c39555..571b6fa6f014a 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ConstructorArgumentTypeExtractorInterface.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ConstructorArgumentTypeExtractorInterface.php
@@ -28,8 +28,6 @@ interface ConstructorArgumentTypeExtractorInterface
*
* @return LegacyType[]|null
*
- * @deprecated since Symfony 7.1, use "getTypeFromConstructor" instead
- *
* @internal
*/
public function getTypesFromConstructor(string $class, string $property): ?array;
diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php
index 83c7fc3fa801e..38b9c68a2e29e 100644
--- a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php
@@ -64,9 +64,6 @@ public function getType(string $class, string $property, array $context = []): ?
return $this->extract('getType', [$class, $property, $context]);
}
- /**
- * @deprecated since Symfony 7.1, use "getType" instead
- */
public function getTypes(string $class, string $property, array $context = []): ?array
{
return $this->extract('getTypes', [$class, $property, $context]);
diff --git a/src/Symfony/Component/Routing/Exception/LogicException.php b/src/Symfony/Component/Routing/Exception/LogicException.php
new file mode 100644
index 0000000000000..16ed58eefe379
--- /dev/null
+++ b/src/Symfony/Component/Routing/Exception/LogicException.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Exception;
+
+class LogicException extends \LogicException
+{
+}
diff --git a/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php b/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php
index 3b895a9f8006e..8372d90ae9b49 100644
--- a/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php
+++ b/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php
@@ -15,6 +15,7 @@
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Routing\Attribute\Route as RouteAnnotation;
+use Symfony\Component\Routing\Exception\LogicException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@@ -226,6 +227,7 @@ public function setResolver(LoaderResolverInterface $resolver): void
public function getResolver(): LoaderResolverInterface
{
+ throw new LogicException(sprintf('The "%s()" method must not be called.', __METHOD__));
}
/**
diff --git a/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php
index 9b8c5d27397ed..ad65f09c253cc 100644
--- a/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php
+++ b/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Routing\Alias;
+use Symfony\Component\Routing\Exception\LogicException;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\AbstractClassController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\ActionPathController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\BazClass;
@@ -54,6 +55,14 @@ protected function setUp(?string $env = null): void
$this->loader = new TraceableAttributeClassLoader($env);
}
+ public function testGetResolver()
+ {
+ $this->expectException(LogicException::class);
+
+ $loader = new TraceableAttributeClassLoader();
+ $loader->getResolver();
+ }
+
/**
* @dataProvider provideTestSupportsChecksResource
*/
diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf
index 383bfc2cf4f17..25cfb43bdf474 100644
--- a/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf
+++ b/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf
@@ -76,7 +76,7 @@
Too many failed login attempts, please try again in %minutes% minutes.
- Πολλές αποτυχημένες προσπάθειες σύνδεσης, δοκιμάστε ξανά σε %minutes% λεπτά.
+ Πολλές αποτυχημένες προσπάθειες σύνδεσης, δοκιμάστε ξανά σε %minutes% λεπτά.
An authentication exception occurred.
- Terjadi sebuah pengecualian otentikasi.
+ Terjadi kesalahan otentikasi.Authentication credentials could not be found.
@@ -16,7 +16,7 @@
Invalid credentials.
- Kredensial salah.
+ Kredensial tidak valid.Cookie has already been used by someone else.
@@ -28,7 +28,7 @@
Invalid CSRF token.
- Token CSRF salah.
+ Token CSRF tidak valid.No authentication provider found to support the authentication token.
@@ -64,19 +64,19 @@
Too many failed login attempts, please try again later.
- Terlalu banyak percobaan login yang salah, silahkan coba lagi nanti.
+ Terlalu banyak percobaan login yang gagal, silahkan coba lagi nanti.Invalid or expired login link.
- Link login salah atau sudah kedaluwarsa.
+ Link login tidak valid atau sudah kedaluwarsa.Too many failed login attempts, please try again in %minutes% minute.
- Terlalu banyak percobaan login yang salah, silahkan coba lagi dalam %minutes% menit.
+ Terlalu banyak percobaan login yang gagal, silahkan coba lagi dalam %minutes% menit.Too many failed login attempts, please try again in %minutes% minutes.
- Terlalu banyak upaya login yang gagal, silakan coba lagi dalam %minutes% menit.|Terlalu banyak upaya login yang gagal, silakan coba lagi dalam %minutes% menit.
+ Terlalu banyak upaya login yang gagal, silakan coba lagi dalam beberapa %minutes% menit.