Skip to content

Commit ea2eafa

Browse files
committed
Merge branch '6.4' into 7.0
* 6.4: (22 commits) fix merge [AssetMapper] Check asset/vendor directory is writable detect wrong e-mail validation modes detect wrong usages of minMessage/maxMessage in options [Security] Remove workflow from empty folder read form values using the chain data accessor [Validator] Review Bulgarian (bg) translation Reviewed italian translation Fix french translation call substr() with integer offsets [Finder] Also consider .git inside the basedir of in() directory review: FR translation [Serializer] Add AbstractNormalizerContextBuilder::defaultConstructorArguments() Update spanish and catalan translations Update AbstractSchemaListener.php to adjust more database params Updated id=113 Arabic translation. symfony#53771 Updated validator Lithuanian translations review: translation RU [PropertyInfo] Fix PHPStan properties type in trait explicitly cast boolean SSL stream options ...
2 parents bee1999 + ea61ad3 commit ea2eafa

File tree

36 files changed

+345
-157
lines changed

36 files changed

+345
-157
lines changed

src/Symfony/Bridge/Doctrine/SchemaListener/AbstractSchemaListener.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use Doctrine\DBAL\Connection;
1515
use Doctrine\DBAL\Exception\TableNotFoundException;
16+
use Doctrine\DBAL\Schema\Table;
17+
use Doctrine\DBAL\Types\Types;
1618
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
1719

