Skip to content

Commit c07eba4

Browse files
committed
Merge branch '7.1' into 7.2
* 7.1: [DoctrineBridge] Revert deprecating by-{id} mapping of entities Add missing UPGRADE notes for 7.1 [Mailer] Fix sendmail transport failure handling and interactive mode [Security] reviewed Romanian translation of key 20
2 parents f73a301 + 50d4143 commit c07eba4

File tree

11 files changed

+233
-40
lines changed

11 files changed

+233
-40
lines changed

UPGRADE-7.1.md

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ Components
3030
* [Form](#Form)
3131
* [Intl](#Intl)
3232
* [HttpClient](#HttpClient)
33-
* [PropertyInfo](#PropertyInfo)
33+
* [HttpKernel](#HttpKernel)
34+
* [Security](#Security)
35+
* [Serializer](#Serializer)
3436
* [Translation](#Translation)
3537
* [Workflow](#Workflow)
3638

@@ -50,17 +52,66 @@ DependencyInjection
5052
* [BC BREAK] When used in the `prependExtension()` method, the `ContainerConfigurator::import()` method now prepends the configuration instead of appending it
5153
* Deprecate `#[TaggedIterator]` and `#[TaggedLocator]` attributes, use `#[AutowireIterator]` and `#[AutowireLocator]` instead
5254

55+
*Before*
56+
```php
57+
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
58+
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;
59+
60+
class HandlerCollection
61+
{
62+
public function __construct(
63+
#[TaggedIterator('app.handler', indexAttribute: 'key')]
64+
iterable $handlers,
65+
66+
#[TaggedLocator('app.handler')]
67+
private ContainerInterface $locator,
68+
) {
69+
}
70+
}
71+
```
72+
73+
*After*
74+
```php
75+
use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;
76+
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
77+
78+
class HandlerCollection
79+
{
80+
public function __construct(
81+
#[AutowireIterator('app.handler', indexAttribute: 'key')]
82+
iterable $handlers,
83+
84+
#[AutowireLocator('app.handler')]
85+
private ContainerInterface $locator,
86+
) {
87+
}
88+
}
89+
```
90+
5391
DoctrineBridge
5492
--------------
5593

56-
* Deprecated `DoctrineExtractor::getTypes()`, use `DoctrineExtractor::getType()` instead
94+
* Deprecate `DoctrineExtractor::getTypes()`, use `DoctrineExtractor::getType()` instead
95+
* Mark class `ProxyCacheWarmer` as `final`
5796

5897
ExpressionLanguage
5998
------------------
6099

61100
* Deprecate passing `null` as the allowed variable names to `ExpressionLanguage::lint()` and `Parser::lint()`,
62101
pass the `IGNORE_UNKNOWN_VARIABLES` flag instead to ignore unknown variables during linting
63102

103+
*Before*
104+
```php
105+
$expressionLanguage->lint('a + 1', null);
106+
```
107+
108+
*After*
109+
```php
110+
use Symfony\Component\ExpressionLanguage\Parser;
111+
112+
$expressionLanguage->lint('a + 1', [], Parser::IGNORE_UNKNOWN_VARIABLES);
113+
```
114+
64115
Form
65116
----
66117

@@ -69,6 +120,7 @@ Form
69120
FrameworkBundle
70121
---------------
71122

123+
* [BC BREAK] Enabling `framework.rate_limiter` requires `symfony/rate-limiter` 7.1 or higher
72124
* Mark classes `ConfigBuilderCacheWarmer`, `Router`, `SerializerCacheWarmer`, `TranslationsCacheWarmer`, `Translator` and `ValidatorCacheWarmer` as `final`
73125
* Deprecate the `router.cache_dir` config option, the Router will always use the `kernel.build_dir` parameter
74126
* Reset env vars when resetting the container
@@ -78,6 +130,37 @@ HttpClient
78130

79131
* Deprecate the `setLogger()` methods of the `NoPrivateNetworkHttpClient`, `TraceableHttpClient` and `ScopingHttpClient` classes, configure the logger of the wrapped clients directly instead
80132

133+
*Before*
134+
```php
135+
// ...
136+
use Symfony\Component\HttpClient\HttpClient;
137+
use Symfony\Component\HttpClient\NoPrivateNetworkHttpClient;
138+
139+
$publicClient = new NoPrivateNetworkHttpClient(HttpClient::create());
140+
$publicClient->setLogger(new Logger());
141+
```
142+
143+
*After*
144+
```php
145+
// ...
146+
use Symfony\Component\HttpClient\HttpClient;
147+
use Symfony\Component\HttpClient\NoPrivateNetworkHttpClient;
148+
149+
$client = HttpClient::create();
150+
$client->setLogger(new Logger());
151+
152+
$publicClient = new NoPrivateNetworkHttpClient($client);
153+
```
154+
155+
HttpKernel
156+
----------
157+
158+
* The `Extension` class is marked as internal, extend the `Extension` class from the DependencyInjection component instead
159+
* Deprecate `Extension::addAnnotatedClassesToCompile()`
160+
* Deprecate `AddAnnotatedClassesToCachePass`
161+
* Deprecate the `setAnnotatedClassCache()` and `getAnnotatedClassesToCompile()` methods of the `Kernel` class
162+
* Deprecate the `addAnnotatedClassesToCompile()` and `getAnnotatedClassesToCompile()` methods of the `Extension` class
163+
81164
Intl
82165
----
83166

@@ -89,18 +172,49 @@ Mailer
89172

90173
* Postmark's "406 - Inactive recipient" API error code now results in a `PostmarkDeliveryEvent` instead of throwing a `HttpTransportException`
91174

92-
HttpKernel
93-
----------
175+
Security
176+
--------
94177

95-
* The `Extension` class is marked as internal, extend the `Extension` class from the DependencyInjection component instead
96-
* Deprecate `Extension::addAnnotatedClassesToCompile()`
97-
* Deprecate `AddAnnotatedClassesToCachePass`
98-
* Deprecate the `setAnnotatedClassCache()` and `getAnnotatedClassesToCompile()` methods of the `Kernel` class
178+
* Change the first and second argument of `OidcTokenHandler` to `Jose\Component\Core\AlgorithmManager` and `Jose\Component\Core\JWKSet` respectively
99179

100180
SecurityBundle
101181
--------------
102182

103183
* Mark class `ExpressionCacheWarmer` as `final`
184+
* Deprecate options `algorithm` and `key` of `oidc` token handler, use
185+
`algorithms` and `keyset` instead
186+
187+
*Before*
188+
```yaml
189+
security:
190+
firewalls:
191+
main:
192+
access_token:
193+
token_handler:
194+
oidc:
195+
algorithm: 'ES256'
196+
key: '{"kty":"...","k":"..."}'
197+
# ...
198+
```
199+
200+
*After*
201+
```yaml
202+
security:
203+
firewalls:
204+
main:
205+
access_token:
206+
token_handler:
207+
oidc:
208+
algorithms: ['ES256']
209+
keyset: '{"keys":[{"kty":"...","k":"..."}]}'
210+
# ...
211+
```
212+
* Deprecate the `security.access_token_handler.oidc.jwk` service, use `security.access_token_handler.oidc.jwkset` instead
213+
214+
Serializer
215+
----------
216+
217+
* Deprecate the `withDefaultContructorArguments()` method of `AbstractNormalizerContextBuilder`, use `withDefaultContructorArguments()` instead (note the typo in the old method name)
104218

105219
Translation
106220
-----------
@@ -111,12 +225,13 @@ TwigBundle
111225
----------
112226

113227
* Mark class `TemplateCacheWarmer` as `final`
228+
* Deprecate the `base_template_class` config option, this option is no-op when using Twig 3+
114229

115230
Validator
116231
---------
117232

118233
* Deprecate not passing a value for the `requireTld` option to the `Url` constraint (the default value will become `true` in 8.0)
119-
* Deprecate `Bic::INVALID_BANK_CODE_ERROR`
234+
* Deprecate `Bic::INVALID_BANK_CODE_ERROR`, as ISO 9362 defines no restrictions on BIC bank code characters
120235

121236
Workflow
122237
--------

src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,8 @@ private function getIdentifier(Request $request, MapEntity $options, ArgumentMet
156156

157157
return $id ?? ($options->stripNull ? false : null);
158158
}
159-
if ($request->attributes->has('id')) {
160-
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');
161159

160+
if ($request->attributes->has('id')) {
162161
return $request->attributes->get('id') ?? ($options->stripNull ? false : null);
163162
}
164163

src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public function parse(Expression|string $expression, array $names, int $flags =
101101
public function lint(Expression|string $expression, ?array $names, int $flags = 0): void
102102
{
103103
if (null === $names) {
104-
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__);
104+
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__);
105105

106106
$flags |= Parser::IGNORE_UNKNOWN_VARIABLES;
107107
$names = [];

src/Symfony/Component/ExpressionLanguage/Parser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public function parse(TokenStream $stream, array $names = [], int $flags = 0): N
112112
public function lint(TokenStream $stream, ?array $names = [], int $flags = 0): void
113113
{
114114
if (null === $names) {
115-
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__);
115+
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__);
116116

117117
$flags |= self::IGNORE_UNKNOWN_VARIABLES;
118118
$names = [];
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
#!/usr/bin/env php
22
<?php
3+
$argsPath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'sendmail_args';
4+
5+
file_put_contents($argsPath, implode(' ', $argv));
6+
37
print "Sending failed";
48
exit(42);

src/Symfony/Component/Mailer/Tests/Transport/SendmailTransportTest.php

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,21 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Mailer\DelayedEnvelope;
16+
use Symfony\Component\Mailer\Envelope;
1617
use Symfony\Component\Mailer\Exception\TransportException;
18+
use Symfony\Component\Mailer\SentMessage;
1719
use Symfony\Component\Mailer\Transport\SendmailTransport;
20+
use Symfony\Component\Mailer\Transport\Smtp\Stream\ProcessStream;
21+
use Symfony\Component\Mailer\Transport\TransportInterface;
1822
use Symfony\Component\Mime\Address;
1923
use Symfony\Component\Mime\Email;
24+
use Symfony\Component\Mime\RawMessage;
2025

2126
class SendmailTransportTest extends TestCase
2227
{
2328
private const FAKE_SENDMAIL = __DIR__.'/Fixtures/fake-sendmail.php -t';
2429
private const FAKE_FAILING_SENDMAIL = __DIR__.'/Fixtures/fake-failing-sendmail.php -t';
30+
private const FAKE_INTERACTIVE_SENDMAIL = __DIR__.'/Fixtures/fake-failing-sendmail.php -bs';
2531

2632
private string $argsPath;
2733

@@ -46,9 +52,7 @@ public function testToString()
4652

4753
public function testToIsUsedWhenRecipientsAreNotSet()
4854
{
49-
if ('\\' === \DIRECTORY_SEPARATOR) {
50-
$this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
51-
}
55+
$this->skipOnWindows();
5256

5357
$mail = new Email();
5458
$mail
@@ -68,20 +72,9 @@ public function testToIsUsedWhenRecipientsAreNotSet()
6872

6973
public function testRecipientsAreUsedWhenSet()
7074
{
71-
if ('\\' === \DIRECTORY_SEPARATOR) {
72-
$this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
73-
}
75+
$this->skipOnWindows();
7476

75-
$mail = new Email();
76-
$mail
77-
->from('from@mail.com')
78-
->to('to@mail.com')
79-
->subject('Subject')
80-
->text('Some text')
81-
;
82-
83-
$envelope = new DelayedEnvelope($mail);
84-
$envelope->setRecipients([new Address('recipient@mail.com')]);
77+
[$mail, $envelope] = $this->defaultMailAndEnvelope();
8578

8679
$sendmailTransport = new SendmailTransport(self::FAKE_SENDMAIL);
8780
$sendmailTransport->send($mail, $envelope);
@@ -90,11 +83,90 @@ public function testRecipientsAreUsedWhenSet()
9083
}
9184

9285
public function testThrowsTransportExceptionOnFailure()
86+
{
87+
$this->skipOnWindows();
88+
89+
[$mail, $envelope] = $this->defaultMailAndEnvelope();
90+
91+
$sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
92+
$this->expectException(TransportException::class);
93+
$this->expectExceptionMessage('Process failed with exit code 42: Sending failed');
94+
$sendmailTransport->send($mail, $envelope);
95+
96+
$streamProperty = new \ReflectionProperty(SendmailTransport::class, 'stream');
97+
$streamProperty->setAccessible(true);
98+
$stream = $streamProperty->getValue($sendmailTransport);
99+
$this->assertNull($stream->stream);
100+
}
101+
102+
public function testStreamIsClearedOnFailure()
103+
{
104+
$this->skipOnWindows();
105+
106+
[$mail, $envelope] = $this->defaultMailAndEnvelope();
107+
108+
$sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
109+
try {
110+
$sendmailTransport->send($mail, $envelope);
111+
} catch (TransportException $e) {
112+
}
113+
114+
$streamProperty = new \ReflectionProperty(SendmailTransport::class, 'stream');
115+
$streamProperty->setAccessible(true);
116+
$stream = $streamProperty->getValue($sendmailTransport);
117+
$innerStreamProperty = new \ReflectionProperty(ProcessStream::class, 'stream');
118+
$innerStreamProperty->setAccessible(true);
119+
$this->assertNull($innerStreamProperty->getValue($stream));
120+
}
121+
122+
public function testDoesNotThrowWhenInteractive()
123+
{
124+
$this->skipOnWindows();
125+
126+
[$mail, $envelope] = $this->defaultMailAndEnvelope();
127+
128+
$sendmailTransport = new SendmailTransport(self::FAKE_INTERACTIVE_SENDMAIL);
129+
$transportProperty = new \ReflectionProperty(SendmailTransport::class, 'transport');
130+
$transportProperty->setAccessible(true);
131+
132+
// Replace the transport with an anonymous consumer that trigger the stream methods
133+
$transportProperty->setValue($sendmailTransport, new class($transportProperty->getValue($sendmailTransport)->getStream()) implements TransportInterface {
134+
private $stream;
135+
136+
public function __construct(ProcessStream $stream)
137+
{
138+
$this->stream = $stream;
139+
}
140+
141+
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
142+
{
143+
$this->stream->initialize();
144+
$this->stream->write('SMTP');
145+
$this->stream->terminate();
146+
147+
return new SentMessage($message, $envelope);
148+
}
149+
150+
public function __toString(): string
151+
{
152+
return 'Interactive mode test';
153+
}
154+
});
155+
156+
$sendmailTransport->send($mail, $envelope);
157+
158+
$this->assertStringEqualsFile($this->argsPath, __DIR__.'/Fixtures/fake-failing-sendmail.php -bs');
159+
}
160+
161+
private function skipOnWindows()
93162
{
94163
if ('\\' === \DIRECTORY_SEPARATOR) {
95164
$this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
96165
}
166+
}
97167

168+
private function defaultMailAndEnvelope(): array
169+
{
98170
$mail = new Email();
99171
$mail
100172
->from('from@mail.com')
@@ -106,9 +178,6 @@ public function testThrowsTransportExceptionOnFailure()
106178
$envelope = new DelayedEnvelope($mail);
107179
$envelope->setRecipients([new Address('recipient@mail.com')]);
108180

109-
$sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
110-
$this->expectException(TransportException::class);
111-
$this->expectExceptionMessage('Process failed with exit code 42: Sending failed');
112-
$sendmailTransport->send($mail, $envelope);
181+
return [$mail, $envelope];
113182
}
114183
}

src/Symfony/Component/Mailer/Transport/SendmailTransport.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public function __construct(?string $command = null, ?EventDispatcherInterface $
6464
$this->stream = new ProcessStream();
6565
if (str_contains($this->command, ' -bs')) {
6666
$this->stream->setCommand($this->command);
67+
$this->stream->setInteractive(true);
6768
$this->transport = new SmtpTransport($this->stream, $dispatcher, $logger);
6869
}
6970
}

0 commit comments

Comments
 (0)