10000 Merge branch '5.4' into 6.4 · symfony/symfony@9344c26 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9344c26

Browse files
Merge branch '5.4' into 6.4
* 5.4: [VarDumper] Fix test suite with PHP 8.4 [DoctrineBridge] Add missing return type [Mailer] Fix sendmail transport not handling failure [HttpClient] Lazily initialize CurlClientState updating missing translations for Greek #53768 [Validator] Allow BICs’ first four characters to be digits [ErrorHandler] Fix exit code when an exception occurs and the exception handler has been unregistered [Validator] Review Arabic translations and add correct translations.
2 parents 34f84cf + 9d362b1 commit 9344c26

File tree

13 files changed

+125
-67
lines changed

13 files changed

+125
-67
lines changed

src/Symfony/Component/ErrorHandler/ErrorHandler.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,10 @@ public static function handleFatalError(?array $error = null): void
595595
set_exception_handler($h);
596596
}
597597
if (!$handler) {
598+
if (null === $error && $exitCode = self::$exitCode) {
599+
register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
600+
}
601+
598602
return;
599603
}
600604
if ($handler !== $h) {
@@ -630,8 +634,7 @@ public static function handleFatalError(?array $error = null): void
630634
// Ignore this re-throw
631635
}
632636

633-
if ($exit && self::$exitCode) {
634-
$exitCode = self::$exitCode;
637+
if ($exit && $exitCode = self::$exitCode) {
635638
register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
636639
}
637640
}

src/Symfony/Component/HttpClient/CurlHttpClient.php

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
5151

5252
private ?LoggerInterface $logger = null;
5353

54+
private int $maxHostConnections;
55+
private int $maxPendingPushes;
56+
5457
/**
5558
* An internal object to share state between the client and its responses.
5659
*/
@@ -69,25 +72,31 @@ public function __construct(array $defaultOptions = [], int $maxHostConnections
6972
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\CurlHttpClient" as the "curl" extension is not installed.');
7073
}
7174

75+
$this->maxHostConnections = $maxHostConnections;
76+
$this->maxPendingPushes = $maxPendingPushes;
77+
7278
$this->defaultOptions['buffer'] ??= self::shouldBuffer(...);
7379

7480
if ($defaultOptions) {
7581
[, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
7682
}
77-
78-
$this->multi = new CurlClientState($maxHostConnections, $maxPendingPushes);
7983
}
8084

8185
public function setLogger(LoggerInterface $logger): void
8286
{
83-
$this->logger = $this->multi->logger = $logger;
87+
$this->logger = $logger;
88+
if (isset($this->multi)) {
89+
$this->multi->logger = $logger;
90+
}
8491
}
8592

8693
/**
8794
* @see HttpClientInterface::OPTIONS_DEFAULTS for available options
8895
*/
8996
public function request(string $method, string $url, array $options = []): ResponseInterface
9097
{
98+
$multi = $this->ensureState();
99+
91100
[$url, $options] = self::prepareRequest($method, $url, $options, $this->defaultOptions);
92101
$scheme = $url['scheme'];
93102
$authority = $url['authority'];
@@ -165,24 +174,24 @@ public function request(string $method, string $url, array $options = []): Respo
165174
}
166175