1820
abstract class AbstractSchemaListener
@@ -22,8 +24,16 @@ abstract public function postGenerateSchema(GenerateSchemaEventArgs $event): voi
2224
protected function getIsSameDatabaseChecker(Connection $connection): \Closure
2325
{
2426
return static function (\Closure $exec) use ($connection): bool {
27+
$schemaManager = $connection->createSchemaManager();
28+
2529
$checkTable = 'schema_subscriber_check_'.bin2hex(random_bytes(7));
26-
$connection->executeStatement(sprintf('CREATE TABLE %s (id INTEGER NOT NULL)', $checkTable));
30+
$table = new Table($checkTable);
31+
$table->addColumn('id', Types::INTEGER)
32+
->setAutoincrement(true)
33+
->setNotnull(true);
34+
$table->setPrimaryKey(['id']);
35+
36+
$schemaManager->createTable($table);
2737

2838
try {
2939
$exec(sprintf('DROP TABLE %s', $checkTable));
@@ -32,7 +42,7 @@ protected function getIsSameDatabaseChecker(Connection $connection): \Closure
3242
}
3343

3444
try {
35-
$connection->executeStatement(sprintf('DROP TABLE %s', $checkTable));
45+
$schemaManager->dropTable($checkTable);
3646

3747
return false;
3848
} catch (TableNotFoundException) {

src/Symfony/Component/AssetMapper/ImportMap/RemotePackageStorage.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\AssetMapper\ImportMap;
1313

14+
use Symfony\Component\AssetMapper\Exception\RuntimeException;
15+
1416
/**
1517
* Manages the local storage of remote/vendor importmap packages.
1618
*/
@@ -52,7 +54,9 @@ public function save(ImportMapEntry $entry, string $contents): void
5254
$vendorPath = $this->getDownloadPath($entry->packageModuleSpecifier, $entry->type);
5355

5456
@mkdir(\dirname($vendorPath), 0777, true);
55-
file_put_contents($vendorPath, $contents);
57+
if (false === @file_put_contents($vendorPath, $contents)) {
58+
throw new RuntimeException(error_get_last()['message'] ?? sprintf('Failed to write file "%s".', $vendorPath));
59+
}
5660
}
5761

5862
public function saveExtraFile(ImportMapEntry $entry, string $extraFilename, string $contents): void
@@ -64,7 +68,9 @@ public function saveExtraFile(ImportMapEntry $entry, string $extraFilename, stri
6468
$vendorPath = $this->getExtraFileDownloadPath($entry, $extraFilename);
6569

6670
@mkdir(\dirname($vendorPath), 0777, true);
67-
file_put_contents($vendorPath, $contents);
71+
if (false === @file_put_contents($vendorPath, $contents)) {
72+
throw new RuntimeException(error_get_last()['message'] ?? sprintf('Failed to write file "%s".', $vendorPath));
73+
}
6874
}
6975

7076
/**

src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapManagerTest.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -201,15 +201,15 @@ public static function getRequirePackageTests(): iterable
201201
];
202202

203203
yield 'single_package_with_a_path' => [
204-
'packages' => [new PackageRequireOptions('some/module', path: self::$writableRoot.'/assets/some_file.js')],
205-
'expectedProviderPackageArgumentCount' => 0,
206-
'resolvedPackages' => [],
207-
'expectedImportMap' => [
208-
'some/module' => [
209-
// converted to relative path
210-
'path' => './assets/some_file.js',
204+
'packages' => [new PackageRequireOptions('some/module', path: self::$writableRoot.'/assets/some_file.js')],
205+
'expectedProviderPackageArgumentCount' => 0,
206+
'resolvedPackages' => [],
207+
'expectedImportMap' => [
208+
'some/module' => [
209+
// converted to relative path
210+
'path' => './assets/some_file.js',
211+
],
211212
],
212-
],
213213
];
214214
}
215215

src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageStorageTest.php

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class RemotePackageStorageTest extends TestCase
2525
protected function setUp(): void
2626
{
2727
$this->filesystem = new Filesystem();
28-
if (!file_exists(self::$writableRoot)) {
28+
if (!$this->filesystem->exists(self::$writableRoot)) {
2929
$this->filesystem->mkdir(self::$writableRoot);
3030
}
3131
}
@@ -41,14 +41,30 @@ public function testGetStorageDir()
4141
$this->assertSame(realpath(self::$writableRoot.'/assets/vendor'), realpath($storage->getStorageDir()));
4242
}
4343

44+
public function testSaveThrowsWhenVendorDirectoryIsNotWritable()
45+
{
46+
$this->filesystem->mkdir($vendorDir = self::$writableRoot.'/assets/acme/vendor');
47+
$this->filesystem->chmod($vendorDir, 0555);
48+
49+
$storage = new RemotePackageStorage($vendorDir);
50+
$entry = ImportMapEntry::createRemote('foo', ImportMapType::JS, '/does/not/matter', '1.0.0', 'module_specifier', false);
51+
52+
$this->expectException(\RuntimeException::class);
53+
$this->expectExceptionMessage('file_put_contents('.$vendorDir.'/module_specifier/module_specifier.index.js): Failed to open stream: No such file or directory');
54+
$storage->save($entry, 'any content');
55+
56+
$this->filesystem->remove($vendorDir);
57+
}
58+
4459
public function testIsDownloaded()
4560
{
4661
$storage = new RemotePackageStorage(self::$writableRoot.'/assets/vendor');
4762
$entry = ImportMapEntry::createRemote('foo', ImportMapType::JS, '/does/not/matter', '1.0.0', 'module_specifier', false);
4863
$this->assertFalse($storage->isDownloaded($entry));
64+
4965
$targetPath = self::$writableRoot.'/assets/vendor/module_specifier/module_specifier.index.js';
50-
@mkdir(\dirname($targetPath), 0777, true);
51-
file_put_contents($targetPath, 'any content');
66+
$this->filesystem->mkdir(\dirname($targetPath));
67+
$this->filesystem->dumpFile($targetPath, 'any content');
5268
$this->assertTrue($storage->isDownloaded($entry));
5369
}
5470

@@ -57,9 +73,10 @@ public function testIsExtraFileDownloaded()
5773
$storage = new RemotePackageStorage(self::$writableRoot.'/assets/vendor');
5874
$entry = ImportMapEntry::createRemote('foo', ImportMapType::JS, '/does/not/matter', '1.0.0', 'module_specifier', false);
5975
$this->assertFalse($storage->isExtraFileDownloaded($entry, '/path/to/extra.woff'));
76+
6077
$targetPath = self::$writableRoot.'/assets/vendor/module_specifier/path/to/extra.woff';
61-
@mkdir(\dirname($targetPath), 0777, true);
62-
file_put_contents($targetPath, 'any content');
78+
$this->filesystem->mkdir(\dirname($targetPath));
79+
$this->filesystem->dumpFile($targetPath, 'any content');
6380
$this->assertTrue($storage->isExtraFileDownloaded($entry, '/path/to/extra.woff'));
6481
}
6582

@@ -92,7 +109,7 @@ public function testGetDownloadedPath(string $packageModuleSpecifier, ImportMapT
92109
$this->assertSame($expectedPath, $storage->getDownloadPath($packageModuleSpecifier, $importMapType));
93110
}
94111

95-
public static function getDownloadPathTests()
112+
public static function getDownloadPathTests(): iterable
96113
{
97114
yield 'javascript bare package' => [
98115
'packageModuleSpecifier' => 'foo',

src/Symfony/Component/Finder/Iterator/VcsIgnoredFilterIterator.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ public function __construct(\Iterator $iterator, string $baseDir)
3737
{
3838
$this->baseDir = $this->normalizePath($baseDir);
3939

40-
foreach ($this->parentDirectoriesUpwards($this->baseDir) as $parentDirectory) {
41-
if (@is_dir("{$parentDirectory}/.git")) {
42-
$this->baseDir = $parentDirectory;
40+
foreach ([$this->baseDir, ...$this->parentDirectoriesUpwards($this->baseDir)] as $directory) {
41+
if (@is_dir("{$directory}/.git")) {
42+
$this->baseDir = $directory;
4343
break;
4444
}
4545
}

src/Symfony/Component/Finder/Tests/Iterator/VcsIgnoredFilterIteratorTest.php

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ protected function tearDown(): void
3434
*
3535
* @dataProvider getAcceptData
3636
*/
37-
public function testAccept(array $gitIgnoreFiles, array $otherFileNames, array $expectedResult)
37+
public function testAccept(array $gitIgnoreFiles, array $otherFileNames, array $expectedResult, string $baseDir = '')
3838
{
3939
$otherFileNames = $this->toAbsolute($otherFileNames);
4040
foreach ($otherFileNames as $path) {
@@ -51,7 +51,8 @@ public function testAccept(array $gitIgnoreFiles, array $otherFileNames, array $
5151

5252
$inner = new InnerNameIterator($otherFileNames);
5353

54-
$iterator = new VcsIgnoredFilterIterator($inner, $this->tmpDir);
54+
$baseDir = $this->tmpDir.('' !== $baseDir ? '/'.$baseDir : '');
55+
$iterator = new VcsIgnoredFilterIterator($inner, $baseDir);
5556

5657
$this->assertIterator($this->toAbsolute($expectedResult), $iterator);
5758
}
@@ -74,6 +75,55 @@ public static function getAcceptData(): iterable
7475
],
7576
];
7677

78+
yield 'simple file - .gitignore and in() from repository root' => [
79+
[
80+
'.gitignore' => 'a.txt',
81+
],
82+
[
83+
'.git',
84+
'a.txt',
85+
'b.txt',
86+
'dir/',
87+
'dir/a.txt',
88+
],
89+
[
90+
'.git',
91+
'b.txt',
92+
'dir',
93+
],
94+
];
95+
96+
yield 'nested git repositories only consider .gitignore files of the most inner repository' => [
97+
[
98+
'.gitignore' => "nested/*\na.txt",
99+
'nested/.gitignore' => 'c.txt',
100+
'nested/dir/.gitignore' => 'f.txt',
101+
],
102+
[
103+
'.git',
104+
'a.txt',
105+
'b.txt',
106+
'nested/',
107+
'nested/.git',
108+
'nested/c.txt',
109+
'nested/d.txt',
110+
'nested/dir/',
111+
'nested/dir/e.txt',
112+
'nested/dir/f.txt',
113+
],
114+
[
115+
'.git',
116+
'a.txt',
117+
'b.txt',
118+
'nested',
119+
'nested/.git',
120+
'nested/d.txt',
121+
'nested/dir',
122+
'nested/dir/e.txt',
123+
],
124+
'nested',
125+
];
126+
77127
yield 'simple file at root' => [
78128
[
79129
'.gitignore' => '/a.txt',

src/Symfony/Component/Form/Extension/Core/DataAccessor/PropertyPathAccessor.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
namespace Symfony\Component\Form\Extension\Core\DataAccessor;
1313

1414
use Symfony\Component\Form\DataAccessorInterface;
15+
use Symfony\Component\Form\DataMapperInterface;
1516
use Symfony\Component\Form\Exception\AccessException;
17+
use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper;
1618
use Symfony\Component\Form\FormInterface;
1719
use Symfony\Component\PropertyAccess\Exception\AccessException as PropertyAccessException;
1820
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
@@ -51,15 +53,25 @@ public function setValue(object|array &$data, mixed $value, FormInterface $form)
5153
throw new AccessException('Unable to write the given value as no property path is defined.');
5254
}
5355

56+
$getValue = function () use ($data, $form, $propertyPath) {
57+
$dataMapper = $this->getDataMapper($form);
58+
59+
if ($dataMapper instanceof DataMapper && null !== $dataAccessor = $dataMapper->getDataAccessor()) {
60+
return $dataAccessor->getValue($data, $form);
61+
}
62+
63+
return $this->getPropertyValue($data, $propertyPath);
64+
};
65+
5466
// If the field is of type DateTimeInterface and the data is the same skip the update to
5567
// keep the original object hash
56-
if ($value instanceof \DateTimeInterface && $value == $this->getPropertyValue($data, $propertyPath)) {
68+
if ($value instanceof \DateTimeInterface && $value == $getValue()) {
5769
return;
5870
}
5971

6072
// If the data is identical to the value in $data, we are
6173
// dealing with a reference
62-
if (!\is_object($data) || !$form->getConfig()->getByReference() || $value !== $this->getPropertyValue($data, $propertyPath)) {
74+
if (!\is_object($data) || !$form->getConfig()->getByReference() || $value !== $getValue()) {
6375
$this->propertyAccessor->setValue($data, $propertyPath, $value);
6476
}
6577
}
@@ -93,4 +105,13 @@ private function getPropertyValue(object|array $data, PropertyPathInterface $pro
93105
return null;
94106
}
95107
}
108+
109+
private function getDataMapper(FormInterface $form): ?DataMapperInterface
110+
{
111+
do {
112+
$dataMapper = $form->getConfig()->getDataMapper();
113+
} while (null === $dataMapper && null !== $form = $form->getParent());
114+
115+
return $dataMapper;
116+
}
96117
}

src/Symfony/Component/Form/Extension/Core/DataMapper/DataMapper.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,12 @@ public function mapFormsToData(\Traversable $forms, mixed &$data): void
7474
}
7575
}
7676
}
77+
78+
/**
79+
* @internal
80+
*/
81+
public function getDataAccessor(): DataAccessorInterface
82+
{
83+
return $this->dataAccessor;
84+
}
7785
}

