Passed
Push — feature/uploadable ( 7c6d25...a7ed20 )
by Daniel
11:07
created

DataUriFile::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the Silverback API Components Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentsBundle\Model\Uploadable;
15
16
use Symfony\Component\HttpFoundation\File\Exception\FileException;
17
use Symfony\Component\HttpFoundation\File\File;
18
use Symfony\Component\Mime\MimeTypes;
19
20
/**
21
 * Based on https://github.com/hshn/base64-encoded-file/blob/master/src/HttpFoundation/File/Base64EncodedFile.php
22
 * Additional support for data uri - regular expression detection.
23
 *
24
 * @author Daniel West <[email protected]>
25
 */
26
class DataUriFile extends File
27
{
28
    /**
29
     * @param string $encoded
30
     * @param bool   $strict
31
     * @param bool   $checkPath
32
     */
33
    public function __construct($encoded, $strict = true, $checkPath = true)
34
    {
35
        parent::__construct($this->restoreToTemporary($encoded, $strict), $checkPath);
36
    }
37
38
    /**
39
     * @param string $encoded
40
     * @param bool   $strict
41
     *
42
     * @throws FileException
43
     */
44
    private function restoreToTemporary($encoded, $strict = true): string
45
    {
46
        preg_match('/^(?:(?:data:(?:\/\/)?([A-Za-z]+\/[A-Za-z.-]+)(?:;(base64))?,|)(?:(.+)|((?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?)))$/', $encoded, $matches);
47
48
        $mimeType = $matches[1];
49
        $base64Match = 'base64' === $matches[2];
50
        $base64 = '' === $mimeType || $base64Match;
51
        if ($base64 && !$mimeType) {
52
            if (false === $decoded = base64_decode($encoded, $strict)) {
53
                throw new FileException('Unable to decode strings as base64');
54
            }
55
56
            if (false === $path = tempnam($directory = sys_get_temp_dir(), 'DataUriFile')) {
57
                throw new FileException(sprintf('Unable to create a file into the "%s" directory', $directory));
58
            }
59
60
            if (false === file_put_contents($path, $decoded, FILE_BINARY)) {
61
                throw new FileException(sprintf('Unable to write the file "%s"', $path));
62
            }
63
64
            return $path;
65
        }
66
67
        if (false === $path = tempnam($directory = sys_get_temp_dir(), 'DataUriFile')) {
68
            throw new FileException(sprintf('Unable to create a file into the "%s" directory', $path));
69
        }
70
        if (null !== $extension = (MimeTypes::getDefault()->getExtensions($mimeType)[0] ?? null)) {
71
            $path .= '.' . $extension;
72
        }
73
        if (false === $target = @fopen($path, 'wb+')) {
74
            throw new FileException(sprintf('Unable to open the file "%s"', $path));
75
        }
76
77
        if (!$base64) {
78
            // data uri
79
            $content = urldecode($matches[3]);
80
            if (false === @fwrite($target, $content)) {
81
                throw new FileException(sprintf('Unable to write the file "%s"', $path));
82
            }
83
        } else {
84
            $source = @fopen($encoded, 'r');
85
            if (false === $source) {
86
                throw new FileException('Unable to decode strings as base64');
87
            }
88
89
            $meta = stream_get_meta_data($source);
90
            if ($strict && (!isset($meta['base64']) || true !== $meta['base64'])) {
91
                throw new FileException('Unable to decode strings as base64');
92
            }
93
94
            if (false === stream_copy_to_stream($source, $target)) {
95
                throw new FileException(sprintf('Unable to write the file "%s"', $path));
96
            }
97
98
            if (false === @fclose($source)) {
99
                throw new FileException(sprintf('Unable to close data stream'));
100
            }
101
        }
102
103
        if (false === @fclose($target)) {
104
            throw new FileException(sprintf('Unable to close the file "%s"', $path));
105
        }
106
107
        return $path;
108
    }
109
}
110