Passed
Push — master ( b3acae...0ab104 )
by Jeroen
02:31
created

ExecuteMediaItemImportHandler::getMd5()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\Command;
4
5
use Backend\Modules\MediaLibrary\Component\StorageProvider\LocalStorageProviderInterface;
6
use Backend\Modules\MediaLibrary\Domain\MediaFolder\MediaFolderRepository;
7
use Backend\Modules\MediaLibrary\Domain\MediaItem\MediaItem;
8
use Backend\Modules\MediaLibrary\Domain\MediaItem\MediaItemRepository;
9
use Backend\Modules\MediaLibrary\Manager\FileManager;
10
use Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\Exception\MediaImportFailed;
11
use Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\MediaItemImport;
12
use Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\MediaItemImportRepository;
13
use Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\Method;
14
15
class ExecuteMediaItemImportHandler
16
{
17
    /** @var FileManager */
18
    protected $fileManager;
19
20
    /** @var LocalStorageProviderInterface */
21
    protected $localStorageProvider;
22
23
    /** @var MediaFolderRepository */
24
    protected $mediaFolderRepository;
25
26
    /** @var MediaItemRepository */
27
    protected $mediaItemRepository;
28
29
    /** @var MediaItemImportRepository */
30
    protected $mediaItemImportRepository;
31
32
    /**
33
     * ExecuteMediaItemImportHandler constructor.
34
     *
35
     * @param FileManager $fileManager
36
     * @param LocalStorageProviderInterface $localStorageProvider
37
     * @param MediaFolderRepository $mediaFolderRepository
38
     * @param MediaItemImportRepository $mediaItemImportRepository
39
     * @param MediaItemRepository $mediaItemRepository
40
     */
41
    public function __construct(
42
        FileManager $fileManager,
43
        LocalStorageProviderInterface $localStorageProvider,
44
        MediaFolderRepository $mediaFolderRepository,
45
        MediaItemImportRepository $mediaItemImportRepository,
46
        MediaItemRepository $mediaItemRepository
47
    ) {
48
        $this->fileManager = $fileManager;
49
        $this->localStorageProvider = $localStorageProvider;
50
        $this->mediaFolderRepository = $mediaFolderRepository;
51
        $this->mediaItemImportRepository = $mediaItemImportRepository;
52
        $this->mediaItemRepository = $mediaItemRepository;
53
    }
54
55
    /**
56
     * @param ExecuteMediaItemImport $executeMediaItemImport
57
     */
58
    public function handle(ExecuteMediaItemImport $executeMediaItemImport)
59
    {
60
        /** @var MediaItemImport $mediaItemImport */
61
        $mediaItemImport = $executeMediaItemImport->getMediaItemImportEntity();
62
63
        if ($this->linkExistingMediaItem($mediaItemImport)) {
64
            return;
65
        }
66
67
        $this->linkNewMediaItem($mediaItemImport);
68
    }
69
70
    /**
71
     * @param MediaItemImport $mediaItemImport
72
     * @return MediaItem|null
73
     */
74
    private function findExistingMediaItem(MediaItemImport $mediaItemImport)
75
    {
76
        /** @var MediaItemImport|null $existingMediaItemImport */
77
        $existingMediaItemImport = $this->mediaItemImportRepository->findExistingImported(
78
            $mediaItemImport->getMediaGroup(),
79
            $mediaItemImport->getPath()
80
        );
81
82
        if (!$existingMediaItemImport instanceof MediaItemImport) {
83
            return null;
84
        }
85
86
        return $existingMediaItemImport->getMediaItem();
87
    }
88
89
    /**
90
     * @param MediaItemImport $mediaItemImport
91
     * @return string
92
     */
93
    private function getDestinationPath(MediaItemImport $mediaItemImport): string
94
    {
95
        // Define upload dir
96
        $uploadDir = $this->localStorageProvider->getUploadRootDir() . '/' . $this->fileManager->getNextShardingFolder();
97
98
        // Generate folder if not exists
99
        $this->fileManager->createFolder($uploadDir);
100
101
        // Generate filename which doesn't exist yet in our media library
102
        $newName = $this->fileManager->getUniqueFileName(
103
            $uploadDir,
104
            basename($mediaItemImport->getPath())
105
        );
106
107
        return $uploadDir . '/' . $newName;
108
    }
109
110
    /**
111
     * @param $path
112
     * @return string
113
     * @throws MediaImportFailed
114
     */
115
    private function getFileSize($path)
116
    {
117
        try {
118
            return filesize($path);
119
        } catch (\Exception $e) {
120
            throw MediaImportFailed::forPath($path);
121
        }
122
    }
123
124
    /**
125
     * @param string $path
126
     * @return string
127
     * @throws MediaImportFailed
128
     */
129
    private function getMd5($path)
130
    {
131
        try {
132
            return md5_file($path);
133
        } catch (\Exception $e) {
134
            throw MediaImportFailed::forPath($path);
135
        }
136
    }
137
138
    /**
139
     * @param MediaItemImport $mediaItemImport
140
     * @param string $destinationPath
141
     * @throws MediaImportFailed
142
     */
143
    private function importMedia(MediaItemImport $mediaItemImport, string $destinationPath)
144
    {
145
        switch ($mediaItemImport->getMethod()->getMethod()) {
146
            case Method::COPY:
147
                $this->fileManager->getFilesystem()->copy($mediaItemImport->getPath(), $destinationPath);
148
                break;
149
            case Method::MOVE:
150
                $this->fileManager->rename($mediaItemImport->getPath(), $destinationPath);
151
                break;
152
            case Method::DOWNLOAD:
153
                $tryDownloading = true;
154
                $downloadCounter = 0;
155
                while ($tryDownloading) {
156
                    $downloadCounter += 1;
157
158
                    // ToDo: add try/catch here, so instead of trying once and throwing exception
159
                    // we should keep in the while loop, until we find the trying is enough
160
                    if (file_put_contents($destinationPath, fopen($mediaItemImport->getPath(), 'r'))) {
161
                        $tryDownloading = false;
162
                        continue;
163
                    }
164
165
                    if ($downloadCounter === 5) {
166
                        throw MediaImportFailed::forPath($mediaItemImport->getPath());
167
                    }
168
                }
169
                break;
170
        }
171
    }
172
173
    /**
174
     * @param MediaItemImport $mediaItemImport
175
     * @param MediaItem|null $mediaItem
176
     * @return bool
177
     * @throws MediaImportFailed
178
     */
179
    private function isMatchingAlreadyExistingMediaItem(
180
        MediaItemImport $mediaItemImport,
181
        MediaItem $mediaItem = null
182
    ): bool {
183
        if ($mediaItem === null) {
184
            return false;
185
        }
186
187
        $oldPath = $mediaItem->getAbsoluteWebPath();
188
        $newPath = $mediaItemImport->getPath();
189
190
        // We check if our existing MediaItem file matches the new one we received
191
        if ($this->getFileSize($oldPath) === $this->getFileSize($newPath)
192
            && $this->getMd5($oldPath) === $this->getMd5($newPath)
193
        ) {
194
            return true;
195
        }
196
197
        return false;
198
    }
199
200
    /**
201
     * We find an eventually existing media item and if it matches exactly, we are going to use it.
202
     *
203
     * @param MediaItemImport $mediaItemImport
204
     * @return bool
205
     */
206
    private function linkExistingMediaItem(MediaItemImport $mediaItemImport): bool
207
    {
208
        /** @var MediaItem|null $existingMediaItem */
209
        $existingMediaItem = $this->findExistingMediaItem($mediaItemImport);
210
211
        // Existing and matching MediaItem found
212
        if ($this->isMatchingAlreadyExistingMediaItem($mediaItemImport, $existingMediaItem)) {
213
            $mediaItemImport->changeStatusToExisting($existingMediaItem);
0 ignored issues
show
Bug introduced by
It seems like $existingMediaItem defined by $this->findExistingMediaItem($mediaItemImport) on line 209 can be null; however, Backend\Modules\MediaLib...hangeStatusToExisting() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
214
215
            return true;
216
        }
217
218
        return false;
219
    }
220
221
    /**
222
     * We got a completely new media item, so we move it to the right place and create a MediaItem for it.
223
     *
224
     * @param MediaItemImport $mediaItemImport
225
     */
226
    private function linkNewMediaItem(MediaItemImport $mediaItemImport)
227
    {
228
        // Create new MediaItem
229
        try {
230
            /** @var string $destinationPath */
231
            $destinationPath = $this->getDestinationPath($mediaItemImport);
232
233
            // We place the media-item in the right path
234
            $this->importMedia($mediaItemImport, $destinationPath);
235
236
            // We now create a MediaItem and change status of MediaItemImport
237
            $mediaItemImport->changeStatusToImported(
238
                $destinationPath,
239
                $this->mediaFolderRepository->findDefault(),
240
                1
241
            );
242
243
            // We add the new item
244
            $this->mediaItemRepository->add($mediaItemImport->getMediaItem());
245
        } catch (\Exception $e) {
246
            $mediaItemImport->changeStatusToError($e->getMessage());
247
        }
248
    }
249
}
250