Repository: spatie/laravel-backup (php)

Issue:
relative_path option includes all folders using full path
Using 7.5.2 of spatie/laravel-backup, with a vanilla Laravel 8 install.

Only thing I've changed in the backup.php config:
```
'relative_path' => base_path()
```
It does backup all the files using a [relative path](https://github.com/spatie/laravel-backup/pull/1158).
But: it also creates all the folders (without the files) with full path: /Users/path_to_source_code

E.g. when I `unzip 2021-04-26-14-45-37.zip`:
```
# files have been extracted, but also shows "Users" folder
ls -1
2021-04-26-14-45-37.zip	artisan			config			phpunit.xml		server.php
README.md		bootstrap		database		public			storage
Users			composer.json		db-dumps		resources		tests
app			composer.lock		package.json		routes			webpack.mix.js

# the "config" folder shows the expected files
$ ls config
app.php			broadcasting.php	database.php		logging.php		services.php
auth.php		cache.php		filesystems.php		mail.php		session.php
backup.php		cors.php		hashing.php		queue.php		view.php

# but under "Users" folder there also is a config folder
$ ls Users/path_to_source_code/
app		config		public		routes		tests
bootstrap	database	resources	storage

# but this folder is empty
$ ls Users/path_to_source_code/config
```

I extract using default unzip on MacOs (also tried p7zip).

Pull request:
Avoid create full path folders when config has relative_path
Try to fix 
Closes #1298

Patch:
diff --git a/src/Tasks/Backup/Zip.php b/src/Tasks/Backup/Zip.php
index a2f89e3..fa05b6e 100644
--- a/src/Tasks/Backup/Zip.php
+++ b/src/Tasks/Backup/Zip.php
@@ -16,12 +16,15 @@ class Zip
 
     public static function createForManifest(Manifest $manifest, string $pathToZip): self
     {
+        $relativePath = config('backup.backup.source.files.relative_path') ?
+            rtrim(config('backup.backup.source.files.relative_path'), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : false;
+
         $zip = new static($pathToZip);
 
         $zip->open();
 
         foreach ($manifest->files() as $file) {
-            $zip->add($file, self::determineNameOfFileInZip($file, $pathToZip));
+            $zip->add($file, self::determineNameOfFileInZip($file, $relativePath));
         }
 
         $zip->close();
@@ -29,20 +32,12 @@ class Zip
         return $zip;
     }
 
-    protected static function determineNameOfFileInZip(string $pathToFile, string $pathToZip)
+    protected static function determineNameOfFileInZip(string $pathToFile, string $relativePath)
     {
-        $zipDirectory = pathinfo($pathToZip, PATHINFO_DIRNAME);
-
-        $fileDirectory = pathinfo($pathToFile, PATHINFO_DIRNAME);
+        $fileDirectory = pathinfo($pathToFile, PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR;
 
-        if (Str::startsWith($fileDirectory, $zipDirectory)) {
-            return str_replace($zipDirectory, '', $pathToFile);
-        }
-
-        if ($relativePath = config('backup.backup.source.files.relative_path')) {
-            if (Str::startsWith($fileDirectory . '/', $relativePath)) {
-                return str_replace($relativePath, '', $pathToFile);
-            }
+        if ($relativePath && $relativePath != DIRECTORY_SEPARATOR && Str::startsWith($fileDirectory, $relativePath)) {
+            return str_replace($relativePath, '', $pathToFile);
         }
 
         return $pathToFile;
@@ -98,7 +93,7 @@ class Zip
 
         foreach ($files as $file) {
             if (is_dir($file)) {
-                $this->zipFile->addEmptyDir($file);
+                $this->zipFile->addEmptyDir(ltrim($nameInZip ?: $file, DIRECTORY_SEPARATOR));
             }
 
             if (is_file($file)) {


Test patch:
diff --git a/tests/Commands/BackupCommandTest.php b/tests/Commands/BackupCommandTest.php
index d5481e5..d1de1e9 100644
--- a/tests/Commands/BackupCommandTest.php
+++ b/tests/Commands/BackupCommandTest.php
@@ -9,6 +9,7 @@ use Illuminate\Support\Facades\Storage;
 use Spatie\Backup\Events\BackupHasFailed;
 use Spatie\Backup\Tests\TestCase;
 use Spatie\DbDumper\Compressors\GzipCompressor;
+use ZipArchive;
 
 class BackupCommandTest extends TestCase
 {
@@ -92,13 +93,41 @@ class BackupCommandTest extends TestCase
     /** @test */
     public function it_can_backup_using_relative_path()
     {
-        config()->set('backup.backup.source.files.include', [$this->getDiskRootPath('local')]);
-        config()->set('backup.backup.source.files.relative_path', $this->getDiskRootPath('local'));
-
-        Storage::disk('local')->put('testing-file.txt', 'dummy content');
+        config()->set('backup.backup.source.files.include', [$this->getStubDirectory()]);
+        config()->set('backup.backup.source.files.exclude', [$this->getStubDirectory('.dot'), $this->getStubDirectory('file'), $this->getStubDirectory('file1.txt.txt')]);
+        config()->set('backup.backup.source.files.relative_path', $this->getStubDirectory());
+
+        $testFiles = [
+            '.dotfile',
+            'archive.zip',
+            '1Mb.file',
+            'directory1/',
+            'directory1/directory1/',
+            'directory1/directory1/file1.txt',
+            'directory1/directory1/file2.txt',
+            'directory1/file1.txt',
+            'directory1/file2.txt',
+            'directory2/',
+            'directory2/directory1/',
+            'directory2/directory1/file1.txt',
+            'file1.txt',
+            'file2.txt',
+            'file3.txt',
+        ];
 
         $this->artisan('backup:run --only-files')->assertExitCode(0);
-        $this->assertFileExistsInZip('local', $this->expectedZipPath, 'testing-file.txt');
+
+        $zipFiles = [];
+        $zip = new ZipArchive();
+        $zip->open(Storage::disk('local')->path($this->expectedZipPath));
+        foreach (range(0, $zip->numFiles - 1) as $i) {
+            $zipFiles[] = $zip->statIndex($i)['name'];
+        }
+        $zip->close();
+        sort($testFiles);
+        sort($zipFiles);
+
+        $this->assertSame($testFiles, $zipFiles);
     }
 
     /** @test */
@@ -290,7 +319,7 @@ class BackupCommandTest extends TestCase
          */
         $this->app['db']->disconnect();
     }
-    
+
     /** @test */
     public function it_should_trigger_the_backup_failed_event()
     {
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 31a37fc..9925ff8 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -149,9 +149,9 @@ abstract class TestCase extends Orchestra
         Carbon::setTestNow($date);
     }
 
-    public function getStubDirectory(): string
+    public function getStubDirectory(?string $file = null): string
     {
-        return __DIR__.'/stubs';
+        return __DIR__.'/stubs'.($file ? '/'.$file : '');
     }
 
     public function createSQLiteDatabase(string $fileName): string