From b54885e3ca38e3e340d9e4cad4d2cc1973749896 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sun, 1 Mar 2026 13:52:36 +0100 Subject: [PATCH] [TASK] Upgrade league/flysystem from v1 to v3 Upgrade league/flysystem from v1 (1.x) to v3 (3.x): - Replace league/flysystem ^1.1.10 with ^3.29 - Add league/flysystem-local ^3.0 dependency - Replace Flysystem v1 API (put/putStream/has/read/readStream) with v3 equivalents (write/writeStream/fileExists/read/readStream) - Update CopyResources to use FilesystemException with try/catch/finally for proper error handling and stream cleanup - Update composer.lock (flysystem 3.32.0, flysystem-local 3.31.0, flyfinder 2.0.0) Signed-off-by: Sebastian Mendel --- composer.json | 2 +- composer.lock | 157 +++++++++++------- .../src/Directives/IncludeDirective.php | 9 - .../Directives/SiteSetSettingsDirective.php | 1 - .../src/EventListeners/CopyResources.php | 35 ++-- .../src/Twig/TwigExtension.php | 4 +- .../site-set-failure/expected/logs/error.log | 2 +- .../expected/logs/warning.log | 2 +- 8 files changed, 124 insertions(+), 88 deletions(-) diff --git a/composer.json b/composer.json index ee2f8844f..ffe86093a 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "ext-dom": "*", "ext-libxml": "*", "brotkrueml/twig-codehighlight": "^1.0", - "league/flysystem": "^1.1.10", + "league/flysystem": "^3.29", "phpdocumentor/dev-server": "^1.9.4", "phpdocumentor/filesystem": "^1.9", "phpdocumentor/guides": "^1.9", diff --git a/composer.lock b/composer.lock index 0dc5bbbfa..99dcd569c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a539658a6108a63f5757061bda1439ce", + "content-hash": "6bc548c131c804a8eba2d57af4be3cae", "packages": [ { "name": "brotkrueml/twig-codehighlight", @@ -876,54 +876,55 @@ }, { "name": "league/flysystem", - "version": "1.1.10", + "version": "3.32.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1" + "reference": "254b1595b16b22dbddaaef9ed6ca9fdac4956725" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1", - "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/254b1595b16b22dbddaaef9ed6ca9fdac4956725", + "reference": "254b1595b16b22dbddaaef9ed6ca9fdac4956725", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "league/mime-type-detection": "^1.3", - "php": "^7.2.5 || ^8.0" + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" }, "conflict": { - "league/flysystem-sftp": "<1.0.6" + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" }, "require-dev": { - "phpspec/prophecy": "^1.11.1", - "phpunit/phpunit": "^8.5.8" - }, - "suggest": { - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, "autoload": { "psr-4": { - "League\\Flysystem\\": "src/" + "League\\Flysystem\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -933,40 +934,77 @@ "authors": [ { "name": "Frank de Jonge", - "email": "info@frenky.net" + "email": "info@frankdejonge.nl" } ], - "description": "Filesystem abstraction: Many filesystems, one API.", + "description": "File storage abstraction for PHP", "keywords": [ - "Cloud Files", "WebDAV", - "abstraction", "aws", "cloud", - "copy.com", - "dropbox", - "file systems", + "file", "files", "filesystem", "filesystems", "ftp", - "rackspace", - "remote", "s3", "sftp", "storage" ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/1.1.10" + "source": "https://github.com/thephpleague/flysystem/tree/3.32.0" }, - "funding": [ + "time": "2026-02-25T17:01:41+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.31.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://offset.earth/frankdejonge", - "type": "other" + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" } ], - "time": "2022-10-04T09:16:37+00:00" + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" + }, + "time": "2026-01-23T15:30:45+00:00" }, { "name": "league/mime-type-detection", @@ -1675,30 +1713,35 @@ }, { "name": "phpdocumentor/flyfinder", - "version": "1.1.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/FlyFinder.git", - "reference": "6e145e676d9fbade7527fd8d4c99ab36b687b958" + "reference": "92f5c407f63952fb66a6bb8c0489935b9c605888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/FlyFinder/zipball/6e145e676d9fbade7527fd8d4c99ab36b687b958", - "reference": "6e145e676d9fbade7527fd8d4c99ab36b687b958", + "url": "https://api.github.com/repos/phpDocumentor/FlyFinder/zipball/92f5c407f63952fb66a6bb8c0489935b9c605888", + "reference": "92f5c407f63952fb66a6bb8c0489935b9c605888", "shasum": "" }, "require": { - "league/flysystem": "^1.0", - "php": "^7.2||^8.0" + "league/flysystem": "^3.0", + "php": "^8.0||^8.1||^8.2||^8.3" }, "require-dev": { - "league/flysystem-memory": "~1", - "mockery/mockery": "^1.3" + "league/flysystem-memory": "~3", + "mockery/mockery": "^1.4", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^1.12", + "phpstan/phpstan-phpunit": "^1.4", + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "^5.26" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -1720,9 +1763,9 @@ ], "support": { "issues": "https://github.com/phpDocumentor/FlyFinder/issues", - "source": "https://github.com/phpDocumentor/FlyFinder/tree/1.1.0" + "source": "https://github.com/phpDocumentor/FlyFinder/tree/2.0.0" }, - "time": "2021-06-04T13:44:40+00:00" + "time": "2024-09-15T19:58:36+00:00" }, { "name": "phpdocumentor/guides", diff --git a/packages/typo3-docs-theme/src/Directives/IncludeDirective.php b/packages/typo3-docs-theme/src/Directives/IncludeDirective.php index 47cc5bb62..1e7845672 100644 --- a/packages/typo3-docs-theme/src/Directives/IncludeDirective.php +++ b/packages/typo3-docs-theme/src/Directives/IncludeDirective.php @@ -54,9 +54,6 @@ public function processNode( } - /** - * @throws \League\Flysystem\FileNotFoundException - */ public function resolveGlobInclude(BlockContext $blockContext, string $inputPath, Directive $directive): LiteralBlockNode|CollectionNode|CodeNode { $parserContext = $blockContext->getDocumentParserContext()->getParser()->getParserContext(); @@ -103,9 +100,6 @@ public function resolveGlobInclude(BlockContext $blockContext, string $inputPath return new CollectionNode($nodes); } - /** - * @throws \League\Flysystem\FileNotFoundException - */ public function resolveBasicInclude(BlockContext $blockContext, string $inputPath, Directive $directive): LiteralBlockNode|CollectionNode|CodeNode { $parserContext = $blockContext->getDocumentParserContext()->getParser()->getParserContext(); @@ -121,9 +115,6 @@ public function resolveBasicInclude(BlockContext $blockContext, string $inputPat return $this->getCollectionFromPath($origin, $path, $directive, $blockContext); } - /** - * @throws \League\Flysystem\FileNotFoundException - */ public function getCollectionFromPath(\League\Flysystem\FilesystemInterface|\phpDocumentor\FileSystem\FileSystem $origin, string $path, Directive $directive, BlockContext $blockContext): LiteralBlockNode|CollectionNode|CodeNode { $contents = $origin->read($path); diff --git a/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php b/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php index cda78f18e..68e22e9fb 100644 --- a/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php +++ b/packages/typo3-docs-theme/src/Directives/SiteSetSettingsDirective.php @@ -126,7 +126,6 @@ public function processNode( } /** - * @throws \League\Flysystem\FileNotFoundException * @throws FileLoadingException */ public function loadFileFromDocumentation(BlockContext $blockContext, string $filename): string diff --git a/packages/typo3-docs-theme/src/EventListeners/CopyResources.php b/packages/typo3-docs-theme/src/EventListeners/CopyResources.php index 093eb77fa..6abc9d455 100644 --- a/packages/typo3-docs-theme/src/EventListeners/CopyResources.php +++ b/packages/typo3-docs-theme/src/EventListeners/CopyResources.php @@ -4,8 +4,9 @@ namespace T3Docs\Typo3DocsTheme\EventListeners; -use League\Flysystem\Adapter\Local; +use League\Flysystem\FilesystemException; use League\Flysystem\Filesystem; +use League\Flysystem\Local\LocalFilesystemAdapter; use phpDocumentor\Guides\Event\PostRenderProcess; use Psr\Log\LoggerInterface; use Symfony\Component\Finder\Finder; @@ -35,29 +36,31 @@ public function __invoke(PostRenderProcess $event): void return; } - $source = new Filesystem(new Local($fullResourcesPath)); + $source = new Filesystem(new LocalFilesystemAdapter($fullResourcesPath)); - /** @var \League\Flysystem\FilesystemInterface */ $destination = $event->getCommand()->getDestination(); $finder = new Finder(); $finder->files()->in($fullResourcesPath); foreach ($finder as $file) { - $stream = $source->readStream($file->getRelativePathname()); - if ($stream === false) { - $this->logger->warning(sprintf('Cannot read stream from "%s"', $file->getRealPath())); - continue; + $stream = null; + try { + $stream = $source->readStream($file->getRelativePathname()); + $destinationPath = sprintf( + '%s/%s%s', + self::DESTINATION_PATH, + $file->getRelativePath() !== '' ? $file->getRelativePath() . '/' : '', + $file->getFilename() + ); + $destination->putStream($destinationPath, $stream); + } catch (FilesystemException $e) { + $this->logger->warning(sprintf('Cannot copy resource "%s": %s', $file->getRealPath(), $e->getMessage())); + } finally { + if (is_resource($stream)) { + fclose($stream); + } } - - $destinationPath = sprintf( - '%s/%s%s', - self::DESTINATION_PATH, - $file->getRelativePath() !== '' ? $file->getRelativePath() . '/' : '', - $file->getFilename() - ); - $destination->putStream($destinationPath, $stream); - is_resource($stream) && fclose($stream); } } } diff --git a/packages/typo3-docs-theme/src/Twig/TwigExtension.php b/packages/typo3-docs-theme/src/Twig/TwigExtension.php index e3cc2635b..3b81facc4 100644 --- a/packages/typo3-docs-theme/src/Twig/TwigExtension.php +++ b/packages/typo3-docs-theme/src/Twig/TwigExtension.php @@ -4,7 +4,7 @@ namespace T3Docs\Typo3DocsTheme\Twig; -use League\Flysystem\Exception; +use League\Flysystem\FilesystemException; use LogicException; use phpDocumentor\Guides\Nodes\AnchorNode; use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; @@ -617,7 +617,7 @@ private function copyAsset( $renderContext->getLoggerInformation(), ); } - } catch (LogicException|Exception $e) { + } catch (LogicException|FilesystemException $e) { $this->logger->error( sprintf('Unable to write file "%s", %s', $outputPath, $e->getMessage()), $renderContext->getLoggerInformation(), diff --git a/tests/Integration/tests/site-set-failure/expected/logs/error.log b/tests/Integration/tests/site-set-failure/expected/logs/error.log index 03f8548ca..2cff43b58 100644 --- a/tests/Integration/tests/site-set-failure/expected/logs/error.log +++ b/tests/Integration/tests/site-set-failure/expected/logs/error.log @@ -1 +1 @@ -app.ERROR: Error while processing "typo3:site-set-settings" directive in "index": Path is outside of the defined root, path: [/../../../tests/Integration/tests/site-set/input/_includes/Sets/FluidStyledContent/settings.definitions.yaml] {"rst-file":"index.rst","currentLineNumber":1} [] +app.ERROR: Error while processing "typo3:site-set-settings" directive in "index": Path traversal detected: /../../../tests/Integration/tests/site-set/input/_includes/Sets/FluidStyledContent/settings.definitions.yaml {"rst-file":"index.rst","currentLineNumber":1} [] diff --git a/tests/Integration/tests/site-set-failure/expected/logs/warning.log b/tests/Integration/tests/site-set-failure/expected/logs/warning.log index 03f8548ca..2cff43b58 100644 --- a/tests/Integration/tests/site-set-failure/expected/logs/warning.log +++ b/tests/Integration/tests/site-set-failure/expected/logs/warning.log @@ -1 +1 @@ -app.ERROR: Error while processing "typo3:site-set-settings" directive in "index": Path is outside of the defined root, path: [/../../../tests/Integration/tests/site-set/input/_includes/Sets/FluidStyledContent/settings.definitions.yaml] {"rst-file":"index.rst","currentLineNumber":1} [] +app.ERROR: Error while processing "typo3:site-set-settings" directive in "index": Path traversal detected: /../../../tests/Integration/tests/site-set/input/_includes/Sets/FluidStyledContent/settings.definitions.yaml {"rst-file":"index.rst","currentLineNumber":1} []