• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nepada / file-upload-control / 9327437708

pending completion
9327437708

Pull #156

github

web-flow
Merge 92839108e into 46a12c798
Pull Request #156: Update mockery/mockery requirement from 1.6.11 to 1.6.12

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.59
/src/FileUploadControl/Storage/FileSystemStorage.php
1
<?php
2
declare(strict_types = 1);
3

4
namespace Nepada\FileUploadControl\Storage;
5

6
use Nepada\FileUploadControl\Storage\Metadata\FileUploadMetadata;
7
use Nepada\FileUploadControl\Storage\Metadata\FileUploadMetadataAlreadyExistsException;
8
use Nepada\FileUploadControl\Storage\Metadata\FileUploadMetadataNotFoundException;
9
use Nepada\FileUploadControl\Storage\Metadata\MetadataJournal;
10
use Nepada\FileUploadControl\Utils\FileSystem;
11
use Nette;
12
use Nette\Http\FileUpload;
13

14
final class FileSystemStorage implements Storage
15
{
16

17
    use Nette\SmartObject;
18

19
    private MetadataJournal $metadataJournal;
20

21
    private FileSystem $fileSystem;
22

23
    private string $directory;
24

25
    public function __construct(MetadataJournal $metadataJournal, FileSystem $fileSystem, string $directory)
1✔
26
    {
27
        $this->metadataJournal = $metadataJournal;
1✔
28
        $this->fileSystem = $fileSystem;
1✔
29
        if (! $this->fileSystem->directoryExists($directory)) {
1✔
30
            throw new \InvalidArgumentException("Directory '$directory' does not exist.");
×
31
        }
32
        if (! $this->fileSystem->isWritable($directory)) {
1✔
33
            throw new \InvalidArgumentException("Directory '$directory' is not writable.");
×
34
        }
35
        $this->directory = $directory;
1✔
36
    }
1✔
37

38
    /**
39
     * @return FileUploadItem[]
40
     */
41
    public function list(): array
42
    {
43
        $items = [];
1✔
44
        foreach ($this->metadataJournal->list() as $id) {
1✔
45
            try {
46
                $fileUploadItem = $this->load($id);
1✔
47
            } catch (FileUploadNotFoundException $exception) {
1✔
48
                continue;
1✔
49
            }
50
            $items[] = $fileUploadItem;
1✔
51
        }
52
        return $items;
1✔
53
    }
54

55
    /**
56
     * @throws FileUploadNotFoundException
57
     */
58
    public function load(FileUploadId $id): FileUploadItem
1✔
59
    {
60
        try {
61
            $metadata = $this->metadataJournal->load($id);
1✔
62
        } catch (FileUploadMetadataNotFoundException $exception) {
1✔
63
            throw new FileUploadNotFoundException($exception->getMessage(), 0, $exception);
1✔
64
        }
65

66
        $file = $this->getFilePath($id);
1✔
67
        if (! $this->fileSystem->fileExists($file)) {
1✔
68
            throw FileUploadNotFoundException::withId($id);
1✔
69
        }
70

71
        $error = $this->fileSystem->fileSize($file) === $metadata->size ? UPLOAD_ERR_OK : UPLOAD_ERR_PARTIAL;
1✔
72

73
        $fileUpload = new FileUpload([
1✔
74
            'name' => $metadata->name,
1✔
75
            'size' => $metadata->size,
1✔
76
            'tmp_name' => $file,
1✔
77
            'error' => $error,
1✔
78
        ]);
79
        return new FileUploadItem($id, $fileUpload);
1✔
80
    }
81

82
    public function delete(FileUploadId $id): void
1✔
83
    {
84
        $this->metadataJournal->delete($id);
1✔
85
        $this->fileSystem->delete($this->getFilePath($id));
1✔
86
    }
1✔
87

88
    /**
89
     * @throws UnableToSaveFileUploadException
90
     */
91
    public function save(FileUploadChunk $fileUploadChunk): FileUploadItem
1✔
92
    {
93
        if ($fileUploadChunk->contentRange->containsFirstByte()) {
1✔
94
            $id = $this->saveNewUpload($fileUploadChunk);
1✔
95
        } else {
96
            $id = $this->resumeExistingUpload($fileUploadChunk);
1✔
97
        }
98

99
        try {
100
            return $this->load($id);
1✔
101
        } catch (FileUploadNotFoundException $exception) {
×
102
            throw new UnableToSaveFileUploadException($exception->getMessage(), 0, $exception);
×
103
        }
104
    }
105

106
    public function destroy(): void
107
    {
108
        $this->metadataJournal->destroy();
1✔
109
        $this->fileSystem->delete($this->directory);
1✔
110
    }
1✔
111

112
    private function getFilePath(FileUploadId $id): string
1✔
113
    {
114
        return $this->directory . DIRECTORY_SEPARATOR . $id->toString();
1✔
115
    }
116

117
    /**
118
     * @throws UnableToSaveFileUploadException
119
     */
120
    private function saveNewUpload(FileUploadChunk $fileUploadChunk): FileUploadId
1✔
121
    {
122
        $metadata = FileUploadMetadata::fromFileUploadChunk($fileUploadChunk);
1✔
123
        $id = $metadata->createFileUploadId();
1✔
124
        try {
125
            $this->metadataJournal->save($id, $metadata);
1✔
126
        } catch (FileUploadMetadataAlreadyExistsException $exception) {
1✔
127
            throw UnableToSaveFileUploadException::withConflict($id);
1✔
128
        }
129

130
        $file = $this->getFilePath($id);
1✔
131
        $contents = $fileUploadChunk->fileUpload->getContents();
1✔
132
        assert(is_string($contents));
1✔
133
        $this->fileSystem->write($file, $contents);
1✔
134

135
        return $id;
1✔
136
    }
137

138
    /**
139
     * @throws UnableToSaveFileUploadException
140
     */
141
    private function resumeExistingUpload(FileUploadChunk $fileUploadChunk): FileUploadId
1✔
142
    {
143
        $metadata = FileUploadMetadata::fromFileUploadChunk($fileUploadChunk);
1✔
144
        $id = $metadata->createFileUploadId();
1✔
145
        try {
146
            $storedMetadata = $this->metadataJournal->load($id);
1✔
147
            if (! $metadata->equals($storedMetadata)) {
1✔
148
                throw UnableToSaveFileUploadException::withConflict($id);
1✔
149
            }
150
        } catch (FileUploadMetadataNotFoundException $exception) {
1✔
151
            throw UnableToSaveFileUploadException::withFailedChunk($id, 'failed to load metadata');
1✔
152
        }
153

154
        $file = $this->getFilePath($id);
1✔
155
        if (! $this->fileSystem->fileExists($file)) {
1✔
156
            throw UnableToSaveFileUploadException::withFailedChunk($id, 'missing previously uploaded file part');
1✔
157
        }
158
        if ($this->fileSystem->fileSize($file) !== $fileUploadChunk->contentRange->getStart()) {
1✔
159
            throw UnableToSaveFileUploadException::withFailedChunk($id, 'previously uploaded file part size does not match given content-range value');
1✔
160
        }
161

162
        $contents = $fileUploadChunk->fileUpload->getContents();
1✔
163
        assert(is_string($contents));
1✔
164
        $this->fileSystem->append($file, $contents);
1✔
165

166
        return $id;
1✔
167
    }
168

169
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc