Completed
Push — develop ( 0703cb...681e92 )
by Lucas
08:24
created

FileManager::has()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * Handles file specific actions
4
 */
5
6
namespace Graviton\FileBundle;
7
8
use Gaufrette\File;
9
use Gaufrette\FileSystem;
10
use Graviton\ExceptionBundle\Exception\MalformedInputException;
11
use Graviton\RestBundle\Model\DocumentModel;
12
use GravitonDyn\FileBundle\Document\File as FileDocument;
13
use GravitonDyn\FileBundle\Document\FileMetadata;
14
use Symfony\Component\HttpFoundation\File\Exception\UploadException;
15
use Symfony\Component\HttpFoundation\File\UploadedFile;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
18
19
/**
20
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
21
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
22
 * @link     http://swisscom.ch
23
 */
24
class FileManager
25
{
26
    /**
27
     * @var FileSystem
28
     */
29
    private $fileSystem;
30
31
    /**
32
     * @var FileDocumentFactory
33
     */
34
    private $fileDocumentFactory;
35
36
    /**
37
     * FileManager constructor.
38
     *
39
     * @param FileSystem          $fileSystem          file system abstraction layer for s3 and more
40
     * @param FileDocumentFactory $fileDocumentFactory Instance to be used to create action entries.
41
     */
42 4
    public function __construct(FileSystem $fileSystem, FileDocumentFactory $fileDocumentFactory)
43
    {
44 4
        $this->fileSystem = $fileSystem;
45 4
        $this->fileDocumentFactory = $fileDocumentFactory;
46 4
    }
47
48
    /**
49
     * Indicates whether the file matching the specified key exists
50
     *
51
     * @param string $key Identifier to be found
52
     *
53
     * @return boolean TRUE if the file exists, FALSE otherwise
54
     */
55 2
    public function has($key)
56
    {
57 2
        return $this->fileSystem->has($key);
58
    }
59
60
    /**
61
     * Deletes the file matching the specified key
62
     *
63
     * @param string $key Identifier to be deleted
64
     *
65
     * @throws \RuntimeException when cannot read file
66
     *
67
     * @return boolean
68
     */
69 2
    public function delete($key)
0 ignored issues
show
Coding Style introduced by
function delete() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
70
    {
71 2
        return $this->fileSystem->delete($key);
72
    }
73
74
    /**
75
     * Reads the content from the file
76
     *
77
     * @param  string $key Key of the file
78
     *
79
     * @throws \Gaufrette\Exception\FileNotFound when file does not exist
80
     * @throws \RuntimeException                 when cannot read file
81
     *
82
     * @return string
83
     */
84 1
    public function read($key)
85
    {
86 1
        return $this->fileSystem->read($key);
87
    }
88
89
    /**
90
     * Stores uploaded files to CDN
91
     *
92
     * @param Request           $request  Current Http request
93
     * @param DocumentModel     $model    Model to be used to manage entity
94
     * @param FileDocument|null $fileData meta information about the file to be stored.
95
     *
96
     * @return array
97
     */
98 1
    public function saveFiles(Request $request, DocumentModel $model, FileDocument $fileData = null)
99
    {
100 1
        $inStore = [];
101 1
        $files = $this->extractUploadedFiles($request);
102
103 1
        foreach ($files as $key => $fileInfo) {
104
            /** @var FileDocument $record */
105 1
            $record = $this->getRecord($model, $fileData, $request->get('id'));
106 1
            $inStore[] = $record->getId();
107
108
            /** @var \Gaufrette\File $file */
109 1
            $file = $this->saveFile($record->getId(), $fileInfo['content']);
110
111 1
            $this->initOrUpdateMetadata(
112 1
                $record,
113 1
                $file->getSize(),
114 1
                $fileInfo,
115
                $fileData
116 1
            );
117
118 1
            $model->updateRecord($record->getId(), $record);
119
120
            // TODO NOTICE: ONLY UPLOAD OF ONE FILE IS CURRENTLY SUPPORTED
121 1
            break;
122 1
        }
123
124 1
        return $inStore;
125
    }
126
127
    /**
128
     * Save or update a file
129
     *
130
     * @param string $id   ID of file
131
     * @param String $data content to save
132
     *
133
     * @return File
134
     *
135
     * @throws BadRequestHttpException
136
     */
137 1
    public function saveFile($id, $data)
138
    {
139 1
        if (is_resource($data)) {
140
            throw new BadRequestHttpException('/file does not support storing resources');
141
        }
142 1
        $file = new File($id, $this->fileSystem);
143 1
        $file->setContent($data);
144
145 1
        return $file;
146
    }
147
148
    /**
149
     * Moves uploaded files to tmp directory
150
     *
151
     * @param Request $request Current http request
152
     *
153
     * @return array
154
     */
155 1
    private function extractUploadedFiles(Request $request)
156
    {
157 1
        $uploadedFiles = [];
158
159
        /** @var  $uploadedFile \Symfony\Component\HttpFoundation\File\UploadedFile */
160 1
        foreach ($request->files->all() as $field => $uploadedFile) {
161 1
            if (0 === $uploadedFile->getError()) {
162 1
                $uploadedFiles[$field] = [
163
                    'data' => [
164 1
                        'mimetype' => $uploadedFile->getMimeType(),
165 1
                        'filename' => $uploadedFile->getClientOriginalName()
166 1
                    ],
167 1
                    'content' => file_get_contents($uploadedFile->getPathName())
168 1
                ];
169 1
            } else {
170
                throw new UploadException($uploadedFile->getErrorMessage());
171
            }
172 1
        }
173
174 1
        if (empty($uploadedFiles)) {
175
            $uploadedFiles['upload'] = [
176
                'data' => [
177
                    'mimetype' => $request->headers->get('Content-Type'),
178
                    'filename' => ''
179
                ],
180
                'content' => $request->getContent()
181
            ];
182
        }
183
184 1
        return $uploadedFiles;
185
    }
186
187
    /**
188
     * Provides a set up instance of the file document
189
     *
190
     * @param DocumentModel     $model    Document model
191
     * @param FileDocument|null $fileData File information
192
     * @param string            $id       Alternative Id to be checked
193
     *
194
     * @return FileDocument
195
     */
196 1
    private function getRecord(DocumentModel $model, FileDocument $fileData = null, $id = '')
197
    {
198
        // does it really exist??
199 1
        if (!empty($fileData)) {
200 1
            $record = $model->find($fileData->getId());
201 1
        }
202 1
        if (empty($record) && !empty($id)) {
203
            $record = $model->find($id);
204
        }
205
206 1
        if (!empty($record)) {
207
            // handle missing 'id' field in input to a PUT operation
208
            // if it is settable on the document, let's set it and move on.. if not, inform the user..
209
            if ($record->getId() != $id) {
210
                // try to set it..
211
                if (is_callable(array($record, 'setId'))) {
212
                    $record->setId($id);
213
                } else {
214
                    throw new MalformedInputException('No ID was supplied in the request payload.');
215
                }
216
            }
217
218
            return $model->updateRecord($id, $record);
219
        }
220
221 1
        $record = $fileData;
222 1
        if (empty($record)) {
223
            $entityClass = $model->getEntityClass();
224
            $record = new $entityClass();
225
        }
226 1
        if (empty($record->getId()) && !empty($id)) {
227
            $record->setId($id);
228
        }
229
230 1
        return $model->insertRecord($record);
231
    }
232
233
    /**
234
     * Updates or initialzes the metadata information of the current entity.
235
     *
236
     * @param FileDocument $file     Document to be used
237
     * @param integer      $fileSize Size of the uploaded file
238
     * @param array        $fileInfo Additional info about the file
239
     * @param FileDocument $fileData File data to be updated
240
     *
241
     * @return void
242
     */
243 1
    private function initOrUpdateMetadata(FileDocument $file, $fileSize, array $fileInfo, FileDocument $fileData = null)
244
    {
245 1
        $meta = $file->getMetadata();
246 1
        $actions = [];
247 1
        $additionalInfo = '';
248
249 1
        if (!empty($fileData)) {
250 1
            $actions = !empty($actions = $fileData->getMetadata()->getAction()->toArray()) ? $actions : [];
251 1
            $additionalInfo = $fileData->getMetadata()->getAdditionalinformation();
252 1
            $file->setLinks(!empty($links = $fileData->getLinks()->toArray()) ? $links : []);
253 1
        }
254
255 1
        if (!empty($meta)) {
256 1
            $actions = (empty($actions)) ? $meta->getAction()->toArray() : $actions;
257 1
            $additionalInfo = empty($additionalInfo) ? $meta->getAdditionalinformation() : $additionalInfo;
258
            $meta
259 1
                ->setAction($actions)
260 1
                ->setAdditionalInformation($additionalInfo)
261 1
                ->setSize((int) $fileSize)
262 1
                ->setModificationdate(new \DateTime());
263
264 1
            if (!empty($fileInfo['data']['mimetype'])) {
265 1
                $meta->setMime($fileInfo['data']['mimetype']);
266 1
            }
267 1
            if (!empty($fileInfo['data']['filename'])) {
268 1
                $meta->setFilename($fileInfo['data']['filename']);
269 1
            }
270
271 1
        } else {
272
            // update record with file metadata
273
            $meta = $this->fileDocumentFactory->initiateFileMataData(
274
                $file->getId(),
275
                (int) $fileSize,
276
                $fileInfo['data']['filename'],
277
                $fileInfo['data']['mimetype'],
278
                $actions,
279
                $additionalInfo
280
            );
281
        }
282
283 1
        $file->setMetadata($meta);
284 1
    }
285
286
    /**
287
     * Extracts different information sent in the request content.
288
     *
289
     * @param Request $request Current http request
290
     *
291
     * @return array
292
     */
293
    public function extractDataFromRequestContent(Request $request)
294
    {
295
        // split content
296
        $contentType = $request->headers->get('Content-Type');
297
        list(, $boundary) = explode('; boundary=', $contentType);
298
299
        // fix boundary dash count
300
        $boundary = '--' . $boundary;
301
302
        $content = $request->getContent();
303
        $contentBlocks = explode($boundary, $content, -1);
304
        $metadataInfo = '';
305
        $fileInfo = '';
306
307
        // determine content blocks usage
308
        foreach ($contentBlocks as $contentBlock) {
309
            if (empty($contentBlock)) {
310
                continue;
311
            }
312
            if (40 === strpos($contentBlock, 'upload')) {
313
                $fileInfo = $contentBlock;
314
                continue;
315
            }
316
            if (40 === strpos($contentBlock, 'metadata')) {
317
                $metadataInfo = $contentBlock;
318
                continue;
319
            }
320
        }
321
322
        $attributes = array_merge(
323
            $request->attributes->all(),
324
            $this->extractMetaDataFromContent($metadataInfo)
325
        );
326
        $files = $this->extractFileFromContent($fileInfo);
327
328
        return ['files' => $files, 'attributes' => $attributes];
329
    }
330
331
    /**
332
     * Extracts meta information from request content.
333
     *
334
     * @param string $metadataInfoString Information about metadata information
335
     *
336
     * @return array
337
     */
338
    private function extractMetaDataFromContent($metadataInfoString)
339
    {
340
        if (empty($metadataInfoString)) {
341
            return ['metadata' => '{}'];
342
        }
343
344
        $metadataInfo = explode("\r\n", ltrim($metadataInfoString));
345
        return ['metadata' => $metadataInfo[2]];
346
    }
347
348
    /**
349
     * Extracts file data from request content
350
     *
351
     * @param string $fileInfoString Information about uploaded files.
352
     *
353
     * @return array
354
     */
355
    private function extractFileFromContent($fileInfoString)
356
    {
357
        if (empty($fileInfoString)) {
358
            return null;
359
        }
360
361
        $fileInfo = explode("\r\n\r\n", ltrim($fileInfoString), 2);
362
363
        // write content to file ("upload_tmp_dir" || sys_get_temp_dir() )
364
        preg_match('@name=\"([^"]*)\";\sfilename=\"([^"]*)\"\s*Content-Type:\s([^"]*)@', $fileInfo[0], $matches);
365
        $originalName = $matches[2];
366
        $dir = ini_get('upload_tmp_dir');
367
        $dir = (empty($dir)) ? sys_get_temp_dir() : $dir;
368
        $file = $dir . '/' . $originalName;
369
        $fileContent = substr($fileInfo[1], 0, -2);
370
371
        // create file
372
        touch($file);
373
        $size = file_put_contents($file, $fileContent, LOCK_EX);
374
375
        $files = [
376
            $matches[1] => new UploadedFile(
377
                $file,
378
                $originalName,
379
                $matches[3],
380
                $size
381
            )
382
        ];
383
384
        return $files;
385
    }
386
}
387