diff --git a/link b/link index 29f9600d6b94e..78f746e831130 100755 --- a/link +++ b/link @@ -49,7 +49,7 @@ $directories = array_merge(...array_values(array_map(function ($part) { $directories[] = __DIR__.'/src/Symfony/Contracts'; foreach ($directories as $dir) { if ($filesystem->exists($composer = "$dir/composer.json")) { - $sfPackages[json_decode(file_get_contents($composer))->name] = $dir; + $sfPackages[json_decode($filesystem->readFile($composer), flags: JSON_THROW_ON_ERROR)->name] = $dir; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php index cb05fe827a14e..32b38de9af025 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -260,7 +260,7 @@ private function getPublicDirectory(ContainerInterface $container): string return $defaultPublicDir; } - $composerConfig = json_decode(file_get_contents($composerFilePath), true); + $composerConfig = json_decode($this->filesystem->readFile($composerFilePath), true, flags: \JSON_THROW_ON_ERROR); return $composerConfig['extra']['public-dir'] ?? $defaultPublicDir; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index 8a1dedd273067..4122db6fdd0cc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -232,7 +232,7 @@ private function warmup(string $warmupDir, string $realBuildDir): void $search = [$warmupDir, str_replace('\\', '\\\\', $warmupDir)]; $replace = str_replace('\\', '/', $realBuildDir); foreach (Finder::create()->files()->in($warmupDir) as $file) { - $content = str_replace($search, $replace, file_get_contents($file), $count); + $content = str_replace($search, $replace, $this->filesystem->readFile($file), $count); if ($count) { file_put_contents($file, $content); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 7a52d9f9bf8ab..911242bc2830a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -71,6 +71,7 @@ use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Glob; use Symfony\Component\Form\Extension\HtmlSanitizer\Type\TextTypeHtmlSanitizerExtension; @@ -3141,7 +3142,7 @@ private function getPublicDirectory(ContainerBuilder $container): string } $container->addResource(new FileResource($composerFilePath)); - $composerConfig = json_decode(file_get_contents($composerFilePath), true); + $composerConfig = json_decode((new Filesystem())->readFile($composerFilePath), true, flags: \JSON_THROW_ON_ERROR); return isset($composerConfig['extra']['public-dir']) ? $projectDir.'/'.$composerConfig['extra']['public-dir'] : $defaultPublicDir; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index 78b13905ebf31..b950b5fd96c1c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -75,7 +75,7 @@ function () use ($file) { $kernelRef = new \ReflectionObject($this->kernel); $kernelFile = $kernelRef->getFileName(); /** @var ResourceInterface[] $meta */ - $meta = unserialize(file_get_contents($containerMetaFile)); + $meta = unserialize($this->fs->readFile($containerMetaFile)); $found = false; foreach ($meta as $resource) { if ((string) $resource === $kernelFile) { @@ -93,7 +93,7 @@ function () use ($file) { ); $this->assertMatchesRegularExpression( sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', $containerClass), - file_get_contents($containerFile), + $this->fs->readFile($containerFile), 'kernel.container_class is properly set on the dumped container' ); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/SodiumVaultTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/SodiumVaultTest.php index 603d13504770f..96d5dcea132a5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/SodiumVaultTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/SodiumVaultTest.php @@ -21,16 +21,18 @@ class SodiumVaultTest extends TestCase { private string $secretsDir; + private Filesystem $filesystem; protected function setUp(): void { + $this->filesystem = new Filesystem(); $this->secretsDir = sys_get_temp_dir().'/sf_secrets/test/'; - (new Filesystem())->remove($this->secretsDir); + $this->filesystem->remove($this->secretsDir); } protected function tearDown(): void { - (new Filesystem())->remove($this->secretsDir); + $this->filesystem->remove($this->secretsDir); } public function testGenerateKeys() @@ -41,8 +43,8 @@ public function testGenerateKeys() $this->assertFileExists($this->secretsDir.'/test.encrypt.public.php'); $this->assertFileExists($this->secretsDir.'/test.decrypt.private.php'); - $encKey = file_get_contents($this->secretsDir.'/test.encrypt.public.php'); - $decKey = file_get_contents($this->secretsDir.'/test.decrypt.private.php'); + $encKey = $this->filesystem->readFile($this->secretsDir.'/test.encrypt.public.php'); + $decKey = $this->filesystem->readFile($this->secretsDir.'/test.decrypt.private.php'); $this->assertFalse($vault->generateKeys()); $this->assertStringEqualsFile($this->secretsDir.'/test.encrypt.public.php', $encKey); diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 7b9d1b3e5033b..4d66cf9bcc34d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -28,7 +28,7 @@ "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/filesystem": "^6.4|^7.0", + "symfony/filesystem": "^7.1", "symfony/finder": "^6.4|^7.0", "symfony/routing": "^6.4|^7.0" }, diff --git a/src/Symfony/Component/AssetMapper/CompiledAssetMapperConfigReader.php b/src/Symfony/Component/AssetMapper/CompiledAssetMapperConfigReader.php index daa656805fe9d..c0e1b44dd4f27 100644 --- a/src/Symfony/Component/AssetMapper/CompiledAssetMapperConfigReader.php +++ b/src/Symfony/Component/AssetMapper/CompiledAssetMapperConfigReader.php @@ -11,6 +11,7 @@ namespace Symfony\Component\AssetMapper; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Path; /** @@ -18,8 +19,12 @@ */ class CompiledAssetMapperConfigReader { - public function __construct(private readonly string $directory) - { + private readonly Filesystem $filesystem; + + public function __construct( + private readonly string $directory, + ) { + $this->filesystem = new Filesystem(); } public function configExists(string $filename): bool @@ -29,7 +34,7 @@ public function configExists(string $filename): bool public function loadConfig(string $filename): array { - return json_decode(file_get_contents(Path::join($this->directory, $filename)), true, 512, \JSON_THROW_ON_ERROR); + return json_decode($this->filesystem->readFile(Path::join($this->directory, $filename)), true, 512, \JSON_THROW_ON_ERROR); } public function saveConfig(string $filename, array $data): string diff --git a/src/Symfony/Component/AssetMapper/Factory/CachedMappedAssetFactory.php b/src/Symfony/Component/AssetMapper/Factory/CachedMappedAssetFactory.php index eff109c22624c..959dee8abdddf 100644 --- a/src/Symfony/Component/AssetMapper/Factory/CachedMappedAssetFactory.php +++ b/src/Symfony/Component/AssetMapper/Factory/CachedMappedAssetFactory.php @@ -17,6 +17,7 @@ use Symfony\Component\Config\Resource\FileExistenceResource; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Filesystem\Filesystem; /** * Decorates the asset factory to load MappedAssets from cache when possible. @@ -36,7 +37,7 @@ public function createMappedAsset(string $logicalPath, string $sourcePath): ?Map $configCache = new ConfigCache($cachePath, $this->debug); if ($configCache->isFresh()) { - return unserialize(file_get_contents($cachePath)); + return unserialize((new Filesystem())->readFile($cachePath)); } $mappedAsset = $this->innerFactory->createMappedAsset($logicalPath, $sourcePath); diff --git a/src/Symfony/Component/AssetMapper/Factory/MappedAssetFactory.php b/src/Symfony/Component/AssetMapper/Factory/MappedAssetFactory.php index 1d2ba703e6592..0b5b3760bdbfc 100644 --- a/src/Symfony/Component/AssetMapper/Factory/MappedAssetFactory.php +++ b/src/Symfony/Component/AssetMapper/Factory/MappedAssetFactory.php @@ -16,6 +16,7 @@ use Symfony\Component\AssetMapper\Exception\RuntimeException; use Symfony\Component\AssetMapper\MappedAsset; use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface; +use Symfony\Component\Filesystem\Filesystem; /** * Creates MappedAsset objects by reading their contents & passing it through compilers. @@ -104,7 +105,7 @@ private function compileContent(MappedAsset $asset): ?string return null; } - $content = file_get_contents($asset->sourcePath); + $content = (new Filesystem())->readFile($asset->sourcePath); $compiled = $this->compiler->compile($content, $asset); return $compiled !== $content ? $compiled : null; diff --git a/src/Symfony/Component/AssetMapper/Tests/Command/AssetMapperCompileCommandTest.php b/src/Symfony/Component/AssetMapper/Tests/Command/AssetMapperCompileCommandTest.php index ec7e3835b8a86..6482dcd71bc29 100644 --- a/src/Symfony/Component/AssetMapper/Tests/Command/AssetMapperCompileCommandTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/Command/AssetMapperCompileCommandTest.php @@ -48,10 +48,9 @@ public function testAssetsAreCompiled() $this->filesystem->remove($targetBuildDir); } // put old "built" versions to make sure the system skips using these - $this->filesystem->mkdir($targetBuildDir); - file_put_contents($targetBuildDir.'/manifest.json', '{}'); - file_put_contents($targetBuildDir.'/importmap.json', '{}'); - file_put_contents($targetBuildDir.'/entrypoint.file6.json', '[]'); + $this->filesystem->dumpFile($targetBuildDir.'/manifest.json', '{}'); + $this->filesystem->dumpFile($targetBuildDir.'/importmap.json', '{}'); + $this->filesystem->dumpFile($targetBuildDir.'/entrypoint.file6.json', '[]'); $command = $application->find('asset-map:compile'); $tester = new CommandTester($command); @@ -65,7 +64,7 @@ public function testAssetsAreCompiled() import '../file4.js'; console.log('file5.js'); - EOF, file_get_contents($targetBuildDir.'/subdir/file5-f4fdc37375c7f5f2629c5659a0579967.js')); + EOF, $this->filesystem->readFile($targetBuildDir.'/subdir/file5-f4fdc37375c7f5f2629c5659a0579967.js')); $finder = new Finder(); $finder->in($targetBuildDir)->files(); @@ -83,10 +82,10 @@ public function testAssetsAreCompiled() 'vendor/@hotwired/stimulus/stimulus.index.js', 'vendor/lodash/lodash.index.js', 'voilĂ .css', - ], array_keys(json_decode(file_get_contents($targetBuildDir.'/manifest.json'), true))); + ], array_keys(json_decode($this->filesystem->readFile($targetBuildDir.'/manifest.json'), true))); $this->assertFileExists($targetBuildDir.'/importmap.json'); - $actualImportMap = json_decode(file_get_contents($targetBuildDir.'/importmap.json'), true); + $actualImportMap = json_decode($this->filesystem->readFile($targetBuildDir.'/importmap.json'), true); $this->assertSame([ '@hotwired/stimulus', // in importmap 'lodash', // in importmap @@ -102,7 +101,7 @@ public function testAssetsAreCompiled() $this->assertSame('js', $actualImportMap['@hotwired/stimulus']['type']); $this->assertFileExists($targetBuildDir.'/entrypoint.file6.json'); - $entrypointData = json_decode(file_get_contents($targetBuildDir.'/entrypoint.file6.json'), true); + $entrypointData = json_decode($this->filesystem->readFile($targetBuildDir.'/entrypoint.file6.json'), true); $this->assertSame([ '/assets/subdir/file5.js', '/assets/file4.js', diff --git a/src/Symfony/Component/AssetMapper/Tests/CompiledAssetMapperConfigReaderTest.php b/src/Symfony/Component/AssetMapper/Tests/CompiledAssetMapperConfigReaderTest.php index 355bf838e1c25..4f36b2310b202 100644 --- a/src/Symfony/Component/AssetMapper/Tests/CompiledAssetMapperConfigReaderTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/CompiledAssetMapperConfigReaderTest.php @@ -55,7 +55,7 @@ public function testSaveConfig() { $reader = new CompiledAssetMapperConfigReader($this->writableRoot); $this->assertEquals($this->writableRoot.\DIRECTORY_SEPARATOR.'foo.json', realpath($reader->saveConfig('foo.json', ['foo' => 'bar']))); - $this->assertEquals(['foo' => 'bar'], json_decode(file_get_contents($this->writableRoot.'/foo.json'), true)); + $this->assertEquals(['foo' => 'bar'], json_decode($this->filesystem->readFile($this->writableRoot.'/foo.json'), true)); } public function testRemoveConfig() diff --git a/src/Symfony/Component/AssetMapper/Tests/Factory/CachedMappedAssetFactoryTest.php b/src/Symfony/Component/AssetMapper/Tests/Factory/CachedMappedAssetFactoryTest.php index 62a37fe837b95..82337d089a688 100644 --- a/src/Symfony/Component/AssetMapper/Tests/Factory/CachedMappedAssetFactoryTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/Factory/CachedMappedAssetFactoryTest.php @@ -131,7 +131,7 @@ private function loadConfigCacheMetadataFor(MappedAsset $mappedAsset): array { $cachedPath = $this->getConfigCachePath($mappedAsset).'.meta'; - return unserialize(file_get_contents($cachedPath)); + return unserialize($this->filesystem->readFile($cachedPath)); } private function saveConfigCache(MappedAsset $mappedAsset): void diff --git a/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageDownloaderTest.php b/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageDownloaderTest.php index e3e8cff663894..7bf20c6829757 100644 --- a/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageDownloaderTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageDownloaderTest.php @@ -79,12 +79,12 @@ public function testDownloadPackagesDownloadsEverythingWithNoInstalled() $this->assertFileExists(self::$writableRoot.'/assets/vendor/foo/foo.index.js'); $this->assertFileExists(self::$writableRoot.'/assets/vendor/bar.js/file.js'); $this->assertFileExists(self::$writableRoot.'/assets/vendor/baz/baz.index.css'); - $this->assertEquals('foo content', file_get_contents(self::$writableRoot.'/assets/vendor/foo/foo.index.js')); + $this->assertEquals('foo content', $this->filesystem->readFile(self::$writableRoot.'/assets/vendor/foo/foo.index.js')); $this->assertFileExists(self::$writableRoot.'/assets/vendor/foo/path/to/extra-file.woff'); - $this->assertEquals('extra file contents', file_get_contents(self::$writableRoot.'/assets/vendor/foo/path/to/extra-file.woff')); - $this->assertEquals('bar content', file_get_contents(self::$writableRoot.'/assets/vendor/bar.js/file.js')); - $this->assertEquals('baz content', file_get_contents(self::$writableRoot.'/assets/vendor/baz/baz.index.css')); - $this->assertEquals('different content', file_get_contents(self::$writableRoot.'/assets/vendor/custom_specifier/custom_specifier.index.js')); + $this->assertEquals('extra file contents', $this->filesystem->readFile(self::$writableRoot.'/assets/vendor/foo/path/to/extra-file.woff')); + $this->assertEquals('bar content', $this->filesystem->readFile(self::$writableRoot.'/assets/vendor/bar.js/file.js')); + $this->assertEquals('baz content', $this->filesystem->readFile(self::$writableRoot.'/assets/vendor/baz/baz.index.css')); + $this->assertEquals('different content', $this->filesystem->readFile(self::$writableRoot.'/assets/vendor/custom_specifier/custom_specifier.index.js')); $installed = require self::$writableRoot.'/assets/vendor/installed.php'; $this->assertEquals( @@ -150,9 +150,9 @@ public function testPackagesWithCorrectInstalledVersionSkipped() $this->assertFileExists(self::$writableRoot.'/assets/vendor/foo/foo.index.js'); $this->assertFileExists(self::$writableRoot.'/assets/vendor/bar.js/file.js'); $this->assertFileExists(self::$writableRoot.'/assets/vendor/baz/baz.index.css'); - $this->assertEquals('original foo content', file_get_contents(self::$writableRoot.'/assets/vendor/foo/foo.index.js')); - $this->assertEquals('new bar content', file_get_contents(self::$writableRoot.'/assets/vendor/bar.js/file.js')); - $this->assertEquals('new baz content', file_get_contents(self::$writableRoot.'/assets/vendor/baz/baz.index.css')); + $this->assertEquals('original foo content', $this->filesystem->readFile(self::$writableRoot.'/assets/vendor/foo/foo.index.js')); + $this->assertEquals('new bar content', $this->filesystem->readFile(self::$writableRoot.'/assets/vendor/bar.js/file.js')); + $this->assertEquals('new baz content', $this->filesystem->readFile(self::$writableRoot.'/assets/vendor/baz/baz.index.css')); $this->assertFileExists(self::$writableRoot.'/assets/vendor/has-missing-extra/has-missing-extra.index.js'); $installed = require self::$writableRoot.'/assets/vendor/installed.php'; diff --git a/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageStorageTest.php b/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageStorageTest.php index 4d41f4b61ce1f..3d390475486d9 100644 --- a/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageStorageTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageStorageTest.php @@ -70,7 +70,7 @@ public function testSave() $storage->save($entry, 'any content'); $targetPath = self::$writableRoot.'/assets/vendor/module_specifier/module_specifier.index.js'; $this->assertFileExists($targetPath); - $this->assertEquals('any content', file_get_contents($targetPath)); + $this->assertEquals('any content', $this->filesystem->readFile($targetPath)); } public function testSaveExtraFile() @@ -80,7 +80,7 @@ public function testSaveExtraFile() $storage->saveExtraFile($entry, '/path/to/extra-file.woff2', 'any content'); $targetPath = self::$writableRoot.'/assets/vendor/module_specifier/path/to/extra-file.woff2'; $this->assertFileExists($targetPath); - $this->assertEquals('any content', file_get_contents($targetPath)); + $this->assertEquals('any content', $this->filesystem->readFile($targetPath)); } /** diff --git a/src/Symfony/Component/AssetMapper/Tests/Path/LocalPublicAssetsFilesystemTest.php b/src/Symfony/Component/AssetMapper/Tests/Path/LocalPublicAssetsFilesystemTest.php index 4363ccbf577a8..7337b694697a0 100644 --- a/src/Symfony/Component/AssetMapper/Tests/Path/LocalPublicAssetsFilesystemTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/Path/LocalPublicAssetsFilesystemTest.php @@ -38,7 +38,7 @@ public function testWrite() $filesystem = new LocalPublicAssetsFilesystem(self::$writableRoot); $filesystem->write('foo/bar.js', 'foobar'); $this->assertFileExists(self::$writableRoot.'/foo/bar.js'); - $this->assertSame('foobar', file_get_contents(self::$writableRoot.'/foo/bar.js')); + $this->assertSame('foobar', $this->filesystem->readFile(self::$writableRoot.'/foo/bar.js')); // with a directory $filesystem->write('foo/baz/bar.js', 'foobar'); @@ -50,6 +50,6 @@ public function testCopy() $filesystem = new LocalPublicAssetsFilesystem(self::$writableRoot); $filesystem->copy(__DIR__.'/../Fixtures/importmaps/assets/pizza/index.js', 'foo/bar.js'); $this->assertFileExists(self::$writableRoot.'/foo/bar.js'); - $this->assertSame("console.log('pizza/index.js');", trim(file_get_contents(self::$writableRoot.'/foo/bar.js'))); + $this->assertSame("console.log('pizza/index.js');", trim($this->filesystem->readFile(self::$writableRoot.'/foo/bar.js'))); } } diff --git a/src/Symfony/Component/AssetMapper/composer.json b/src/Symfony/Component/AssetMapper/composer.json index 11f3786a40727..4db41cfaa4651 100644 --- a/src/Symfony/Component/AssetMapper/composer.json +++ b/src/Symfony/Component/AssetMapper/composer.json @@ -19,7 +19,7 @@ "php": ">=8.2", "composer/semver": "^3.0", "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^6.4|^7.0", + "symfony/filesystem": "^7.1", "symfony/http-client": "^6.4|^7.0" }, "require-dev": { diff --git a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php index 43a53f931b788..b7307489c0b88 100644 --- a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php +++ b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php @@ -134,7 +134,7 @@ public function write(string $content, ?array $metadata = null): void private function safelyUnserialize(string $file): mixed { $meta = false; - $content = file_get_contents($file); + $content = (new Filesystem())->readFile($file); $signalingException = new \UnexpectedValueException(); $prevUnserializeHandler = ini_set('unserialize_callback_func', self::class.'::handleUnserializeCallback'); $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) { diff --git a/src/Symfony/Component/Config/Util/XmlUtils.php b/src/Symfony/Component/Config/Util/XmlUtils.php index d86a5823ad994..a7a43555b8509 100644 --- a/src/Symfony/Component/Config/Util/XmlUtils.php +++ b/src/Symfony/Component/Config/Util/XmlUtils.php @@ -13,6 +13,7 @@ use Symfony\Component\Config\Util\Exception\InvalidXmlException; use Symfony\Component\Config\Util\Exception\XmlParsingException; +use Symfony\Component\Filesystem\Filesystem; /** * XMLUtils is a bunch of utility methods to XML operations. @@ -79,7 +80,7 @@ public static function parse(string $content, string|callable|null $schemaOrCall $valid = false; } } elseif (is_file($schemaOrCallable)) { - $schemaSource = file_get_contents((string) $schemaOrCallable); + $schemaSource = (new Filesystem())->readFile((string) $schemaOrCallable); $valid = @$dom->schemaValidateSource($schemaSource); } else { libxml_use_internal_errors($internalErrors); @@ -122,7 +123,7 @@ public static function loadFile(string $file, string|callable|null $schemaOrCall throw new \InvalidArgumentException(sprintf('File "%s" is not readable.', $file)); } - $content = @file_get_contents($file); + $content = (new Filesystem())->readFile($file); if ('' === trim($content)) { throw new \InvalidArgumentException(sprintf('File "%s" does not contain valid XML, it is empty.', $file)); diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index 47adca28de845..37206042aa8b0 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^6.4|^7.0", + "symfony/filesystem": "^7.1", "symfony/polyfill-ctype": "~1.8" }, "require-dev": { diff --git a/src/Symfony/Component/Emoji/Resources/bin/build.php b/src/Symfony/Component/Emoji/Resources/bin/build.php index 088838cdd0466..04dfd422ef396 100755 --- a/src/Symfony/Component/Emoji/Resources/bin/build.php +++ b/src/Symfony/Component/Emoji/Resources/bin/build.php @@ -56,6 +56,7 @@ public static function getEmojisCodePoints(): array public static function buildRules(array $emojisCodePoints): Generator { + $filesystem = new Filesystem(); $files = (new Finder()) ->files() ->in([ @@ -74,7 +75,7 @@ public static function buildRules(array $emojisCodePoints): Generator $mapsByLocale[$locale] ??= []; $document = new DOMDocument(); - $document->loadXML(file_get_contents($file)); + $document->loadXML($filesystem->readFile($file)); $xpath = new DOMXPath($document); $results = $xpath->query('.//annotation[@type="tts"]'); @@ -128,7 +129,7 @@ public static function buildRules(array $emojisCodePoints): Generator public static function buildGitHubRules(array $emojisCodePoints): iterable { - $emojis = json_decode(file_get_contents(__DIR__.'/vendor/github-emojis.json'), true); + $emojis = json_decode((new Filesystem())->readFile(__DIR__.'/vendor/github-emojis.json'), true, flags: JSON_THROW_ON_ERROR); $ignored = []; $maps = []; @@ -157,7 +158,7 @@ public static function buildGitHubRules(array $emojisCodePoints): iterable public static function buildSlackRules(array $emojisCodePoints): iterable { - $emojis = json_decode(file_get_contents(__DIR__.'/vendor/slack-emojis.json'), true); + $emojis = json_decode((new Filesystem())->readFile(__DIR__.'/vendor/slack-emojis.json'), true, flags: JSON_THROW_ON_ERROR); $ignored = []; $emojiSlackMaps = []; @@ -213,9 +214,10 @@ public static function cleanTarget(): void public static function saveRules(iterable $rulesByLocale): void { + $fs = new Filesystem(); $firstChars = []; foreach ($rulesByLocale as $filename => $rules) { - file_put_contents(self::TARGET_DIR."/$filename.php", "dumpFile(self::TARGET_DIR."/$filename.php", " $v) { if (!str_starts_with($filename, 'emoji-')) { @@ -234,7 +236,7 @@ public static function saveRules(iterable $rulesByLocale): void $quickCheck = '"'.str_replace('%', '\\x', rawurlencode(implode('', $firstChars))).'"'; $file = dirname(__DIR__, 2).'/EmojiTransliterator.php'; - file_put_contents($file, preg_replace('/QUICK_CHECK = .*;/m', "QUICK_CHECK = {$quickCheck};", file_get_contents($file))); + $fs->dumpFile($file, preg_replace('/QUICK_CHECK = .*;/m', "QUICK_CHECK = {$quickCheck};", $fs->readFile($file))); } private static function testEmoji(string $emoji, string $locale, string $codePoints): bool diff --git a/src/Symfony/Component/Emoji/composer.json b/src/Symfony/Component/Emoji/composer.json index c4997c1b32b92..0c3da1c74c041 100644 --- a/src/Symfony/Component/Emoji/composer.json +++ b/src/Symfony/Component/Emoji/composer.json @@ -20,7 +20,7 @@ "ext-intl": "*" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0", + "symfony/filesystem": "^7.1", "symfony/finder": "^6.4|^7.0", "symfony/var-exporter": "^6.4|^7.0" }, diff --git a/src/Symfony/Component/Filesystem/CHANGELOG.md b/src/Symfony/Component/Filesystem/CHANGELOG.md index b4bd22eb5eb85..80818d1b58374 100644 --- a/src/Symfony/Component/Filesystem/CHANGELOG.md +++ b/src/Symfony/Component/Filesystem/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.1 +--- + + * Add the `Filesystem::readFile()` method + 7.0 --- diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 81b508b1e32a8..42e64630e4d15 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -694,6 +694,25 @@ public function appendToFile(string $filename, $content, bool $lock = false): vo } } + /** + * Returns the content of a file as a string. + * + * @throws IOException If the file cannot be read + */ + public function readFile(string $filename): string + { + if (is_dir($filename)) { + throw new IOException(sprintf('Failed to read file "%s": File is a directory.', $filename)); + } + + $content = self::box('file_get_contents', $filename); + if (false === $content) { + throw new IOException(sprintf('Failed to read file "%s": ', $filename).self::$lastError, 0, null, $filename); + } + + return $content; + } + private function toIterable(string|iterable $files): iterable { return is_iterable($files) ? $files : [$files]; diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index dc7e74c849918..a1b3a666138ca 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1802,6 +1802,43 @@ public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile() $this->assertFilePermissions(745, $filename); } + public function testReadFile() + { + $licenseFile = \dirname(__DIR__).'/LICENSE'; + + $this->assertStringEqualsFile($licenseFile, $this->filesystem->readFile($licenseFile)); + } + + public function testReadNonExistentFile() + { + $this->expectException(IOException::class); + $this->expectExceptionMessageMatches('#^Failed to read file ".+/Tests/invalid"\\: file_get_contents\\(.+/Tests/invalid\\)\\: Failed to open stream\\: No such file or directory$#'); + + $this->filesystem->readFile(__DIR__.'/invalid'); + } + + public function testReadDirectory() + { + $this->expectException(IOException::class); + $this->expectExceptionMessageMatches('#^Failed to read file ".+/Tests"\\: File is a directory\\.$#'); + + $this->filesystem->readFile(__DIR__); + } + + public function testReadUnreadableFile() + { + $this->markAsSkippedIfChmodIsMissing(); + + $filename = $this->workspace.'/unreadable.txt'; + file_put_contents($filename, 'Hello World'); + chmod($filename, 0o000); + + $this->expectException(IOException::class); + $this->expectExceptionMessageMatches('#^Failed to read file ".+/unreadable.txt"\\: file_get_contents\\(.+/unreadable.txt\\)\\: Failed to open stream\\: Permission denied$#'); + + $this->filesystem->readFile($filename); + } + public function testCopyShouldKeepExecutionPermission() { $this->markAsSkippedIfChmodIsMissing();