These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the CRUDlex package. |
||
5 | * |
||
6 | * (c) Philip Lehmann-Böhm <[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 | namespace CRUDlex; |
||
13 | |||
14 | use League\Flysystem\FilesystemInterface; |
||
15 | use Symfony\Component\HttpFoundation\Request; |
||
16 | use Symfony\Component\HttpFoundation\StreamedResponse; |
||
17 | |||
18 | /** |
||
19 | * Handles the files. |
||
20 | */ |
||
21 | class FileHandler |
||
22 | { |
||
23 | |||
24 | /** |
||
25 | * Brings the abstract access to the filesystem. |
||
26 | * @var FilesystemInterface |
||
27 | */ |
||
28 | protected $filesystem; |
||
29 | |||
30 | /** |
||
31 | * Holds the entity definition. |
||
32 | * @var EntityDefinition |
||
33 | */ |
||
34 | protected $entityDefinition; |
||
35 | |||
36 | /** |
||
37 | * Constructs a file system path for the given parameters for storing the |
||
38 | * file of the file field. |
||
39 | * |
||
40 | * @param string $entityName |
||
41 | * the entity name |
||
42 | * @param Entity $entity |
||
43 | * the entity |
||
44 | * @param string $field |
||
45 | * the file field in the entity |
||
46 | * |
||
47 | * @return string |
||
48 | * the constructed path for storing the file of the file field |
||
49 | */ |
||
50 | 3 | protected function getPath($entityName, Entity $entity, $field) |
|
51 | { |
||
52 | 3 | return $this->entityDefinition->getField($field, 'path').'/'.$entityName.'/'.$entity->get('id').'/'.$field; |
|
53 | } |
||
54 | |||
55 | /** |
||
56 | * Executes a function for each file field of this entity. |
||
57 | * |
||
58 | * @param Entity $entity |
||
59 | * the just created entity |
||
60 | * @param string $entityName |
||
61 | * the name of the entity as this class here is not aware of it |
||
62 | * @param \Closure $function |
||
63 | * the function to perform, takes $entity, $entityName and $field as parameter |
||
64 | */ |
||
65 | 3 | protected function performOnFiles(Entity $entity, $entityName, $function) |
|
66 | { |
||
67 | 3 | $fields = $this->entityDefinition->getEditableFieldNames(); |
|
68 | 3 | foreach ($fields as $field) { |
|
69 | 3 | if ($this->entityDefinition->getType($field) == 'file') { |
|
70 | 3 | $function($entity, $entityName, $field); |
|
71 | } |
||
72 | } |
||
73 | 3 | } |
|
74 | |||
75 | /** |
||
76 | * Writes the uploaded files. |
||
77 | * |
||
78 | * @param AbstractData $data |
||
79 | * the AbstractData instance who should receive the events |
||
80 | * @param Request $request |
||
81 | * the HTTP request containing the file data |
||
82 | * @param Entity $entity |
||
83 | * the just manipulated entity |
||
84 | * @param string $entityName |
||
85 | * the name of the entity as this class here is not aware of it |
||
86 | * @param string $action |
||
87 | * the name of the performed action |
||
88 | * |
||
89 | * @return boolean |
||
90 | * true if all before events passed |
||
91 | */ |
||
92 | 2 | protected function shouldWriteFile(AbstractData $data, Request $request, Entity $entity, $entityName, $action) |
|
93 | { |
||
94 | 2 | $result = $data->getEvents()->shouldExecute($entity, 'before', $action); |
|
95 | 2 | if (!$result) { |
|
96 | 1 | return false; |
|
97 | } |
||
98 | 2 | $filesystem = $this->filesystem; |
|
99 | $this->performOnFiles($entity, $entityName, function($entity, $entityName, $field) use ($filesystem, $request) { |
||
100 | 2 | $file = $request->files->get($field); |
|
101 | 2 | if ($file !== null && $file->isValid()) { |
|
102 | 2 | $path = $this->getPath($entityName, $entity, $field); |
|
103 | 2 | $filename = $path.'/'.$file->getClientOriginalName(); |
|
104 | 2 | if ($filesystem->has($filename)) { |
|
105 | 1 | $filesystem->delete($filename); |
|
106 | } |
||
107 | 2 | $stream = fopen($file->getRealPath(), 'r+'); |
|
108 | 2 | $filesystem->writeStream($filename, $stream); |
|
109 | 2 | fclose($stream); |
|
110 | } |
||
111 | 2 | }); |
|
112 | 2 | $data->getEvents()->shouldExecute($entity, 'after', $action); |
|
113 | 2 | return true; |
|
114 | } |
||
115 | |||
116 | /** |
||
117 | * FileHandler constructor. |
||
118 | * @param FilesystemInterface $filesystem |
||
119 | * the filesystem to use |
||
120 | * @param EntityDefinition $entityDefinition |
||
121 | * the entity definition to use |
||
122 | */ |
||
123 | 5 | public function __construct(FilesystemInterface $filesystem, EntityDefinition $entityDefinition) |
|
124 | { |
||
125 | 5 | $this->filesystem = $filesystem; |
|
126 | 5 | $this->entityDefinition = $entityDefinition; |
|
127 | 5 | } |
|
128 | |||
129 | |||
130 | /** |
||
131 | * Renders (outputs) a file of an entity. This includes setting headers |
||
132 | * like the file size, mimetype and name, too. |
||
133 | * |
||
134 | * @param Entity $entity |
||
135 | * the entity to render the file from |
||
136 | * @param string $entityName |
||
137 | * the name of the entity as this class here is not aware of it |
||
138 | * @param string $field |
||
139 | * the field of the entity containing the file to be rendered |
||
140 | * |
||
141 | * @return StreamedResponse |
||
142 | * the HTTP streamed response |
||
143 | */ |
||
144 | 1 | public function renderFile(Entity $entity, $entityName, $field) |
|
145 | { |
||
146 | 1 | $targetPath = $this->getPath($entityName, $entity, $field); |
|
147 | 1 | $fileName = $entity->get($field); |
|
148 | 1 | $file = $targetPath.'/'.$fileName; |
|
149 | 1 | $mimeType = $this->filesystem->getMimetype($file); |
|
150 | 1 | $size = $this->filesystem->getSize($file); |
|
151 | 1 | $stream = $this->filesystem->readStream($file); |
|
152 | $response = new StreamedResponse(function() use ($stream) { |
||
153 | 1 | while ($data = fread($stream, 1024)) { |
|
154 | 1 | echo $data; |
|
155 | 1 | flush(); |
|
156 | } |
||
157 | 1 | fclose($stream); |
|
158 | 1 | }, 200, [ |
|
159 | 1 | 'Cache-Control' => 'public, max-age=86400', |
|
160 | 1 | 'Content-length' => $size, |
|
161 | 1 | 'Content-Type' => $mimeType, |
|
162 | 1 | 'Content-Disposition' => 'attachment; filename="'.$fileName.'"' |
|
163 | ]); |
||
164 | 1 | return $response; |
|
165 | } |
||
166 | |||
167 | |||
168 | /** |
||
169 | * Deletes all files of an existing entity. |
||
170 | * |
||
171 | * @param AbstractData $data |
||
172 | * the AbstractData instance who should receive the events |
||
173 | * @param Entity $entity |
||
174 | * the entity to delete the files from |
||
175 | * @param string $entityName |
||
176 | * the name of the entity as this class here is not aware of it |
||
177 | * |
||
178 | * @return boolean |
||
179 | * true on successful deletion |
||
180 | */ |
||
181 | 1 | View Code Duplication | public function deleteFiles(AbstractData $data, Entity $entity, $entityName) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
|||
182 | { |
||
183 | 1 | $result = $data->getEvents()->shouldExecute($entity, 'before', 'deleteFiles'); |
|
184 | 1 | if (!$result) { |
|
185 | 1 | return false; |
|
186 | } |
||
187 | $this->performOnFiles($entity, $entityName, function($entity, $entityName, $field) { |
||
188 | // For now, we are defensive and don't delete ever. As soon as soft deletion is optional, files will get deleted. |
||
189 | 1 | }); |
|
190 | 1 | $data->getEvents()->shouldExecute($entity, 'after', 'deleteFiles'); |
|
191 | 1 | return true; |
|
192 | } |
||
193 | |||
194 | /** |
||
195 | * Deletes a specific file from an existing entity. |
||
196 | * |
||
197 | * @param AbstractData $data |
||
198 | * the AbstractData instance who should receive the events |
||
199 | * @param Entity $entity |
||
200 | * the entity to delete the file from |
||
201 | * @param string $entityName |
||
202 | * the name of the entity as this class here is not aware of it |
||
203 | * @param string $field |
||
204 | * the field of the entity containing the file to be deleted |
||
205 | * @return bool true on successful deletion |
||
206 | * true on successful deletion |
||
207 | */ |
||
208 | 1 | View Code Duplication | public function deleteFile(AbstractData $data, Entity $entity, $entityName, $field) |
0 ignored issues
–
show
function deleteFile() 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...
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
|||
209 | { |
||
210 | 1 | $result = $data->getEvents()->shouldExecute($entity, 'before', 'deleteFile'); |
|
211 | 1 | if (!$result) { |
|
212 | 1 | return false; |
|
213 | } |
||
214 | // For now, we are defensive and don't delete ever. As soon as soft deletion is optional, files will get deleted. |
||
215 | 1 | $data->getEvents()->shouldExecute($entity, 'after', 'deleteFile'); |
|
216 | 1 | return true; |
|
217 | } |
||
218 | |||
219 | /** |
||
220 | * Creates the uploaded files of a newly created entity. |
||
221 | * |
||
222 | * @param AbstractData $data |
||
223 | * the AbstractData instance who should receive the events |
||
224 | * @param Request $request |
||
225 | * the HTTP request containing the file data |
||
226 | * @param Entity $entity |
||
227 | * the just created entity |
||
228 | * @param string $entityName |
||
229 | * the name of the entity as this class here is not aware of it |
||
230 | * |
||
231 | * @return boolean |
||
232 | * true if all before events passed |
||
233 | */ |
||
234 | 1 | public function createFiles(AbstractData $data, Request $request, Entity $entity, $entityName) |
|
0 ignored issues
–
show
function createFiles() 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...
|
|||
235 | { |
||
236 | 1 | return $this->shouldWriteFile($data, $request, $entity, $entityName, 'createFiles'); |
|
237 | } |
||
238 | |||
239 | /** |
||
240 | * Updates the uploaded files of an updated entity. |
||
241 | * |
||
242 | * @param AbstractData $data |
||
243 | * the AbstractData instance who should receive the events |
||
244 | * @param Request $request |
||
245 | * the HTTP request containing the file data |
||
246 | * @param Entity $entity |
||
247 | * the updated entity |
||
248 | * @param string $entityName |
||
249 | * the name of the entity as this class here is not aware of it |
||
250 | * |
||
251 | * @return boolean |
||
252 | * true on successful update |
||
253 | */ |
||
254 | 1 | public function updateFiles(AbstractData $data, Request $request, Entity $entity, $entityName) |
|
0 ignored issues
–
show
function updateFiles() 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...
|
|||
255 | { |
||
256 | // With optional soft deletion, the file should be deleted first. |
||
257 | 1 | return $this->shouldWriteFile($data, $request, $entity, $entityName, 'updateFiles'); |
|
258 | } |
||
259 | |||
260 | } |
||
261 |
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.