src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/DataMapperTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor;
1717
use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper;
1818
use Symfony\Component\Form\Extension\Core\Type\DateType;
19+
use Symfony\Component\Form\Extension\Core\Type\FormType;
20+
use Symfony\Component\Form\Extension\Core\Type\TextType;
1921
use Symfony\Component\Form\Form;
2022
use Symfony\Component\Form\FormConfigBuilder;
2123
use Symfony\Component\Form\FormFactoryBuilder;
@@ -403,6 +405,25 @@ public function testMapFormsToDataMapsDateTimeInstanceToArrayIfNotSetBefore()
403405

404406
$this->assertEquals(['date' => new \DateTime('2022-08-04', new \DateTimeZone('UTC'))], $form->getData());
405407
}
408+
409+
public function testMapFormToDataWithOnlyGetterConfigured()
410+
{
411+
$person = new DummyPerson('foo');
412+
$form = (new FormFactoryBuilder())
413+
->getFormFactory()
414+
->createBuilder(FormType::class, $person)
415+
->add('name', TextType::class, [
416+
'getter' => function (DummyPerson $person) {
417+
return $person->myName();
418+
},
419+
])
420+
->getForm();
421+
$form->submit([
422+
'name' => 'bar',
423+
]);
424+
425+
$this->assertSame('bar', $person->myName());
426+
}
406427
}
407428

