Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Preview 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 Preview, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
46 | class Preview { |
||
47 | //the thumbnail folder |
||
48 | const THUMBNAILS_FOLDER = 'thumbnails'; |
||
49 | |||
50 | const MODE_FILL = 'fill'; |
||
51 | const MODE_COVER = 'cover'; |
||
52 | |||
53 | //config |
||
54 | private $maxScaleFactor; |
||
55 | /** @var int maximum width allowed for a preview */ |
||
56 | private $configMaxWidth; |
||
57 | /** @var int maximum height allowed for a preview */ |
||
58 | private $configMaxHeight; |
||
59 | |||
60 | //fileview object |
||
61 | private $fileView = null; |
||
62 | private $userView = null; |
||
63 | |||
64 | //vars |
||
65 | /** @var File */ |
||
66 | private $file; |
||
67 | private $maxX; |
||
68 | private $maxY; |
||
69 | private $scalingUp; |
||
70 | private $mimeType; |
||
71 | private $keepAspect = false; |
||
72 | private $mode = self::MODE_FILL; |
||
73 | |||
74 | //used to calculate the size of the preview to generate |
||
75 | /** @var int $maxPreviewWidth max width a preview can have */ |
||
76 | private $maxPreviewWidth; |
||
77 | /** @var int $maxPreviewHeight max height a preview can have */ |
||
78 | private $maxPreviewHeight; |
||
79 | /** @var int $previewWidth calculated width of the preview we're looking for */ |
||
80 | private $previewWidth; |
||
81 | /** @var int $previewHeight calculated height of the preview we're looking for */ |
||
82 | private $previewHeight; |
||
83 | |||
84 | // filemapper used for deleting previews |
||
85 | // index is path, value is fileinfo |
||
86 | public static $deleteFileMapper = []; |
||
87 | public static $deleteChildrenMapper = []; |
||
88 | |||
89 | /** |
||
90 | * preview images object |
||
91 | * |
||
92 | * @var IImage |
||
93 | */ |
||
94 | private $preview; |
||
95 | /** @var string */ |
||
96 | private $versionId; |
||
97 | |||
98 | /** |
||
99 | * check if thumbnail or bigger version of thumbnail of file is cached |
||
100 | * |
||
101 | * @param string $user userid - if no user is given, OC_User::getUser will be used |
||
102 | * @param string $root path of root |
||
103 | * @param Node $file The path to the file where you want a thumbnail from |
||
104 | * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the |
||
105 | * shape of the image |
||
106 | * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the |
||
107 | * shape of the image |
||
108 | * @param bool $scalingUp Disable/Enable up-scaling of previews |
||
109 | * @param string $versionId |
||
110 | * @throws \Exception |
||
111 | */ |
||
112 | public function __construct( |
||
153 | |||
154 | /** |
||
155 | * returns the path of the file you want a thumbnail from |
||
156 | * |
||
157 | * @return File |
||
158 | */ |
||
159 | public function getFile() { |
||
162 | |||
163 | /** |
||
164 | * returns the max width of the preview |
||
165 | * |
||
166 | * @return integer |
||
167 | */ |
||
168 | public function getMaxX() { |
||
171 | |||
172 | /** |
||
173 | * returns the max height of the preview |
||
174 | * |
||
175 | * @return integer |
||
176 | */ |
||
177 | public function getMaxY() { |
||
180 | |||
181 | /** |
||
182 | * returns whether or not scalingup is enabled |
||
183 | * |
||
184 | * @return bool |
||
185 | */ |
||
186 | public function getScalingUp() { |
||
189 | |||
190 | /** |
||
191 | * returns the name of the thumbnailfolder |
||
192 | * |
||
193 | * @return string |
||
194 | */ |
||
195 | public function getThumbnailsFolder() { |
||
198 | |||
199 | /** |
||
200 | * returns the max scale factor |
||
201 | * |
||
202 | * @return string |
||
203 | */ |
||
204 | public function getMaxScaleFactor() { |
||
207 | |||
208 | /** |
||
209 | * returns the max width set in ownCloud's config |
||
210 | * |
||
211 | * @return integer |
||
212 | */ |
||
213 | public function getConfigMaxX() { |
||
216 | |||
217 | /** |
||
218 | * returns the max height set in ownCloud's config |
||
219 | * |
||
220 | * @return integer |
||
221 | */ |
||
222 | public function getConfigMaxY() { |
||
225 | |||
226 | /** |
||
227 | * Returns the FileInfo object associated with the file to preview |
||
228 | * |
||
229 | * @return false|Files\FileInfo|\OCP\Files\FileInfo |
||
230 | */ |
||
231 | protected function getFileInfo() { |
||
234 | |||
235 | /** |
||
236 | * @return array|null |
||
237 | */ |
||
238 | private function getChildren() { |
||
248 | |||
249 | /** |
||
250 | * Sets the path of the file you want a preview of |
||
251 | * |
||
252 | * @param Node $file |
||
253 | * @param string $versionId |
||
254 | * |
||
255 | * @return Preview |
||
256 | */ |
||
257 | public function setFile(Node $file, $versionId = null) { |
||
264 | |||
265 | /** |
||
266 | * Forces the use of a specific media type |
||
267 | * |
||
268 | * @param string $mimeType |
||
269 | */ |
||
270 | public function setMimetype($mimeType) { |
||
273 | |||
274 | /** |
||
275 | * Sets the max width of the preview. It's capped by the maximum allowed size set in the |
||
276 | * configuration |
||
277 | * |
||
278 | * @param int $maxX |
||
279 | * |
||
280 | * @throws \Exception |
||
281 | * @return \OC\Preview |
||
282 | */ |
||
283 | public function setMaxX($maxX = 1) { |
||
293 | |||
294 | /** |
||
295 | * Sets the max height of the preview. It's capped by the maximum allowed size set in the |
||
296 | * configuration |
||
297 | * |
||
298 | * @param int $maxY |
||
299 | * |
||
300 | * @throws \Exception |
||
301 | * @return \OC\Preview |
||
302 | */ |
||
303 | public function setMaxY($maxY = 1) { |
||
313 | |||
314 | /** |
||
315 | * Sets whether we're allowed to scale up when generating a preview. It's capped by the maximum |
||
316 | * allowed scale factor set in the configuration |
||
317 | * |
||
318 | * @param bool $scalingUp |
||
319 | * |
||
320 | * @return \OC\Preview |
||
321 | */ |
||
322 | public function setScalingup($scalingUp) { |
||
330 | |||
331 | /** |
||
332 | * Set whether to cover or fill the specified dimensions |
||
333 | * |
||
334 | * @param string $mode |
||
335 | * |
||
336 | * @return \OC\Preview |
||
337 | */ |
||
338 | public function setMode($mode) { |
||
343 | |||
344 | /** |
||
345 | * Sets whether we need to generate a preview which keeps the aspect ratio of the original file |
||
346 | * |
||
347 | * @param bool $keepAspect |
||
348 | * |
||
349 | * @return \OC\Preview |
||
350 | */ |
||
351 | public function setKeepAspect($keepAspect) { |
||
356 | |||
357 | /** |
||
358 | * Makes sure we were given a file to preview and that it exists in the filesystem |
||
359 | * |
||
360 | * @return bool |
||
361 | */ |
||
362 | public function isFileValid() { |
||
378 | |||
379 | /** |
||
380 | * Deletes the preview of a file with specific width and height |
||
381 | * |
||
382 | * This should never delete the max preview, use deleteAllPreviews() instead |
||
383 | * |
||
384 | * @return bool |
||
385 | */ |
||
386 | public function deletePreview() { |
||
397 | |||
398 | /** |
||
399 | * Deletes all previews of a file |
||
400 | */ |
||
401 | public function deleteAllPreviews() { |
||
425 | |||
426 | /** |
||
427 | * Checks if a preview matching the asked dimensions or a bigger version is already cached |
||
428 | * |
||
429 | * * We first retrieve the size of the max preview since this is what we be used to create |
||
430 | * all our preview. If it doesn't exist we return false, so that it can be generated |
||
431 | * * Using the dimensions of the max preview, we calculate what the size of the new |
||
432 | * thumbnail should be |
||
433 | * * And finally, we look for a suitable candidate in the cache |
||
434 | * |
||
435 | * @return string|false path to the cached preview if it exists or false |
||
436 | */ |
||
437 | public function isCached() { |
||
498 | |||
499 | /** |
||
500 | * Returns the dimensions of the max preview |
||
501 | * |
||
502 | * @param FileInfo[] $allThumbnails the list of all our cached thumbnails |
||
503 | * |
||
504 | * @return int[] |
||
505 | */ |
||
506 | private function getMaxPreviewSize($allThumbnails) { |
||
520 | |||
521 | /** |
||
522 | * Check if a specific thumbnail size is cached |
||
523 | * |
||
524 | * @param FileInfo[] $allThumbnails the list of all our cached thumbnails |
||
525 | * @param string $name |
||
526 | * @return bool |
||
527 | */ |
||
528 | private function thumbnailSizeExists(array $allThumbnails, $name) { |
||
537 | |||
538 | /** |
||
539 | * Determines the size of the preview we should be looking for in the cache |
||
540 | * |
||
541 | * @return integer[] |
||
542 | */ |
||
543 | private function simulatePreviewDimensions() { |
||
556 | |||
557 | /** |
||
558 | * Resizes the boundaries to match the aspect ratio |
||
559 | * |
||
560 | * @param int $askedWidth |
||
561 | * @param int $askedHeight |
||
562 | * |
||
563 | * @param int $originalWidth |
||
564 | * @param int $originalHeight |
||
565 | * @return integer[] |
||
566 | */ |
||
567 | private function applyAspectRatio($askedWidth, $askedHeight, $originalWidth = 0, $originalHeight = 0) { |
||
589 | |||
590 | /** |
||
591 | * Resizes the boundaries to cover the area |
||
592 | * |
||
593 | * @param int $askedWidth |
||
594 | * @param int $askedHeight |
||
595 | * @param int $previewWidth |
||
596 | * @param int $previewHeight |
||
597 | * @return integer[] |
||
598 | */ |
||
599 | private function applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight) { |
||
615 | |||
616 | /** |
||
617 | * Makes sure an upscaled preview doesn't end up larger than the max dimensions defined in the |
||
618 | * config |
||
619 | * |
||
620 | * @param int $askedWidth |
||
621 | * @param int $askedHeight |
||
622 | * |
||
623 | * @return integer[] |
||
624 | */ |
||
625 | private function fixSize($askedWidth, $askedHeight) { |
||
633 | |||
634 | /** |
||
635 | * Checks if a bigger version of a file preview is cached and if not |
||
636 | * return the preview of max allowed dimensions |
||
637 | * |
||
638 | * @param FileInfo[] $allThumbnails the list of all our cached thumbnails |
||
639 | * |
||
640 | * @return string path to bigger thumbnail |
||
641 | */ |
||
642 | private function isCachedBigger($allThumbnails) { |
||
660 | |||
661 | /** |
||
662 | * Get possible bigger thumbnails of the given image with the proper aspect ratio |
||
663 | * |
||
664 | * @param FileInfo[] $allThumbnails the list of all our cached thumbnails |
||
665 | * |
||
666 | * @return string[] an array of paths to bigger thumbnails |
||
667 | */ |
||
668 | private function getPossibleThumbnails($allThumbnails) { |
||
692 | |||
693 | /** |
||
694 | * Looks at the preview filename from the cache and extracts the size of the preview |
||
695 | * |
||
696 | * @param string $name |
||
697 | * |
||
698 | * @return array<int,int,float> |
||
699 | */ |
||
700 | private function getDimensionsFromFilename($name) { |
||
708 | |||
709 | /** |
||
710 | * @param int $x |
||
711 | * @param int $y |
||
712 | * |
||
713 | * @return bool |
||
714 | */ |
||
715 | private function unscalable($x, $y) { |
||
734 | |||
735 | /** |
||
736 | * Returns a preview of a file |
||
737 | * |
||
738 | * The cache is searched first and if nothing usable was found then a preview is |
||
739 | * generated by one of the providers |
||
740 | * |
||
741 | * @return IImage |
||
742 | */ |
||
743 | public function getPreview() { |
||
770 | |||
771 | /** |
||
772 | * Sends the preview, including the headers to client which requested it |
||
773 | * |
||
774 | * @param null|string $mimeTypeForHeaders the media type to use when sending back the reply |
||
775 | * |
||
776 | * @throws NotFoundException |
||
777 | */ |
||
778 | public function showPreview($mimeTypeForHeaders = null) { |
||
796 | |||
797 | /** |
||
798 | * Retrieves the preview from the cache and resizes it if necessary |
||
799 | * |
||
800 | * @param string $cached the path to the cached preview |
||
801 | */ |
||
802 | private function getCachedPreview($cached) { |
||
828 | |||
829 | /** |
||
830 | * Resizes, crops, fixes orientation and stores in the cache |
||
831 | */ |
||
832 | private function resizeAndStore() { |
||
900 | |||
901 | /** |
||
902 | * Calculates the new dimensions of the preview |
||
903 | * |
||
904 | * The new dimensions can be larger or smaller than the ones of the preview we have to resize |
||
905 | * |
||
906 | * @param IImage $image |
||
907 | * @param int $askedWidth |
||
908 | * @param int $askedHeight |
||
909 | * @param int $previewWidth |
||
910 | * @param int $previewHeight |
||
911 | * |
||
912 | * @return int[] |
||
913 | */ |
||
914 | private function scale($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight) { |
||
952 | |||
953 | /** |
||
954 | * Crops a preview which is larger than the dimensions we've received |
||
955 | * |
||
956 | * @param IImage $image |
||
957 | * @param int $askedWidth |
||
958 | * @param int $askedHeight |
||
959 | * @param int $previewWidth |
||
960 | * @param int $previewHeight |
||
961 | */ |
||
962 | private function crop($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight = null) { |
||
970 | |||
971 | /** |
||
972 | * Crops an image if it's larger than the dimensions we've received and fills the empty space |
||
973 | * with a transparent background |
||
974 | * |
||
975 | * @param IImage $image |
||
976 | * @param int $askedWidth |
||
977 | * @param int $askedHeight |
||
978 | * @param int $previewWidth |
||
979 | * @param int $previewHeight |
||
980 | */ |
||
981 | private function cropAndFill($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight) { |
||
1016 | |||
1017 | /** |
||
1018 | * Saves a preview in the cache to speed up future calls |
||
1019 | * |
||
1020 | * Do not nullify the preview as it might send the whole process in a loop |
||
1021 | * |
||
1022 | * @param int $previewWidth |
||
1023 | * @param int $previewHeight |
||
1024 | */ |
||
1025 | private function storePreview($previewWidth, $previewHeight) { |
||
1036 | |||
1037 | /** |
||
1038 | * Returns the path to a preview based on its dimensions and aspect |
||
1039 | * |
||
1040 | * @param int|null $maxX |
||
1041 | * @param int|null $maxY |
||
1042 | * |
||
1043 | * @return string |
||
1044 | */ |
||
1045 | private function buildCachePath($maxX = null, $maxY = null) { |
||
1070 | |||
1071 | /** |
||
1072 | * Returns the path to the folder where the previews are stored, identified by the fileId |
||
1073 | * |
||
1074 | * @return string |
||
1075 | */ |
||
1076 | private function getPreviewPath($fileId = null) { |
||
1086 | |||
1087 | /** |
||
1088 | * Asks the provider to send a preview of the file which respects the maximum dimensions |
||
1089 | * defined in the configuration and after saving it in the cache, it is then resized to the |
||
1090 | * asked dimensions |
||
1091 | * |
||
1092 | * This is only called once in order to generate a large PNG of dimensions defined in the |
||
1093 | * configuration file. We'll be able to quickly resize it later on. |
||
1094 | * We never upscale the original conversion as this will be done later by the resizing |
||
1095 | * operation |
||
1096 | * |
||
1097 | */ |
||
1098 | private function generatePreview() { |
||
1149 | |||
1150 | /** |
||
1151 | * Defines the media icon, for the media type of the original file, as the preview |
||
1152 | */ |
||
1153 | private function getMimeIcon() { |
||
1165 | |||
1166 | /** |
||
1167 | * Stores the max preview in the cache |
||
1168 | * |
||
1169 | * @param string $previewPath path to the preview |
||
1170 | */ |
||
1171 | private function storeMaxPreview($previewPath) { |
||
1196 | |||
1197 | /** |
||
1198 | * Limits a dimension to the maximum dimension provided as argument |
||
1199 | * |
||
1200 | * @param int $dim |
||
1201 | * @param int $maxDim |
||
1202 | * @param string $dimName |
||
1203 | * |
||
1204 | * @return integer |
||
1205 | */ |
||
1206 | private function limitMaxDim($dim, $maxDim, $dimName) { |
||
1218 | |||
1219 | /** |
||
1220 | * @param array $args |
||
1221 | */ |
||
1222 | public static function post_write($args) { |
||
1225 | |||
1226 | /** |
||
1227 | * @param array $args |
||
1228 | */ |
||
1229 | public static function prepare_delete_files($args) { |
||
1232 | |||
1233 | /** |
||
1234 | * @param array $args |
||
1235 | * @param string $prefix |
||
1236 | */ |
||
1237 | public static function prepare_delete(array $args, $prefix = '') { |
||
1256 | |||
1257 | /** |
||
1258 | * @param string $absolutePath |
||
1259 | * @param \OCP\Files\FileInfo $info |
||
1260 | */ |
||
1261 | private static function addPathToDeleteFileMapper($absolutePath, $info) { |
||
1264 | |||
1265 | /** |
||
1266 | * @param Folder $node |
||
1267 | * |
||
1268 | * @return array |
||
1269 | */ |
||
1270 | private static function getAllChildren($node) { |
||
1287 | |||
1288 | /** |
||
1289 | * @param array $args |
||
1290 | */ |
||
1291 | public static function post_delete_files($args) { |
||
1294 | |||
1295 | /** |
||
1296 | * @param array $args |
||
1297 | */ |
||
1298 | public static function post_delete_versions($args) { |
||
1301 | |||
1302 | /** |
||
1303 | * @param array $args |
||
1304 | * @param string $prefix |
||
1305 | */ |
||
1306 | public static function post_delete($args, $prefix = '') { |
||
1328 | } |
||
1329 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.
Either this assignment is in error or an instanceof check should be added for that assignment.