From 4a210ec7bc14903199fe5f4050e6f61734cf2112 Mon Sep 17 00:00:00 2001 From: insign <1113045+insign@users.noreply.github.com> Date: Sun, 29 Mar 2026 03:27:21 +0000 Subject: [PATCH] feat: Add `rename` method and fix PHP 8.4 deprecations - Implemented `rename(array $renames)` method to rename files inside archives using `7z rn` command. - Updated `SevenZipTest.php` with `testRename` validating successful operations (zip, 7z, tar) and exception handling for unsupported formats (bzip2). - Resolved implicit nullable parameter deprecations (`?Throwable $previous`, `?string $value`) in PHP 8.4 for `ExecutableNotFoundException` and `addFlag`. - Removed resolved issues from `phpstan-baseline.neon`. --- phpstan-baseline.neon | 12 ----- .../ExecutableNotFoundException.php | 2 +- src/SevenZip.php | 46 ++++++++++++++++++- tests/SevenZipTest.php | 37 +++++++++++++-- 4 files changed, 80 insertions(+), 17 deletions(-) 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..e61a683 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) { @@ -1463,6 +1463,50 @@ public function verify(): string return $this->runCommand($command); } + /** + * Renames files inside 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 if renames array is empty. + */ + 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 ($this->getPassword()) { + $this->addFlag("p", $this->getPassword(), glued: true); + } + + $renameArgs = []; + foreach ($renames as $old => $new) { + $renameArgs[] = $old; + $renameArgs[] = $new; + } + + $command = [ + $this->sevenZipPath, + "rn", + ...$this->flagrize($this->getAlwaysFlags()), + ...$this->flagrize($this->getCustomFlags()), + $this->getSourcePath(), + ...$renameArgs, + ]; + + return $this->runCommand($command); + } + public function getFlag(string $flag): mixed { return $this->customFlags[$flag] ?? null; diff --git a/tests/SevenZipTest.php b/tests/SevenZipTest.php index 824e221..98ff1fb 100644 --- a/tests/SevenZipTest.php +++ b/tests/SevenZipTest.php @@ -274,9 +274,34 @@ 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 ($format === 'bzip2') { + $this->expectException(\RuntimeException::class); + } + + $renames = [ + 'source/Avatart.svg' => 'source/Avatar_renomeado.svg', + 'source/js_interop_usage.md' => 'source/js_interop_usage_renomeado.md', + ]; + + $output = $this->sevenZip + ->source(path: $archive) + ->rename($renames); + + if ($format !== 'bzip2') { + $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,8 +312,14 @@ public function testExtract(string $format): void ->source(path: $archive) ->target(path: $target) ->extract(); - $this->assertFileExists(filename: $target . '/source/Avatart.svg'); - $this->assertFileExists(filename: $target . '/source/js_interop_usage.md'); + + if ($format !== 'bzip2') { + $this->assertFileExists(filename: $target . '/source/Avatar_renomeado.svg'); + $this->assertFileExists(filename: $target . '/source/js_interop_usage_renomeado.md'); + } else { + $this->assertFileExists(filename: $target . '/source/Avatart.svg'); + $this->assertFileExists(filename: $target . '/source/js_interop_usage.md'); + } unlink($archive); }