| Total Complexity | 40 |
| Total Lines | 334 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Complex classes like MediaFileService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MediaFileService, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 61 | class MediaFileService |
||
| 62 | { |
||
| 63 | public const EDIT_RESTRICTIONS = [ |
||
| 64 | 'locked', |
||
| 65 | ]; |
||
| 66 | |||
| 67 | public const PRIVACY_RESTRICTIONS = [ |
||
| 68 | 'none', |
||
| 69 | 'privacy', |
||
| 70 | 'confidential', |
||
| 71 | ]; |
||
| 72 | |||
| 73 | public const EXTENSION_TO_FORM = [ |
||
| 74 | 'jpg' => 'jpeg', |
||
| 75 | 'tif' => 'tiff', |
||
| 76 | ]; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * What is the largest file a user may upload? |
||
| 80 | */ |
||
| 81 | public function maxUploadFilesize(): string |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * Returns the given size from an ini value in bytes. |
||
| 94 | * |
||
| 95 | * @param string $size |
||
| 96 | * |
||
| 97 | * @return int |
||
| 98 | */ |
||
| 99 | private function parseIniFileSize(string $size): int |
||
| 115 | } |
||
| 116 | } |
||
| 117 | |||
| 118 | /** |
||
| 119 | * A list of key/value options for media types. |
||
| 120 | * |
||
| 121 | * @param string $current |
||
| 122 | * |
||
| 123 | * @return array<int|string,string> |
||
| 124 | * |
||
| 125 | * @deprecated - Will be removed in 2.1.0 - use Registry::elementFactory()->make('OBJE:FILE:FORM:TYPE')->values() |
||
| 126 | */ |
||
| 127 | public function mediaTypes($current = ''): array |
||
| 130 | } |
||
| 131 | |||
| 132 | /** |
||
| 133 | * A list of media files not already linked to a media object. |
||
| 134 | * |
||
| 135 | * @param Tree $tree |
||
| 136 | * @param FilesystemInterface $data_filesystem |
||
| 137 | * |
||
| 138 | * @return array<string> |
||
| 139 | */ |
||
| 140 | public function unusedFiles(Tree $tree, FilesystemInterface $data_filesystem): array |
||
| 141 | { |
||
| 142 | $used_files = DB::table('media_file') |
||
| 143 | ->where('m_file', '=', $tree->id()) |
||
| 144 | ->where('multimedia_file_refn', 'NOT LIKE', 'http://%') |
||
| 145 | ->where('multimedia_file_refn', 'NOT LIKE', 'https://%') |
||
| 146 | ->pluck('multimedia_file_refn') |
||
| 147 | ->all(); |
||
| 148 | |||
| 149 | $disk_files = $tree->mediaFilesystem($data_filesystem)->listContents('', true); |
||
| 150 | |||
| 151 | $disk_files = array_filter($disk_files, static function (array $item) { |
||
| 152 | // Older versions of webtrees used a couple of special folders. |
||
| 153 | return |
||
| 154 | $item['type'] === 'file' && |
||
| 155 | !str_contains($item['path'], '/thumbs/') && |
||
| 156 | !str_contains($item['path'], '/watermarks/'); |
||
| 157 | }); |
||
| 158 | |||
| 159 | $disk_files = array_map(static function (array $item): string { |
||
| 160 | return $item['path']; |
||
| 161 | }, $disk_files); |
||
| 162 | |||
| 163 | $unused_files = array_diff($disk_files, $used_files); |
||
| 164 | |||
| 165 | sort($unused_files); |
||
| 166 | |||
| 167 | return array_combine($unused_files, $unused_files); |
||
| 168 | } |
||
| 169 | |||
| 170 | /** |
||
| 171 | * Store an uploaded file (or URL), either to be added to a media object |
||
| 172 | * or to create a media object. |
||
| 173 | * |
||
| 174 | * @param ServerRequestInterface $request |
||
| 175 | * |
||
| 176 | * @return string The value to be stored in the 'FILE' field of the media object. |
||
| 177 | */ |
||
| 178 | public function uploadFile(ServerRequestInterface $request): string |
||
| 179 | { |
||
| 180 | $tree = $request->getAttribute('tree'); |
||
| 181 | assert($tree instanceof Tree); |
||
| 182 | |||
| 183 | $data_filesystem = Registry::filesystem()->data(); |
||
| 184 | |||
| 185 | $params = (array) $request->getParsedBody(); |
||
| 186 | $file_location = $params['file_location']; |
||
| 187 | |||
| 188 | switch ($file_location) { |
||
| 189 | case 'url': |
||
| 190 | $remote = $params['remote']; |
||
| 191 | |||
| 192 | if (str_contains($remote, '://')) { |
||
| 193 | return $remote; |
||
| 194 | } |
||
| 195 | |||
| 196 | return ''; |
||
| 197 | |||
| 198 | case 'unused': |
||
| 199 | $unused = $params['unused']; |
||
| 200 | |||
| 201 | if ($tree->mediaFilesystem($data_filesystem)->has($unused)) { |
||
| 202 | return $unused; |
||
| 203 | } |
||
| 204 | |||
| 205 | return ''; |
||
| 206 | |||
| 207 | case 'upload': |
||
| 208 | default: |
||
| 209 | $folder = $params['folder']; |
||
| 210 | $auto = $params['auto']; |
||
| 211 | $new_file = $params['new_file']; |
||
| 212 | |||
| 213 | /** @var UploadedFileInterface|null $uploaded_file */ |
||
| 214 | $uploaded_file = $request->getUploadedFiles()['file']; |
||
| 215 | if ($uploaded_file === null || $uploaded_file->getError() !== UPLOAD_ERR_OK) { |
||
| 216 | return ''; |
||
| 217 | } |
||
| 218 | |||
| 219 | // The filename |
||
| 220 | $new_file = strtr($new_file, ['\\' => '/']); |
||
| 221 | if ($new_file !== '' && !str_contains($new_file, '/')) { |
||
| 222 | $file = $new_file; |
||
| 223 | } else { |
||
| 224 | $file = $uploaded_file->getClientFilename(); |
||
| 225 | } |
||
| 226 | |||
| 227 | // The folder |
||
| 228 | $folder = strtr($folder, ['\\' => '/']); |
||
| 229 | $folder = trim($folder, '/'); |
||
| 230 | if ($folder !== '') { |
||
| 231 | $folder .= '/'; |
||
| 232 | } |
||
| 233 | |||
| 234 | // Generate a unique name for the file? |
||
| 235 | if ($auto === '1' || $tree->mediaFilesystem($data_filesystem)->has($folder . $file)) { |
||
| 236 | $folder = ''; |
||
| 237 | $extension = pathinfo($uploaded_file->getClientFilename(), PATHINFO_EXTENSION); |
||
| 238 | $file = sha1((string) $uploaded_file->getStream()) . '.' . $extension; |
||
|
1 ignored issue
–
show
|
|||
| 239 | } |
||
| 240 | |||
| 241 | try { |
||
| 242 | $tree->mediaFilesystem($data_filesystem)->putStream($folder . $file, $uploaded_file->getStream()->detach()); |
||
| 243 | |||
| 244 | return $folder . $file; |
||
| 245 | } catch (RuntimeException | InvalidArgumentException $ex) { |
||
| 246 | FlashMessages::addMessage(I18N::translate('There was an error uploading your file.')); |
||
| 247 | |||
| 248 | return ''; |
||
| 249 | } |
||
| 250 | } |
||
| 251 | } |
||
| 252 | |||
| 253 | /** |
||
| 254 | * Convert the media file attributes into GEDCOM format. |
||
| 255 | * |
||
| 256 | * @param string $file |
||
| 257 | * @param string $type |
||
| 258 | * @param string $title |
||
| 259 | * @param string $note |
||
| 260 | * |
||
| 261 | * @return string |
||
| 262 | */ |
||
| 263 | public function createMediaFileGedcom(string $file, string $type, string $title, string $note): string |
||
| 264 | { |
||
| 265 | // Tidy non-printing characters |
||
| 266 | $type = trim(preg_replace('/\s+/', ' ', $type)); |
||
| 267 | $title = trim(preg_replace('/\s+/', ' ', $title)); |
||
| 268 | |||
| 269 | $gedcom = '1 FILE ' . $file; |
||
| 270 | |||
| 271 | $format = strtolower(pathinfo($file, PATHINFO_EXTENSION)); |
||
|
1 ignored issue
–
show
|
|||
| 272 | $format = self::EXTENSION_TO_FORM[$format] ?? $format; |
||
| 273 | |||
| 274 | if ($format !== '') { |
||
| 275 | $gedcom .= "\n2 FORM " . $format; |
||
| 276 | } elseif ($type !== '') { |
||
| 277 | $gedcom .= "\n2 FORM"; |
||
| 278 | } |
||
| 279 | |||
| 280 | if ($type !== '') { |
||
| 281 | $gedcom .= "\n3 TYPE " . $type; |
||
| 282 | } |
||
| 283 | |||
| 284 | if ($title !== '') { |
||
| 285 | $gedcom .= "\n2 TITL " . $title; |
||
| 286 | } |
||
| 287 | |||
| 288 | if ($note !== '') { |
||
| 289 | // Convert HTML line endings to GEDCOM continuations |
||
| 290 | $gedcom .= "\n1 NOTE " . strtr($note, ["\r\n" => "\n2 CONT "]); |
||
| 291 | } |
||
| 292 | |||
| 293 | return $gedcom; |
||
| 294 | } |
||
| 295 | |||
| 296 | /** |
||
| 297 | * Fetch a list of all files on disk (in folders used by any tree). |
||
| 298 | * |
||
| 299 | * @param FilesystemInterface $data_filesystem Fileystem to search |
||
| 300 | * @param string $media_folder Root folder |
||
| 301 | * @param bool $subfolders Include subfolders |
||
| 302 | * |
||
| 303 | * @return Collection<string> |
||
| 304 | */ |
||
| 305 | public function allFilesOnDisk(FilesystemInterface $data_filesystem, string $media_folder, bool $subfolders): Collection |
||
| 306 | { |
||
| 307 | $array = $data_filesystem->listContents($media_folder, $subfolders); |
||
| 308 | |||
| 309 | return Collection::make($array) |
||
| 310 | ->filter(static function (array $metadata): bool { |
||
| 311 | return |
||
| 312 | $metadata['type'] === 'file' && |
||
| 313 | !str_contains($metadata['path'], '/thumbs/') && |
||
| 314 | !str_contains($metadata['path'], '/watermark/'); |
||
| 315 | }) |
||
| 316 | ->map(static function (array $metadata): string { |
||
| 317 | return $metadata['path']; |
||
| 318 | }); |
||
| 319 | } |
||
| 320 | |||
| 321 | /** |
||
| 322 | * Fetch a list of all files on in the database. |
||
| 323 | * |
||
| 324 | * @param string $media_folder Root folder |
||
| 325 | * @param bool $subfolders Include subfolders |
||
| 326 | * |
||
| 327 | * @return Collection<string> |
||
| 328 | */ |
||
| 329 | public function allFilesInDatabase(string $media_folder, bool $subfolders): Collection |
||
| 346 | } |
||
| 347 | |||
| 348 | /** |
||
| 349 | * Generate a list of all folders in either the database or the filesystem. |
||
| 350 | * |
||
| 351 | * @param FilesystemInterface $data_filesystem |
||
| 352 | * |
||
| 353 | * @return Collection<string,string> |
||
| 354 | */ |
||
| 355 | public function allMediaFolders(FilesystemInterface $data_filesystem): Collection |
||
| 395 | }); |
||
| 396 | } |
||
| 397 | } |
||
| 398 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.