Skip to content

Commit ba614ef

Browse files
committed
Merge branch '7.0' into 7.1
* 7.0: [VarExporter] Uniform unitialized property error message under ghost and non-ghost objects [AssetMapper] Ignore comment lines in JavaScriptImportPathCompiler Update configuration path in help message [Validator] Review Albanian translation [Process] Fix Inconsistent Exit Status in proc_get_status for PHP Versions Below 8.3 [Validator] Update Czech (cz) translation Sync translations [Mailer][Postmark][Webhook] Make allowed IPs configurable Review portuguese translations [Validator] Fix fields without constraints in `Collection` deal with fields for which no constraints have been configured [DomCrawler] [Form] Fix the exclusion of <template>
2 parents 0c362d2 + 7877f70 commit ba614ef

File tree

17 files changed

+254
-30
lines changed

17 files changed

+254
-30
lines changed

src/Symfony/Component/AssetMapper/Compiler/JavaScriptImportPathCompiler.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,31 @@
2727
*/
2828
final class JavaScriptImportPathCompiler implements AssetCompilerInterface
2929
{
30-
// https://regex101.com/r/qFoeoR/1
31-
private const IMPORT_PATTERN = '/(?:\'(?:[^\'\\\\]|\\\\.)*\'|"(?:[^"\\\\]|\\\\.)*")|(?:import\s*(?:(?:\*\s*as\s+\w+|[\w\s{},*]+)\s*from\s*)?|\bimport\()\s*[\'"`](\.\/[^\'"`]+|(\.\.\/)*[^\'"`]+)[\'"`]\s*[;\)]?/m';
30+
/**
31+
* @see https://regex101.com/r/1iBAIb/1
32+
*/
33+
private const IMPORT_PATTERN = '/
34+
^
35+
(?:\/\/.*) # Lines that start with comments
36+
|
37+
(?:
38+
\'(?:[^\'\\\\]|\\\\.)*\' # Strings enclosed in single quotes
39+
|
40+
"(?:[^"\\\\]|\\\\.)*" # Strings enclosed in double quotes
41+
)
42+
|
43+
(?: # Import statements (script captured)
44+
import\s*
45+
(?:
46+
(?:\*\s*as\s+\w+|\s+[\w\s{},*]+)
47+
\s*from\s*
48+
)?
49+
|
50+
\bimport\(
51+
)
52+
\s*[\'"`](\.\/[^\'"`]+|(\.\.\/)*[^\'"`]+)[\'"`]\s*[;\)]
53+
?
54+
/mx';
3255

3356
public function __construct(
3457
private readonly ImportMapConfigReader $importMapConfigReader,
@@ -42,7 +65,7 @@ public function compile(string $content, MappedAsset $asset, AssetMapperInterfac
4265
return preg_replace_callback(self::IMPORT_PATTERN, function ($matches) use ($asset, $assetMapper, $content) {
4366
$fullImportString = $matches[0][0];
4467

45-
// Ignore enquoted strings (e.g. console.log("import 'foo';")
68+
// Ignore matches that did not capture import statements
4669
if (!isset($matches[1][0])) {
4770
return $fullImportString;
4871
}

src/Symfony/Component/AssetMapper/Tests/Compiler/JavaScriptImportPathCompilerTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,16 @@ public static function provideCompileTests(): iterable
177177
'expectedJavaScriptImports' => ['/assets/other.js' => ['lazy' => false, 'asset' => 'other.js', 'add' => true]],
178178
];
179179

180+
yield 'commented_import_on_one_line_then_module_name_on_next_is_not_ok' => [
181+
'input' => "// import \n './other.js';",
182+
'expectedJavaScriptImports' => [],
183+
];
184+
185+
yield 'commented_import_on_one_line_then_import_on_next_is_ok' => [
186+
'input' => "// import\nimport { Foo } from './other.js';",
187+
'expectedJavaScriptImports' => ['/assets/other.js' => ['lazy' => false, 'asset' => 'other.js', 'add' => true]],
188+
];
189+
180190
yield 'importing_a_css_file_is_included' => [
181191
'input' => "import './styles.css';",
182192
'expectedJavaScriptImports' => ['/assets/styles.css' => ['lazy' => false, 'asset' => 'styles.css', 'add' => true]],

src/Symfony/Component/DomCrawler/Form.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,14 +418,14 @@ private function initialize(): void
418418
// corresponding elements are either descendants or have a matching HTML5 form attribute
419419
$formId = Crawler::xpathLiteral($this->node->getAttribute('id'));
420420

421-
$fieldNodes = $xpath->query(sprintf('( descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)] )[not(ancestor::template)]', $formId));
421+
$fieldNodes = $xpath->query(sprintf('( descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)] )[( not(ancestor::template) or ancestor::turbo-stream )]', $formId));
422422
foreach ($fieldNodes as $node) {
423423
$this->addField($node);
424424
}
425425
} else {
426426
// do the xpath query with $this->node as the context node, to only find descendant elements
427427
// however, descendant elements with form attribute are not part of this form
428-
$fieldNodes = $xpath->query('( descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)] )[not(ancestor::template)]', $this->node);
428+
$fieldNodes = $xpath->query('( descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)] )[( not(ancestor::template) or ancestor::turbo-stream )]', $this->node);
429429
foreach ($fieldNodes as $node) {
430430
$this->addField($node);
431431
}

src/Symfony/Component/DomCrawler/Tests/FormTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,9 @@ public function testGetValues()
432432
$form = $this->createForm('<form><template><input type="text" name="foo" value="foo" /></template><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
433433
$this->assertEquals(['bar' => 'bar'], $form->getValues(), '->getValues() does not include template fields');
434434
$this->assertFalse($form->has('foo'));
435+
436+
$form = $this->createForm('<turbo-stream><template><form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><select multiple="multiple" name="baz[]"></select><input type="submit" /></form></template></turbo-stream>');
437+
$this->assertEquals(['foo[bar]' => 'foo', 'bar' => 'bar', 'baz' => []], $form->getValues(), '->getValues() returns all form field values from template field inside a turbo-stream');
435438
}
436439

437440
public function testSetValues()
@@ -486,6 +489,9 @@ public function testGetFiles()
486489
$form = $this->createForm('<form method="post"><template><input type="file" name="foo"/></template><input type="text" name="bar" value="bar"/><input type="submit"/></form>');
487490
$this->assertEquals([], $form->getFiles(), '->getFiles() does not include template file fields');
488491
$this->assertFalse($form->has('foo'));
492+
493+
$form = $this->createForm('<turbo-stream><template><form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form></template></turbo-stream>');
494+
$this->assertEquals(['foo[bar]' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]], $form->getFiles(), '->getFiles() return files fields from template inside turbo-stream');
489495
}
490496

491497
public function testGetPhpFiles()

src/Symfony/Component/Form/Resources/translations/validators.sq.xlf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
<header>
55
<note>
66
Për fjalët e huaja, të cilat nuk kanë përkthim të drejtpërdrejtë, ju lutemi të ndiqni rregullat e mëposhtme:
7-
a) në rast se emri është akronim i përdorur gjerësisht si i përveçëm, atëherë, emri lakohet pa thonjëza dhe mbaresa shkruhet me vizë ndarëse. Gjinia gjykohet sipas rastit. Shembull: JSON-i (mashkullore)
8-
b) në rast se emri është akronim i papërdorur gjerësisht si i përveçëm, atëherë, emri lakohet pa thonjëza dhe mbaresa shkruhet me vizë ndarëse. Gjinia është femërore. Shembull: URL-ja (femërore)
9-
c) në rast se emri duhet lakuar për shkak të rasës në fjali, atëherë, emri lakohet pa thonjëza dhe mbaresa shkruhet me vizë ndarëse. Shembull: host-i, prej host-it
10-
d) në rast se emri nuk duhet lakuar për shkak të trajtës në fjali, atëherë, emri rrethohet me thonjëzat “”. Shembull: “locale”
7+
a) në rast se emri është akronim i përdorur gjerësisht si i përveçëm, atëherë, emri lakohet pa thonjëza dhe mbaresa shkruhet me vizë ndarëse. Gjinia gjykohet sipas rastit. Shembull: JSON-i (mashkullore)
8+
b) në rast se emri është akronim i papërdorur gjerësisht si i përveçëm, atëherë, emri lakohet pa thonjëza dhe mbaresa shkruhet me vizë ndarëse. Gjinia është femërore. Shembull: URL-ja (femërore)
9+
c) në rast se emri duhet lakuar për shkak të rasës në fjali, atëherë, emri lakohet pa thonjëza dhe mbaresa shkruhet me vizë ndarëse. Shembull: host-i, prej host-it
10+
d) në rast se emri nuk duhet lakuar për shkak të trajtës në fjali, atëherë, emri rrethohet me thonjëzat “”. Shembull: “locale”
1111
</note>
1212
</header>
1313
<body>

