Completed
Push — master ( aba493...5356ed )
by Ruud
315:38 queued 305:00
created

Kunstmaan/MediaBundle/Helper/File/FileHandler.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\MediaBundle\Helper\File;
4
5
use Gaufrette\Filesystem;
6
use Kunstmaan\MediaBundle\Entity\Media;
7
use Kunstmaan\MediaBundle\Form\File\FileType;
8
use Kunstmaan\MediaBundle\Helper\ExtensionGuesserFactoryInterface;
9
use Kunstmaan\MediaBundle\Helper\Media\AbstractMediaHandler;
10
use Kunstmaan\MediaBundle\Helper\MimeTypeGuesserFactoryInterface;
11
use Kunstmaan\UtilitiesBundle\Helper\SlugifierInterface;
12
use Symfony\Component\HttpFoundation\File\File;
13
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesserInterface;
14
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
15
use Symfony\Component\HttpFoundation\File\UploadedFile;
16
17
/**
18
 * FileHandler
19
 */
20
class FileHandler extends AbstractMediaHandler
21
{
22
    /**
23
     * @var string
24
     */
25
    const TYPE = 'file';
26
27
    /**
28
     * @var string
29
     */
30
    public $mediaPath;
31
32
    /**
33
     * @var Filesystem
34
     */
35
    public $fileSystem;
36
37
    /**
38
     * @var MimeTypeGuesserInterface
39
     */
40
    public $mimeTypeGuesser;
41
42
    /**
43
     * @var ExtensionGuesserInterface
44
     */
45
    public $extensionGuesser;
46
47
    /**
48
     * Files with a blacklisted extension will be converted to txt
49
     *
50
     * @var array
51
     */
52
    private $blacklistedExtensions = array();
53
54
    /**
55
     * @var SlugifierInterface
56
     */
57
    private $slugifier;
58
59
    /**
60
     * Constructor
61
     *
62
     * @param int                              $priority
63
     * @param MimeTypeGuesserFactoryInterface  $mimeTypeGuesserFactory
64
     * @param ExtensionGuesserFactoryInterface $extensionGuesserFactoryInterface
65
     */
66 4
    public function __construct($priority, MimeTypeGuesserFactoryInterface $mimeTypeGuesserFactory, ExtensionGuesserFactoryInterface $extensionGuesserFactoryInterface)
67
    {
68 4
        parent::__construct($priority);
69 4
        $this->mimeTypeGuesser = $mimeTypeGuesserFactory->get();
70 4
        $this->extensionGuesser = $extensionGuesserFactoryInterface->get();
71 4
    }
72
73
    /**
74
     * @param SlugifierInterface $slugifier
75
     */
76
    public function setSlugifier(SlugifierInterface $slugifier)
77
    {
78
        $this->slugifier = $slugifier;
79
    }
80
81
    /**
82
     * Inject the blacklisted
83
     *
84
     * @param array $blacklistedExtensions
85
     */
86
    public function setBlacklistedExtensions(array $blacklistedExtensions)
87
    {
88
        $this->blacklistedExtensions = $blacklistedExtensions;
89
    }
90
91
    /**
92
     * Inject the path used in media urls.
93
     *
94
     * @param string $mediaPath
95
     */
96
    public function setMediaPath($mediaPath)
97
    {
98
        $this->mediaPath = $mediaPath;
99
    }
100
101
    public function setFileSystem(Filesystem $fileSystem)
102
    {
103
        $this->fileSystem = $fileSystem;
104
    }
105
106
    /**
107
     * @return string
108
     */
109
    public function getName()
110
    {
111
        return 'File Handler';
112
    }
113
114
    /**
115
     * @return string
116
     */
117
    public function getType()
118
    {
119
        return FileHandler::TYPE;
120
    }
121
122
    /**
123
     * @return string
124
     */
125
    public function getFormType()
126
    {
127
        return FileType::class;
128
    }
129
130
    /**
131
     * @param mixed $object
132
     *
133
     * @return bool
134
     */
135 2
    public function canHandle($object)
136
    {
137 2
        if ($object instanceof File ||
138 2
            ($object instanceof Media &&
139 2
            (is_file($object->getContent()) || $object->getLocation() == 'local'))
140
        ) {
141 1
            return true;
142
        }
143
144 1
        return false;
145
    }
146
147
    /**
148
     * @param Media $media
149
     *
150
     * @return FileHelper
151
     */
152
    public function getFormHelper(Media $media)
153
    {
154
        return new FileHelper($media);
155
    }
156
157
    /**
158
     * @param Media $media
159
     *
160
     * @throws \RuntimeException when the file does not exist
161
     */
162
    public function prepareMedia(Media $media)
163
    {
164
        if (null === $media->getUuid()) {
165
            $uuid = uniqid();
166
            $media->setUuid($uuid);
167
        }
168
169
        $content = $media->getContent();
170
        if (empty($content)) {
171
            return;
172
        }
173
174
        if (!$content instanceof File) {
175
            if (!is_file($content)) {
176
                throw new \RuntimeException('Invalid file');
177
            }
178
179
            $file = new File($content);
180
            $media->setContent($file);
181
        }
182
183
        $contentType = $this->mimeTypeGuesser->guess($content->getPathname());
184
        if ($content instanceof UploadedFile) {
185
            $pathInfo = pathinfo($content->getClientOriginalName());
186
187
            if (!\array_key_exists('extension', $pathInfo)) {
188
                $pathInfo['extension'] = $this->extensionGuesser->guess($contentType);
189
            }
190
191
            $media->setOriginalFilename($this->slugifier->slugify($pathInfo['filename']).'.'.$pathInfo['extension']);
192
            $name = $media->getName();
193
194
            if (empty($name)) {
195
                $media->setName($media->getOriginalFilename());
196
            }
197
        }
198
199
        $media->setContentType($contentType);
200
        $media->setFileSize(filesize($media->getContent()));
201
        $media->setUrl($this->mediaPath . $this->getFilePath($media));
202
        $media->setLocation('local');
203
    }
204
205
    /**
206
     * @param Media $media
207
     */
208
    public function removeMedia(Media $media)
209
    {
210
        $adapter = $this->fileSystem->getAdapter();
211
212
        // Remove the file from filesystem
213
        $fileKey = $this->getFilePath($media);
214
        if ($adapter->exists($fileKey)) {
215
            $adapter->delete($fileKey);
216
        }
217
218
        // Remove the files containing folder if there's nothing left
219
        $folderPath = $this->getFileFolderPath($media);
220
        if ($adapter->exists($folderPath) && $adapter->isDirectory($folderPath) && !empty($folderPath)) {
221
            $allMyKeys = $adapter->keys();
222
            $everythingfromdir = preg_grep('/'.$folderPath, $allMyKeys);
223
224
            if (\count($everythingfromdir) === 1) {
225
                $adapter->delete($folderPath);
226
            }
227
        }
228
229
        $media->setRemovedFromFileSystem(true);
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235
    public function updateMedia(Media $media)
236
    {
237
        $this->saveMedia($media);
238
    }
239
240
    /**
241
     * @param Media $media
242
     */
243
    public function saveMedia(Media $media)
244
    {
245
        if (!$media->getContent() instanceof File) {
246
            return;
247
        }
248
249
        $originalFile = $this->getOriginalFile($media);
250
        $originalFile->setContent(file_get_contents($media->getContent()->getRealPath()));
251
    }
252
253
    /**
254
     * @param Media $media
255
     *
256
     * @return \Gaufrette\File
257
     */
258
    public function getOriginalFile(Media $media)
259
    {
260
        return $this->fileSystem->get($this->getFilePath($media), true);
261
    }
262
263
    /**
264
     * @param mixed $data
265
     *
266
     * @return Media
267
     */
268
    public function createNew($data)
269
    {
270
        if ($data instanceof File) {
271
            /** @var $data File */
272
            $media = new Media();
273
            if (method_exists($data, 'getClientOriginalName')) {
274
                $media->setOriginalFilename($data->getClientOriginalName());
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class Symfony\Component\HttpFoundation\File\File as the method getClientOriginalName() does only exist in the following sub-classes of Symfony\Component\HttpFoundation\File\File: Symfony\Component\HttpFoundation\File\UploadedFile. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
275
            } else {
276
                $media->setOriginalFilename($data->getFilename());
277
            }
278
            $media->setContent($data);
279
280
            $contentType = $this->mimeTypeGuesser->guess($media->getContent()->getPathname());
281
            $media->setContentType($contentType);
282
283
            return $media;
284
        }
285
286
        return null;
287
    }
288
289
    /**
290
     * {@inheritdoc}
291
     */
292
    public function getShowTemplate(Media $media)
293
    {
294
        return 'KunstmaanMediaBundle:Media\File:show.html.twig';
295
    }
296
297
    /**
298
     * @return array
299
     */
300
    public function getAddFolderActions()
301
    {
302
        return array(
303
            FileHandler::TYPE => array(
304
                'type' => FileHandler::TYPE,
305
                'name' => 'media.file.add',
306
            ),
307
        );
308
    }
309
310
    /**
311
     * @param Media $media
312
     *
313
     * @return string
314
     */
315
    private function getFilePath(Media $media)
316
    {
317
        $filename = $media->getOriginalFilename();
318
        $filename = str_replace(array('/', '\\', '%'), '', $filename);
319
320
        if (!empty($this->blacklistedExtensions)) {
321
            $filename = preg_replace('/\.('.implode('|', $this->blacklistedExtensions).')$/', '.txt', $filename);
322
        }
323
324
        $parts = pathinfo($filename);
325
        $filename = $this->slugifier->slugify($parts['filename']);
326
        if (\array_key_exists('extension', $parts)) {
327
            $filename .= '.'.strtolower($parts['extension']);
328
        }
329
330
        return sprintf(
331
            '%s/%s',
332
            $media->getUuid(),
333
            $filename
334
        );
335
    }
336
337
    /**
338
     * @param Media $media
339
     *
340
     * @return string
341
     */
342
    private function getFileFolderPath(Media $media)
343
    {
344
        return substr($this->getFilePath($media), 0, strrpos($this->getFilePath($media), $media->getOriginalFilename()));
345
    }
346
}
347