Completed
Push — master ( 5b123a...5e7977 )
by Freek
01:21
created

Filesystem::copyToMediaLibraryFromRemote()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
cc 3
nc 2
nop 4
1
<?php
2
3
namespace Spatie\MediaLibrary\Filesystem;
4
5
use Illuminate\Contracts\Filesystem\Factory;
6
use Illuminate\Support\Str;
7
use Spatie\MediaLibrary\Conversion\ConversionCollection;
8
use Spatie\MediaLibrary\Events\MediaHasBeenAdded;
9
use Spatie\MediaLibrary\FileManipulator;
10
use Spatie\MediaLibrary\Helpers\File;
11
use Spatie\MediaLibrary\Models\Media;
12
use Illuminate\Support\Facades\Storage;
13
use Spatie\MediaLibrary\Helpers\RemoteFile;
14
use Spatie\MediaLibrary\PathGenerator\PathGeneratorFactory;
15
16
class Filesystem
17
{
18
    /** @var \Illuminate\Contracts\Filesystem\Factory */
19
    protected $filesystem;
20
21
    /** @var array */
22
    protected $customRemoteHeaders = [];
23
24
    public function __construct(Factory $filesystem)
25
    {
26
        $this->filesystem = $filesystem;
27
    }
28
29
    public function add(string $file, Media $media, ?string $targetFileName = null)
30
    {
31
        $this->copyToMediaLibrary($file, $media, null, $targetFileName);
32
33
        event(new MediaHasBeenAdded($media));
34
35
        app(FileManipulator::class)->createDerivedFiles($media);
36
    }
37
38
    public function addRemote(RemoteFile $file, Media $media, ?string $targetFileName = null)
39
    {
40
        $this->copyToMediaLibraryFromRemote($file, $media, null, $targetFileName);
41
42
        event(new MediaHasBeenAdded($media));
43
44
        app(FileManipulator::class)->createDerivedFiles($media);
45
    }
46
47
    public function copyToMediaLibraryFromRemote(RemoteFile $file, Media $media, ?string $type = null, ?string $targetFileName = null)
48
    {
49
        $storage = Storage::disk($file->getDisk());
50
51
        $destinationFileName = $targetFileName ?: $file->getFilename();
52
53
        $destination = $this->getMediaDirectory($media, $type).$destinationFileName;
54
55
        $this->filesystem->disk($media->disk)
56
            ->getDriver()->writeStream(
57
                $destination,
58
                $storage->getDriver()->readStream($file->getKey()),
59
                $media->getDiskDriverName() === 'local'
60
                    ? [] : $this->getRemoteHeadersForFile(
61
                        $file->getKey(),
62
                        $media->getCustomHeaders(),
63
                        $storage->mimeType($file->getKey())
64
                    )
65
            );
66
    }
67
68
    public function copyToMediaLibrary(string $pathToFile, Media $media, ?string $type = null, ?string $targetFileName = null)
69
    {
70
        $destinationFileName = $targetFileName ?: pathinfo($pathToFile, PATHINFO_BASENAME);
71
72
        $destination = $this->getMediaDirectory($media, $type).$destinationFileName;
73
74
        $file = fopen($pathToFile, 'r');
75
76
        if ($media->getDiskDriverName() === 'local') {
77
            $this->filesystem
78
                ->disk($media->disk)
79
                ->put($destination, $file);
80
81
            fclose($file);
82
83
            return;
84
        }
85
86
        $this->filesystem
87
            ->disk($media->disk)
88
            ->put($destination, $file, $this->getRemoteHeadersForFile($pathToFile, $media->getCustomHeaders()));
89
90
        if (is_resource($file)) {
91
            fclose($file);
92
        }
93
    }
94
95
    public function addCustomRemoteHeaders(array $customRemoteHeaders)
96
    {
97
        $this->customRemoteHeaders = $customRemoteHeaders;
98
    }
99
100
    public function getRemoteHeadersForFile(string $file, array $mediaCustomHeaders = [], string $mimeType = null) : array
101
    {
102
        $mimeTypeHeader = ['ContentType' => $mimeType ?: File::getMimeType($file)];
103
104
        $extraHeaders = config('medialibrary.remote.extra_headers');
105
106
        return array_merge($mimeTypeHeader, $extraHeaders, $this->customRemoteHeaders, $mediaCustomHeaders);
107
    }
108
109
    public function getStream(Media $media)
110
    {
111
        $sourceFile = $this->getMediaDirectory($media).'/'.$media->file_name;
112
113
        return $this->filesystem->disk($media->disk)->readStream($sourceFile);
114
    }
115
116
    public function copyFromMediaLibrary(Media $media, string $targetFile): string
117
    {
118
        touch($targetFile);
119
120
        $stream = $this->getStream($media);
121
122
        $targetFileStream = fopen($targetFile, 'a');
123
124
        while (! feof($stream)) {
125
            $chunk = fread($stream, 1024);
126
            fwrite($targetFileStream, $chunk);
127
        }
128
129
        fclose($stream);
130
131
        fclose($targetFileStream);
132
133
        return $targetFile;
134
    }
135
136
    public function removeAllFiles(Media $media)
137
    {
138
        $mediaDirectory = $this->getMediaDirectory($media);
139
140
        $conversionsDirectory = $this->getMediaDirectory($media, 'conversions');
141
142
        $responsiveImagesDirectory = $this->getMediaDirectory($media, 'responsiveImages');
143
144
        collect([$mediaDirectory, $conversionsDirectory, $responsiveImagesDirectory])
145
146
            ->each(function ($directory) use ($media) {
147
                $this->filesystem->disk($media->disk)->deleteDirectory($directory);
148
            });
149
    }
150
151
    public function removeFile(Media $media, string $path)
152
    {
153
        $this->filesystem->disk($media->disk)->delete($path);
154
    }
155
156
    public function removeResponsiveImages(Media $media, string $conversionName = 'medialibrary_original')
157
    {
158
        $responsiveImagesDirectory = $this->getResponsiveImagesDirectory($media);
159
160
        $allFilePaths = $this->filesystem->allFiles($responsiveImagesDirectory);
0 ignored issues
show
Bug introduced by
The method allFiles() does not seem to exist on object<Illuminate\Contracts\Filesystem\Factory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
161
162
        $responsiveImagePaths = array_filter(
163
            $allFilePaths,
164
            function (string $path) use ($conversionName) {
165
                return Str::contains($path, $conversionName);
166
            }
167
        );
168
169
        $this->filesystem->delete($responsiveImagePaths);
0 ignored issues
show
Bug introduced by
The method delete() does not seem to exist on object<Illuminate\Contracts\Filesystem\Factory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
170
    }
171
172
    public function syncFileNames(Media $media)
173
    {
174
        $this->renameMediaFile($media);
175
176
        $this->renameConversionFiles($media);
177
    }
178
179
    protected function renameMediaFile(Media $media)
180
    {
181
        $newFileName = $media->file_name;
182
        $oldFileName = $media->getOriginal('file_name');
183
184
        $mediaDirectory = $this->getMediaDirectory($media);
185
186
        $oldFile = $mediaDirectory.'/'.$oldFileName;
187
        $newFile = $mediaDirectory.'/'.$newFileName;
188
189
        $this->filesystem->disk($media->disk)->move($oldFile, $newFile);
190
    }
191
192
    protected function renameConversionFiles(Media $media)
193
    {
194
        $newFileName = $media->file_name;
195
        $oldFileName = $media->getOriginal('file_name');
196
197
        $conversionDirectory = $this->getConversionDirectory($media);
198
199
        $conversionCollection = ConversionCollection::createForMedia($media);
200
201
        foreach ($media->getMediaConversionNames() as $conversionName) {
202
            $conversion = $conversionCollection->getByName($conversionName);
203
204
            $oldFile = $conversionDirectory.$conversion->getConversionFile($oldFileName);
205
            $newFile = $conversionDirectory.$conversion->getConversionFile($newFileName);
206
207
            $disk = $this->filesystem->disk($media->disk);
208
209
            // A media conversion file might be missing, waiting to be generated, failed etc.
210
            if (! $disk->exists($oldFile)) {
211
                continue;
212
            }
213
214
            $disk->move($oldFile, $newFile);
215
        }
216
    }
217
218
    public function getMediaDirectory(Media $media, ?string $type = null) : string
219
    {
220
        $pathGenerator = PathGeneratorFactory::create();
221
222
        if (! $type) {
223
            $directory = $pathGenerator->getPath($media);
224
        }
225
226
        if ($type === 'conversions') {
227
            $directory = $pathGenerator->getPathForConversions($media);
228
        }
229
230
        if ($type === 'responsiveImages') {
231
            $directory = $pathGenerator->getPathForResponsiveImages($media);
232
        }
233
234
        if (! in_array($media->getDiskDriverName(), ['s3'], true)) {
235
            $this->filesystem->disk($media->disk)->makeDirectory($directory);
0 ignored issues
show
Bug introduced by
The variable $directory does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
236
        }
237
238
        return $directory;
239
    }
240
241
    public function getConversionDirectory(Media $media) : string
242
    {
243
        return $this->getMediaDirectory($media, 'conversions');
244
    }
245
246
    public function getResponsiveImagesDirectory(Media $media) : string
247
    {
248
        return $this->getMediaDirectory($media, 'responsiveImages');
249
    }
250
}
251