src/Symfony/Component/Mailer/Bridge/Postmark/Webhook/PostmarkRequestParser.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,18 @@ final class PostmarkRequestParser extends AbstractRequestParser
2727
{
2828
public function __construct(
2929
private readonly PostmarkPayloadConverter $converter,
30+
31+
// https://postmarkapp.com/support/article/800-ips-for-firewalls#webhooks
32+
// localhost is added for testing
33+
private readonly array $allowedIPs = ['3.134.147.250', '50.31.156.6', '50.31.156.77', '18.217.206.57', '127.0.0.1'],
3034
) {
3135
}
3236

3337
protected function getRequestMatcher(): RequestMatcherInterface
3438
{
3539
return new ChainRequestMatcher([
3640
new MethodRequestMatcher('POST'),
37-
// https://postmarkapp.com/support/article/800-ips-for-firewalls#webhooks
38-
// localhost is added for testing
39-
new IpsRequestMatcher(['3.134.147.250', '50.31.156.6', '50.31.156.77', '18.217.206.57', '127.0.0.1']),
41+
new IpsRequestMatcher($this->allowedIPs),
4042
new IsJsonRequestMatcher(),
4143
]);
4244
}

src/Symfony/Component/PasswordHasher/Command/UserPasswordHashCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ protected function configure(): void
6565
Suppose that you have the following security configuration in your application:
6666
6767
<comment>
68-
# app/config/security.yml
68+
# config/packages/security.yml
6969
security:
7070
password_hashers:
7171
Symfony\Component\Security\Core\User\InMemoryUser: plaintext

src/Symfony/Component/Process/Process.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class Process implements \IteratorAggregate
8181
private WindowsPipes|UnixPipes $processPipes;
8282

8383
private ?int $latestSignal = null;
84+
private ?int $cachedExitCode = null;
8485

8586
private static ?bool $sigchild = null;
8687

@@ -1291,6 +1292,19 @@ protected function updateStatus(bool $blocking): void
12911292
$this->processInformation = proc_get_status($this->process);
12921293
$running = $this->processInformation['running'];
12931294

1295+
// In PHP < 8.3, "proc_get_status" only returns the correct exit status on the first call.
1296+
// Subsequent calls return -1 as the process is discarded. This workaround caches the first
1297+
// retrieved exit status for consistent results in later calls, mimicking PHP 8.3 behavior.
1298+
if (\PHP_VERSION_ID < 80300) {
1299+
if (!isset($this->cachedExitCode) && !$running && -1 !== $this->processInformation['exitcode']) {
1300+
$this->cachedExitCode = $this->processInformation['exitcode'];
1301+
}
1302+
1303+
if (isset($this->cachedExitCode) && !$running && -1 === $this->processInformation['exitcode']) {
1304+
$this->processInformation['exitcode'] = $this->cachedExitCode;
1305+
}
1306+
}
1307+
12941308
$this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running);
12951309