167176
// curl's resolve feature varies by host:port but ours varies by host only, let's handle this with our own DNS map
168-
if (isset($this->multi->dnsCache->hostnames[$host])) {
169-
$options['resolve'] += [$host => $this->multi->dnsCache->hostnames[$host]];
177+
if (isset($multi->dnsCache->hostnames[$host])) {
178+
$options['resolve'] += [$host => $multi->dnsCache->hostnames[$host]];
170179
}
171180

172-
if ($options['resolve'] || $this->multi->dnsCache->evictions) {
181+
if ($options['resolve'] || $multi->dnsCache->evictions) {
173182
// First reset any old DNS cache entries then add the new ones
174-
$resolve = $this->multi->dnsCache->evictions;
175-
$this->multi->dnsCache->evictions = [];
183+
$resolve = $multi->dnsCache->evictions;
184+
$multi->dnsCache->evictions = [];
176185

177186
if ($resolve && 0x072A00 > CurlClientState::$curlVersion['version_number']) {
178187
// DNS cache removals require curl 7.42 or higher
179-
$this->multi->reset();
188+
$multi->reset();
180189
}
181190

182191
foreach ($options['resolve'] as $host => $ip) {
183192
$resolve[] = null === $ip ? "-$host:$port" : "$host:$port:$ip";
184-
$this->multi->dnsCache->hostnames[$host] = $ip;
185-
$this->multi->dnsCache->removals["-$host:$port"] = "-$host:$port";
193+
$multi->dnsCache->hostnames[$host] = $ip;
194+
$multi->dnsCache->removals["-$host:$port"] = "-$host:$port";
186195
}
187196

188197
$curlopts[\CURLOPT_RESOLVE] = $resolve;
@@ -285,16 +294,16 @@ public function request(string $method, string $url, array $options = []): Respo
285294
$curlopts += $options['extra']['curl'];
286295
}
287296

288-
if ($pushedResponse = $this->multi->pushedResponses[$url] ?? null) {
289-
unset($this->multi->pushedResponses[$url]);
297+
if ($pushedResponse = $multi->pushedResponses[$url] ?? null) {
298+
unset($multi->pushedResponses[$url]);
290299

291300
if (self::acceptPushForRequest($method, $options, $pushedResponse)) {
292301
$this->logger?->debug(sprintf('Accepting pushed response: "%s %s"', $method, $url));
293302

294303
// Reinitialize the pushed response with request's options
295304
$ch = $pushedResponse->handle;
296305
$pushedResponse = $pushedResponse->response;
297-
$pushedResponse->__construct($this->multi, $url, $options, $this->logger);
306+
$pushedResponse->__construct($multi, $url, $options, $this->logger);
298307
} else {
299308
$this->logger?->debug(sprintf('Rejecting pushed response: "%s"', $url));
300309
$pushedResponse = null;
@@ -304,7 +313,7 @@ public function request(string $method, string $url, array $options = []): Respo
304313
if (!$pushedResponse) {
305314
$ch = curl_init();
306315
$this->logger?->info(sprintf('Request: "%s %s"', $method, $url));
307-
$curlopts += [\CURLOPT_SHARE => $this->multi->share];
316+
$curlopts += [\CURLOPT_SHARE => $multi->share];
308317
}
309318

310319
foreach ($curlopts as $opt => $value) {
@@ -314,7 +323,7 @@ public function request(string $method, string $url, array $options = []): Respo
314323
}
315324
}
316325

317-
return $pushedResponse ?? new CurlResponse($this->multi, $ch, $options, $this->logger, $method, self::createRedirectResolver($options, $host, $port), CurlClientState::$curlVersion['version_number'], $url);
326+
return $pushedResponse ?? new CurlResponse($multi, $ch, $options, $this->logger, $method, self::createRedirectResolver($options, $host, $port), CurlClientState::$curlVersion['version_number'], $url);
318327
}
319328

320329
public function stream(ResponseInterface|iterable $responses, ?float $timeout = null): ResponseStreamInterface
@@ -323,9 +332,11 @@ public function stream(ResponseInterface|iterable $responses, ?float $timeout =
323332
$responses = [$responses];
324333
}
325334

326-
if ($this->multi->handle instanceof \CurlMultiHandle) {
335+
$multi = $this->ensureState();
336+
337+
if ($multi->handle instanceof \CurlMultiHandle) {
327338
$active = 0;
328-
while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($this->multi->handle, $active)) {
339+
while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($multi->handle, $active)) {
329340
}
330341
}
331342

@@ -334,7 +345,9 @@ public function stream(ResponseInterface|iterable $responses, ?float $timeout =
334345

335346
public function reset(): void
336347
{
337-
$this->multi->reset();
348+
if (isset($this->multi)) {
349+
$this->multi->reset();
350+
}
338351
}
339352

340353
/**
@@ -434,6 +447,16 @@ private static function createRedirectResolver(array $options, string $host, int
434447
};
435448
}
436449

450+
private function ensureState(): CurlClientState
451+
{
452+
if (!isset($this->multi)) {
453+
$this->multi = new CurlClientState($this->maxHostConnections, $this->maxPendingPushes);
454+
$this->multi->logger = $this->logger;
455+
}
456+
457+
return $this->multi;
458+
}
459+
437460
private function findConstantName(int $opt): ?string
438461
{
439462
$constants = array_filter(get_defined_constants(), static fn ($v, $k) => $v === $opt && 'C' === $k[0] && (str_starts_with($k, 'CURLOPT_') || str_starts_with($k, 'CURLINFO_')), \ARRAY_FILTER_USE_BOTH);

src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ public function testHandleIsReinitOnReset()
5858
{
5959
$httpClient = $this->getHttpClient(__FUNCTION__);
6060

61-
$r = new \ReflectionProperty($httpClient, 'multi');
62-
$clientState = $r->getValue($httpClient);
61+
$r = new \ReflectionMethod($httpClient, 'ensureState');
62+
$clientState = $r->invoke($httpClient);
6363
$initialShareId = $clientState->share;
6464
$httpClient->reset();
6565
self::assertNotSame($initialShareId, $clientState->share);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env php
2+
<?php
3+
print "Sending failed";
4+
exit(42);

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Mailer\DelayedEnvelope;
16+
use Symfony\Component\Mailer\Exception\TransportException;
1617
use Symfony\Component\Mailer\Transport\SendmailTransport;
1718
use Symfony\Component\Mime\Address;
1819
use Symfony\Component\Mime\Email;
1920

2021
class SendmailTransportTest extends TestCase
2122
{
2223
private const FAKE_SENDMAIL = __DIR__.'/Fixtures/fake-sendmail.php -t';
24+
private const FAKE_FAILING_SENDMAIL = __DIR__.'/Fixtures/fake-failing-sendmail.php -t';
2325

2426
private string $argsPath;
2527

@@ -86,4 +88,27 @@ public function testRecipientsAreUsedWhenSet()
8688

8789
$this->assertStringEqualsFile($this->argsPath, __DIR__.'/Fixtures/fake-sendmail.php -ffrom@mail.com recipient@mail.com');
8890
}
91+
92+
public function testThrowsTransportExceptionOnFailure()
93+
{
94+
if ('\\' === \DIRECTORY_SEPARATOR) {
95+
$this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
96+
}
97+
98+
$mail = new Email();
99+
$mail
100+
->from('from@mail.com')
101+
->to('to@mail.com')
102+
->subject('Subject')
103+
->text('Some text')
104+
;
105+
106+
$envelope = new DelayedEnvelope($mail);
107+
$envelope->setRecipients([new Address('recipient@mail.com')]);
108+
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);
113+
}
89114
}

src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ abstract class AbstractStream
3030
protected $in;
3131
/** @var resource|null */
3232
protected $out;
33+
protected $err;
3334

3435
private string $debug = '';
3536

@@ -68,7 +69,7 @@ abstract public function initialize(): void;
6869

6970
public function terminate(): void
7071
{
71-
$this->stream = $this->out = $this->in = null;
72+
$this->stream = $this->err = $this->out = $this->in = null;
7273
}
7374

7475
public function readLine(): string

src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,20 @@ public function initialize(): void
4545
}
4646
$this->in = &$pipes[0];
4747
$this->out = &$pipes[1];
48+
$this->err = &$pipes[2];
4849
}
4950

5051
public function terminate(): void
5152
{
5253
if (null !== $this->stream) {
5354
fclose($this->in);
55+
$out = stream_get_contents($this->out);
5456
fclose($this->out);
55-
proc_close($this->stream);
57+
$err = stream_get_contents($this->err);
58+
fclose($this->err);
59+
if (0 !== $exitCode = proc_close($this->stream)) {
60+
throw new TransportException('Process failed with exit code '.$exitCode.': '.$out.$err);
61+
}
5662
}
5763

5864
parent::terminate();

src/Symfony/Component/Validator/Constraints/BicValidator.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,6 @@ public function validate(mixed $value, Constraint $constraint)
102102
return;
103103
}
104104

105-
// first 4 letters must be alphabetic (bank code)
106-
if (!ctype_alpha(substr($canonicalize, 0, 4))) {
107-
$this->context->buildViolation($constraint->message)
108-
->setParameter('{{ value }}', $this->formatValue($value))
109-
->setCode(Bic::INVALID_BANK_CODE_ERROR)
110-
->addViolation();
111-
112-
return;
113-
}
114-
115105
$bicCountryCode = substr($canonicalize, 4, 2);
116106
if (!isset(self::BIC_COUNTRY_TO_IBAN_COUNTRY_MAP[$bicCountryCode]) && !Countries::exists($bicCountryCode)) {
117107
$this->context->buildViolation($constraint->message)

src/Symfony/Component/Validator/Resources/translations/validators.ar.xlf

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136
</trans-unit>
137137
<trans-unit id="37" resname="This is not a valid IP address.">
138138
<source>This value is not a valid IP address.</source>
139-
<target state="needs-review-translation">هذه القيمة ليست عنوان IP صالحًا.</target>
139+
<target>هذا ليس عنوان IP صحيح.</target>
140140
</trans-unit>
141141
<trans-unit id="38">
142142
<source>This value is not a valid language.</source>
@@ -192,7 +192,7 @@
192192
</trans-unit>
193193
<trans-unit id="51" resname="No temporary folder was configured in php.ini.">
194194
<source>No temporary folder was configured in php.ini, or the configured folder does not exist.</source>
195-
<target state="needs-review-translation">لم يتم تكوين مجلد مؤقت في ملف php.ini، أو المجلد المعد لا يوجد.</target>
195+
<target>لم يتم تكوين مجلد مؤقت في ملف php.ini.</target>
196196
</trans-unit>
197197
<trans-unit id="52">
198198
<source>Cannot write temporary file to disk.</source>
@@ -224,7 +224,7 @@
224224
</trans-unit>
225225
<trans-unit id="59" resname="This is not a valid International Bank Account Number (IBAN).">
226226
<source>This value is not a valid International Bank Account Number (IBAN).</source>
227-
<target state="needs-review-translation">هذه القيمة ليست رقم حساب بنكي دولي (IBAN) صالحًا.</target>
227+
<target>هذه القيمة ليست رقم حساب بنكي دولي (IBAN) صالحًا.</target>
228228
</trans-unit>
229229
<trans-unit id="60">
230230
<source>This value is not a valid ISBN-10.</source>
@@ -312,15 +312,15 @@
312312
</trans-unit>
313313
<trans-unit id="81" resname="This is not a valid Business Identifier Code (BIC).">
314314
<source>This value is not a valid Business Identifier Code (BIC).</source>
315-
<target state="needs-review-translation">هذه القيمة ليست رمز معرف الأعمال (BIC) صالحًا.</target>
315+
<target>هذه القيمة ليست رمز معرف أعمال (BIC) صالحًا.</target>
316316
</trans-unit>
317317
<trans-unit id="82">
318318
<source>Error</source>
319319
<target>خطأ</target>
320320
</trans-unit>
321321
<trans-unit id="83" resname="This is not a valid UUID.">
322322
<source>This value is not a valid UUID.</source>
323-
<target state="needs-review-translation">هذه القيمة ليست UUID صالحًا.</target>
323+
<target>هذه القيمة ليست UUID صالحًا.</target>
324324
</trans-unit>
325325
<trans-unit id="84">
326326
<source>This value should be a multiple of {{ compared_value }}.</source>
@@ -432,11 +432,11 @@
432432
</trans-unit>
433433
<trans-unit id="111">
434434
<source>The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.</source>
435-
<target state="needs-review-translation">تم اكتشاف ترميز الأحرف غير صالح ({{ detected }}). الترميزات المسموح بها هي {{ encodings }}.</target>
435+
<target>تم اكتشاف ترميز أحرف غير صالح ({{ detected }}). الترميزات المسموح بها هي {{ encodings }}.</target>
436436
</trans-unit>
437437
<trans-unit id="112">
438438
<source>This value is not a valid MAC address.</source>
439-
<target state="needs-review-translation">هذه القيمة ليست عنوان MAC صالحًا.</target>
439+
<target>هذه القيمة ليست عنوان MAC صالحًا.</target>
440440
</trans-unit>
441441
</body>
442442
</file>

0 commit comments

Comments
 (0)
0