408429
class SubmittedForm extends Form
@@ -439,4 +460,9 @@ public function rename($name): void
439460
{
440461
$this->name = $name;
441462
}
463+
464+
public function setName($name): void
465+
{
466+
$this->name = $name;
467+
}
442468
}

src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ public function __construct(array $options, \Redis|Relay|\RedisCluster|null $red
8888
throw new InvalidArgumentException('Cannot configure Redis Sentinel and Redis Cluster instance at the same time.');
8989
}
9090

91+
$booleanStreamOptions = [
92+
'allow_self_signed',
93+
'capture_peer_cert',
94+
'capture_peer_cert_chain',
95+
'disable_compression',
96+
'SNI_enabled',
97+
'verify_peer',
98+
'verify_peer_name',
99+
];
100+
101+
foreach ($options['ssl'] ?? [] as $streamOption => $value) {
102+
if (\in_array($streamOption, $booleanStreamOptions, true) && \is_string($value)) {
103+
$options['ssl'][$streamOption] = filter_var($value, \FILTER_VALIDATE_BOOL);
104+
}
105+
}
106+
91107
if ((\is_array($host) && null === $sentinelMaster) || $redis instanceof \RedisCluster) {
92108
$hosts = \is_string($host) ? [$host.':'.$port] : $host; // Always ensure we have an array
93109
$this->redis = static fn () => self::initializeRedisCluster($redis, $hosts, $auth, $options);

0 commit comments

Comments
 (0)