12961310
if ($this->fallbackStatus && $this->isSigchildEnabled()) {

src/Symfony/Component/Process/Tests/ProcessTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,60 @@ public function testEnvCaseInsensitiveOnWindows()
15961596
}
15971597
}
15981598

1599+
public function testMultipleCallsToProcGetStatus()
1600+
{
1601+
$process = $this->getProcess('echo foo');
1602+
$process->start(static function () use ($process) {
1603+
return $process->isRunning();
1604+
});
1605+
while ($process->isRunning()) {
1606+
usleep(1000);
1607+
}
1608+
$this->assertSame(0, $process->getExitCode());
1609+
}
1610+
1611+
public function testFailingProcessWithMultipleCallsToProcGetStatus()
1612+
{
1613+
$process = $this->getProcess('exit 123');
1614+
$process->start(static function () use ($process) {
1615+
return $process->isRunning();
1616+
});
1617+
while ($process->isRunning()) {
1618+
usleep(1000);
1619+
}
1620+
$this->assertSame(123, $process->getExitCode());
1621+
}
1622+
1623+
/**
1624+
* @group slow
1625+
*/
1626+
public function testLongRunningProcessWithMultipleCallsToProcGetStatus()
1627+
{
1628+
$process = $this->getProcess('php -r "sleep(1); echo \'done\';"');
1629+
$process->start(static function () use ($process) {
1630+
return $process->isRunning();
1631+
});
1632+
while ($process->isRunning()) {
1633+
usleep(1000);
1634+
}
1635+
$this->assertSame(0, $process->getExitCode());
1636+
}
1637+
1638+
/**
1639+
* @group slow
1640+
*/
1641+
public function testLongRunningProcessWithMultipleCallsToProcGetStatusError()
1642+
{
1643+
$process = $this->getProcess('php -r "sleep(1); echo \'failure\'; exit(123);"');
1644+
$process->start(static function () use ($process) {
1645+
return $process->isRunning();
1646+
});
1647+
while ($process->isRunning()) {
1648+
usleep(1000);
1649+
}
1650+
$this->assertSame(123, $process->getExitCode());
1651+
}
1652+
15991653
/**
16001654
* @group transient-on-windows
16011655
*/

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Collection extends Composite
4343
*/
4444
public function __construct(mixed $fields = null, ?array $groups = null, mixed $payload = null, ?bool $allowExtraFields = null, ?bool $allowMissingFields = null, ?string $extraFieldsMessage = null, ?string $missingFieldsMessage = null)
4545
{
46-
if (\is_array($fields) && ([] === $fields || ($firstField = reset($fields)) instanceof Constraint || ($firstField[0] ?? null) instanceof Constraint)) {
46+
if (self::isFieldsOption($fields)) {
4747
$fields = ['fields' => $fields];
4848
}
4949

@@ -81,4 +81,31 @@ protected function getCompositeOption(): string
8181
{
8282
return 'fields';
8383
}
84+
85+
private static function isFieldsOption($options): bool
86+
{
87+
if (!\is_array($options)) {
88+
return false;
89+
}
90+
91+
foreach ($options as $optionOrField) {
92+
if ($optionOrField instanceof Constraint) {
93+
return true;
94+
}
95+
96+
if (null === $optionOrField) {
97+
continue;
98+
}
99+
100+
if (!\is_array($optionOrField)) {
101+
return false;
102+
}
103+
104+
if ($optionOrField && !($optionOrField[0] ?? null) instanceof Constraint) {
105+
return false;
106+
}
107+
}
108+
109+
return true;
110+
}
84111
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy