Issues (281)

Branch: master

src/Backend/Modules/Pages/Ajax/UploadFile.php (1 issue)

1
<?php
2
3
namespace Backend\Modules\Pages\Ajax;
4
5
use Symfony\Component\HttpFoundation\Request;
6
use Symfony\Component\Filesystem\Filesystem;
7
use Common\Core\Model;
8
use Common\Uri;
9
use Backend\Core\Engine\Base\AjaxAction;
10
use Backend\Core\Engine\Exception;
11
use Symfony\Component\HttpFoundation\Response;
12
13
/**
14
 * This action will enable you to upload files trough ajax.
15
 * It accepts both XmlHttp requests or file uploads using a form.
16
 *
17
 * Note that it only accepts single file uploads.
18
 * The type $_GET parameter will be used to determine which folder to upload in.
19
 */
20
class UploadFile extends AjaxAction
21
{
22
    private const ALLOWED_EXTENSIONS = [
23
        'apng',
24
        'avif',
25
        'gif',
26
        'jfif',
27
        'jpeg',
28
        'jpg',
29
        'pjp',
30
        'pjpeg',
31
        'png',
32
        'svg',
33
        'webp',
34
    ];
35
36
    public function execute(): void
37
    {
38
        $request = $this->getRequest();
39
40
        try {
41
            $fileName = $this->writeFile(
42
                $this->getFileContentFromRequest($request),
43
                $this->getFileNameFromRequest($request),
44
                $request->get('type')
45
            );
46
        } catch (Exception $backendCoreException) {
47
            $this->output(Response::HTTP_BAD_REQUEST, $backendCoreException->getMessage());
48
49
            return;
50
        }
51
52
        $this->output(Response::HTTP_OK, $fileName);
53
    }
54
55
    /**
56
     * Extracts the uploaded file from a request. It handles both XmlHttpRequest
57
     * uploads and form uploads (with files in the $_FILES global)
58
     *
59
     * @param Request $request
60
     *
61
     * @throws Exception When no file could be extracted
62
     *
63
     * @return string The content of the uploaded file
64
     */
65
    private function getFileContentFromRequest(Request $request): string
66
    {
67
        // ajax uploaders fallback to submitting a form with the file in the fields.
68
        $uploadedFiles = $request->files->all();
69
        if (count($uploadedFiles) === 1) {
70
            $file = array_values($uploadedFiles)[0];
71
72
            return file_get_contents($file->getPathname());
73
        }
74
75
        throw new Exception('The request doesn\'t contain one file.');
76
    }
77
78
    /**
79
     * Extracts the uploaded file name from a request. It handles both XmlHttpRequest
80
     * uploads and form uploads (with files in the $_FILES global)
81
     *
82
     * @param Request $request
83
     *
84
     * @throws Exception When no file could be extracted
85
     *
86
     * @return string The content of the uploaded file
87
     */
88
    private function getFileNameFromRequest(Request $request): string
89
    {
90
        // ajax uploaders fallback to submitting a form with the file in the fields.
91
        $uploadedFiles = $request->files->all();
92
        if (count($uploadedFiles) === 1) {
93
            $file = array_values($uploadedFiles)[0];
94
95
            return $file->getClientOriginalName();
96
        }
97
98
        throw new Exception('The request doesn\'t contain one file.');
99
    }
100
101
    /**
102
     * Writes some content to a file in a given folder
103
     *
104
     * @param string $content
105
     * @param string $fileName
106
     * @param string $destinationFolder
107
     *
108
     * @return string The filename of the written file.
109
     */
110
    private function writeFile(string $content, string $fileName, string $destinationFolder): string
111
    {
112
        $path = FRONTEND_FILES_PATH . '/Pages/' . basename($destinationFolder);
113
114
        // create the needed folder if it doesn't exist
115
        $filesystem = new Filesystem();
116
        if (!$filesystem->exists($path)) {
117
            $filesystem->mkdir($path);
118
        }
119
120
        // convert the filename to url friendly version
121
        $baseName = Uri::getUrl(pathinfo($fileName, PATHINFO_FILENAME));
122
        $extension = pathinfo($fileName, PATHINFO_EXTENSION);
123
        if (!in_array(strtolower($extension), self::ALLOWED_EXTENSIONS)) {
0 ignored issues
show
It seems like $extension can also be of type array; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

123
        if (!in_array(strtolower(/** @scrutinizer ignore-type */ $extension), self::ALLOWED_EXTENSIONS)) {
Loading history...
124
            throw new Exception('This is not an image.');
125
        }
126
        $fileName = $baseName . '.' . $extension;
127
128
        // generate a non-existing filename
129
        while ($filesystem->exists($path . '/' . $fileName)) {
130
            $baseName = Model::addNumber($baseName);
131
            $fileName = $baseName . '.' . $extension;
132
        }
133
134
        // save the content of the file
135
        $filesystem->dumpFile(
136
            $path . '/' . $fileName,
137
            $content
138
        );
139
140
        return $fileName;
141
    }
142
}
143