From 032120a5123b3eb41db85c320530bd2b1817a3e6 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 03:18:14 +0000 Subject: [PATCH 1/2] feat: Add `rename` method to `SevenZip` wrapper Added a new `rename` method to the `SevenZip` class allowing the renaming of files within an existing archive via the `7z rn` command. The implementation includes: - Validation checks for required source path and empty renames arrays. - Graceful handling of unsupported formats (e.g. `bzip2`) by throwing a `RuntimeException`. - Addition of a new `testRename` test in `tests/SevenZipTest.php`, integrated properly with the `#[Depends]` test flow (executed between verify and extract). - Addition of API documentation for the `rename` method to the `README.md` file. - Fixed two implicit nullable parameters deprecated in PHP 8.4 and updated `phpstan-baseline.neon`. Co-authored-by: insign <1113045+insign@users.noreply.github.com> --- README.md | 22 ++++++++ phpstan-baseline.neon | 12 ----- .../ExecutableNotFoundException.php | 2 +- src/SevenZip.php | 53 ++++++++++++++++++- tests/SevenZipTest.php | 34 +++++++++++- 5 files changed, 107 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index fc1d5ce..c435f2d 100644 --- a/README.md +++ b/README.md @@ -643,6 +643,28 @@ Removes a compression flag. **Returns**: The current instance of the SevenZip class. +### `rename(array $renames): string` + +Renames files within an archive. Note that the rename operation is not natively supported for `bzip2` archives. + +**Parameters** + +- `$renames`: An associative array mapping old file names to new file names. + +**Returns**: The output of the 7-Zip command. + +**Throws** + +- `InvalidArgumentException`: If the archive path is not set or the renames array is empty. +- `RuntimeException`: If the format is not supported (e.g., `bzip2`). + +**Example** + +```php +$sevenZip->source('/path/to/archive.7z') + ->rename(['old_name.txt' => 'new_name.txt']); +``` + ### `reset(): SevenZip` Resets the property values to their original state. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 04a5ea4..470db3b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,17 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Deprecated in PHP 8\.4\: Parameter \#3 \$previous \(Throwable\) is implicitly nullable via default value null\.$#' - identifier: parameter.implicitlyNullable - count: 1 - path: src/Exceptions/ExecutableNotFoundException.php - - - - message: '#^Deprecated in PHP 8\.4\: Parameter \#2 \$value \(string\) is implicitly nullable via default value null\.$#' - identifier: parameter.implicitlyNullable - count: 1 - path: src/SevenZip.php - - message: '#^Method Verseles\\SevenZip\\SevenZip\:\:addFlag\(\) should return \$this\(Verseles\\SevenZip\\SevenZip\) but returns Verseles\\SevenZip\\SevenZip\.$#' identifier: return.type diff --git a/src/Exceptions/ExecutableNotFoundException.php b/src/Exceptions/ExecutableNotFoundException.php index 3b1c30b..131bad2 100644 --- a/src/Exceptions/ExecutableNotFoundException.php +++ b/src/Exceptions/ExecutableNotFoundException.php @@ -6,7 +6,7 @@ class ExecutableNotFoundException extends \Exception { - public function __construct($message = 'Executable 7z not found.', $code = 0, Throwable $previous = null) + public function __construct($message = 'Executable 7z not found.', $code = 0, ?Throwable $previous = null) { parent::__construct($message, $code, $previous); } diff --git a/src/SevenZip.php b/src/SevenZip.php index 67eecbb..4cf2f2d 100644 --- a/src/SevenZip.php +++ b/src/SevenZip.php @@ -315,7 +315,7 @@ public function mmt(int|bool|string $threads = "on"): self */ public function addFlag( string $flag, - string $value = null, + ?string $value = null, bool $glued = false, ): self { if ($glued && $value !== null) { @@ -1086,6 +1086,57 @@ public function fileInfo(): array return $this->parseFileInfoOutput($output); } + /** + * Rename files in an archive. + * + * @param array $renames An associative array mapping old file names to new file names. + * + * @return string The output of the 7-Zip command. + * @throws \InvalidArgumentException If source path is not set or the renames array is empty. + * @throws \RuntimeException If the archive format is unsupported. + */ + public function rename(array $renames): string + { + if (!$this->getSourcePath()) { + throw new \InvalidArgumentException( + "Archive file path (source) must be set", + ); + } + + if (empty($renames)) { + throw new \InvalidArgumentException( + "Renames array cannot be empty", + ); + } + + if (in_array($this->getFormat(), ['bzip2', 'bz2'])) { + throw new \RuntimeException( + "Rename operation is not supported for bzip2 archives", + ); + } + + if ($this->getPassword()) { + $this->addFlag("p", $this->getPassword(), glued: true); + } + + $renameArgs = []; + foreach ($renames as $oldName => $newName) { + $renameArgs[] = $oldName; + $renameArgs[] = $newName; + } + + $command = [ + $this->sevenZipPath, + "rn", + ...$this->flagrize($this->getAlwaysFlags()), + ...$this->flagrize($this->getCustomFlags()), + $this->getSourcePath(), + ...$renameArgs, + ]; + + return $this->runCommand($command); + } + /** * Test the integrity of an archive. * diff --git a/tests/SevenZipTest.php b/tests/SevenZipTest.php index 824e221..7c9f800 100644 --- a/tests/SevenZipTest.php +++ b/tests/SevenZipTest.php @@ -274,9 +274,35 @@ public function testVerify(string $format): void $this->assertStringContainsString('Everything is Ok', $output); } - #[Covers('\Verseles\SevenZip\SevenZip::extract')] + #[Covers('\Verseles\SevenZip\SevenZip::rename')] #[DataProvider('compressAndExtractDataProvider')] #[Depends('testVerify')] + public function testRename(string $format): void + { + $archive = $this->testDir . '/target/archive.' . $format; + + if (in_array($format, ['bzip2', 'bz2'])) { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Rename operation is not supported for bzip2 archives'); + } + + $renames = [ + 'source/Avatart.svg' => 'source/RenamedAvatar.svg' + ]; + + $output = $this->sevenZip + ->format($format) + ->source(path: $archive) + ->rename($renames); + + if (!in_array($format, ['bzip2', 'bz2'])) { + $this->assertStringContainsString('Everything is Ok', $output); + } + } + + #[Covers('\Verseles\SevenZip\SevenZip::extract')] + #[DataProvider('compressAndExtractDataProvider')] + #[Depends('testRename')] public function testExtract(string $format): void { $archive = $this->testDir . '/target/archive.' . $format; @@ -287,7 +313,11 @@ public function testExtract(string $format): void ->source(path: $archive) ->target(path: $target) ->extract(); - $this->assertFileExists(filename: $target . '/source/Avatart.svg'); + if (in_array($format, ['bzip2', 'bz2'])) { + $this->assertFileExists(filename: $target . '/source/Avatart.svg'); + } else { + $this->assertFileExists(filename: $target . '/source/RenamedAvatar.svg'); + } $this->assertFileExists(filename: $target . '/source/js_interop_usage.md'); unlink($archive); From 75147707b481a2a71d4d59bfda9fec7f8430ee33 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 03:51:57 +0000 Subject: [PATCH 2/2] fix: Enforce `bzip2` unsupported check using source file extension Updated the `rename` method to detect the archive format by extracting the file extension from the provided `source()` path. This ensures that a `RuntimeException` is properly thrown when attempting to rename within a `bzip2` archive, even if the user relies on the default `7z` format state without explicitly calling `->format('bzip2')`. Also updated the `testRename` test to verify this exact behavior by not setting the format manually prior to calling `rename()`. Co-authored-by: insign <1113045+insign@users.noreply.github.com> --- src/SevenZip.php | 4 +++- tests/SevenZipTest.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SevenZip.php b/src/SevenZip.php index 4cf2f2d..e13e876 100644 --- a/src/SevenZip.php +++ b/src/SevenZip.php @@ -1109,7 +1109,9 @@ public function rename(array $renames): string ); } - if (in_array($this->getFormat(), ['bzip2', 'bz2'])) { + $extension = strtolower(pathinfo($this->getSourcePath(), PATHINFO_EXTENSION)); + + if (in_array($this->getFormat(), ['bzip2', 'bz2']) || in_array($extension, ['bzip2', 'bz2'])) { throw new \RuntimeException( "Rename operation is not supported for bzip2 archives", ); diff --git a/tests/SevenZipTest.php b/tests/SevenZipTest.php index 7c9f800..c24a31f 100644 --- a/tests/SevenZipTest.php +++ b/tests/SevenZipTest.php @@ -290,8 +290,8 @@ public function testRename(string $format): void 'source/Avatart.svg' => 'source/RenamedAvatar.svg' ]; + // Do not set format explicitly so that we rely on source file extension for unsupported tests $output = $this->sevenZip - ->format($format) ->source(path: $archive) ->rename($renames);