| Total Complexity | 126 | 
| Total Lines | 679 | 
| Duplicated Lines | 0 % | 
| Changes | 0 | ||
Complex classes like FileBehavior 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 FileBehavior, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 11 | class FileBehavior extends Behavior | ||
| 12 | {
 | ||
| 13 | |||
| 14 | /** | ||
| 15 |      * {@inheritDoc}
 | ||
| 16 | */ | ||
| 17 | protected $_defaultConfig = [ | ||
| 18 | 'library' => 'gd', | ||
| 19 | 'types' => [ // Default allowed types | ||
| 20 | 'image/bmp', | ||
| 21 | 'image/gif', | ||
| 22 | 'image/jpeg', | ||
| 23 | 'image/jpg', | ||
| 24 | 'image/pjpeg', | ||
| 25 | 'image/pjpg', | ||
| 26 | 'image/png', | ||
| 27 | 'image/x-png', | ||
| 28 | 'image/webp', | ||
| 29 | ], | ||
| 30 | 'extensions' => [ // Default allowed extensions | ||
| 31 | 'bmp', | ||
| 32 | 'gif', | ||
| 33 | 'jpeg', | ||
| 34 | 'jpg', | ||
| 35 | 'pjpg', | ||
| 36 | 'pjpeg', | ||
| 37 | 'png', | ||
| 38 | 'webp', | ||
| 39 | ], | ||
| 40 | 'path' => 'files', | ||
| 41 | 'background' => [255, 255, 255, 127], | ||
| 42 | 'watermark' => '', | ||
| 43 | 'thumbs' => [], | ||
| 44 | ]; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Array of files to upload | ||
| 48 | * | ||
| 49 | * @var array | ||
| 50 | */ | ||
| 51 | protected $_files = []; | ||
| 52 | |||
| 53 | /** | ||
| 54 |      * {@inheritDoc}
 | ||
| 55 | */ | ||
| 56 | public function initialize(array $config) | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | /** | ||
| 72 |      * {@inheritDoc}
 | ||
| 73 | */ | ||
| 74 | public function beforeMarshal(Event $event, $data = [], $options = []) | ||
| 75 |     {
 | ||
| 76 |         if (!empty($config = $this->getConfig($this->getTable()->getAlias()))) {
 | ||
| 77 |             foreach ($config as $field => $fieldOptions) {
 | ||
| 78 |                 if (array_key_exists($field, $data)) {
 | ||
| 79 | // Save file array to suffixed field | ||
| 80 | $data['_' . $field] = $data[$field]; | ||
| 81 | |||
| 82 | // Detect multiple files by array keys | ||
| 83 |                     if (is_numeric(key($data[$field]))) {
 | ||
| 84 |                         foreach (array_keys($data[$field]) as $key) {
 | ||
| 85 |                             if (!empty($data[$field][$key]['tmp_name'])) {
 | ||
| 86 | $this->_files[$field . '_' . $key] = array_merge($fieldOptions, [ | ||
| 87 | 'name' => $this->_prepareName($data[$field][$key]['name']), | ||
| 88 | 'source' => $data[$field][$key]['tmp_name'], | ||
| 89 | ]); | ||
| 90 | |||
| 91 | $data[$field][$key] = $this->_files[$field . '_' . $key]['name']; | ||
| 92 |                             } else {
 | ||
| 93 | unset($data[$field][$key]); | ||
| 94 | } | ||
| 95 | } | ||
| 96 |                     } else {
 | ||
| 97 |                         if (!empty($data[$field]['tmp_name'])) {
 | ||
| 98 | $this->_files[$field] = array_merge($fieldOptions, [ | ||
| 99 | 'name' => $this->_prepareName($data[$field]['name']), | ||
| 100 | 'source' => $data[$field]['tmp_name'], | ||
| 101 | ]); | ||
| 102 | |||
| 103 | $data[$field] = $this->_files[$field]['name']; | ||
| 104 |                         } else {
 | ||
| 105 | unset($data[$field]); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 |      * {@inheritDoc}
 | ||
| 115 | */ | ||
| 116 | public function afterSave(Event $event, EntityInterface $entity, $options = []) | ||
| 117 |     {
 | ||
| 118 | $this->_prepareFile($entity); | ||
| 119 | } | ||
| 120 | |||
| 121 | /** | ||
| 122 |      * {@inheritDoc}
 | ||
| 123 | */ | ||
| 124 | public function beforeDelete(Event $event, EntityInterface $entity) | ||
| 125 |     {
 | ||
| 126 | return $this->deleteFile($event); | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * Copy source file to destination and if field (image) has configurations for thumbs, then create them. | ||
| 131 | * | ||
| 132 | * @param EntityInterface $entity Entity | ||
| 133 | */ | ||
| 134 | protected function _prepareFile(EntityInterface $entity) | ||
| 135 |     {
 | ||
| 136 |         foreach ($this->_files as $field => $fieldOptions) {
 | ||
| 137 |             if (!is_dir($fieldOptions['path'])) {
 | ||
| 138 | $this->_prepareDir($fieldOptions['path']); | ||
| 139 | } | ||
| 140 | |||
| 141 | $file = $fieldOptions['path'] . DS . $fieldOptions['name']; | ||
| 142 | |||
| 143 |             if (move_uploaded_file($fieldOptions['source'], $file) || (file_exists($fieldOptions['source']) && rename($fieldOptions['source'], $file))) {
 | ||
| 144 | $fileType = mb_strtolower(getimagesize($fieldOptions['source'])['mime']); | ||
| 145 | |||
| 146 |                 $fieldTypes = array_map(function ($fieldType) {
 | ||
| 147 | return mb_strtolower($fieldType); | ||
| 148 | }, $fieldOptions['types']); | ||
| 149 | |||
| 150 | // Create thumbs | ||
| 151 |                 if (mb_strpos($fileType, 'image/') !== false && in_array($fileType, $fieldTypes)) {
 | ||
| 152 | $this->_prepareThumbs($file, $fieldOptions); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | /** | ||
| 159 | * Delete file with created thumbs | ||
| 160 | * | ||
| 161 | * @param Event $event Reference to event | ||
| 162 | * @return boolean True if is success | ||
| 163 | */ | ||
| 164 | public function deleteFile(Event $event) | ||
| 165 |     {
 | ||
| 166 | // Get field list of model schema | ||
| 167 | $modelSchema = $model->schema(); | ||
|  | |||
| 168 | |||
| 169 |         foreach ($this->settings[$model->alias] as $fieldName => $fieldOptions) {
 | ||
| 170 | // Check is field in model schema | ||
| 171 |             if (isset($modelSchema[$fieldName])) {
 | ||
| 172 | $dataField = $model->findById($model->id); | ||
| 173 | |||
| 174 |                 if (is_array($dataField) && !empty($dataField[$model->alias][$fieldName])) {
 | ||
| 175 | // Pattern for original file with thumbs | ||
| 176 | $filePattern = $this->settings[$model->alias][$fieldName]['path'] . DS . substr($dataField[$model->alias][$fieldName], 0, 14); | ||
| 177 | |||
| 178 |                     foreach (glob($filePattern . '*') as $fileName) {
 | ||
| 179 | // Remove file | ||
| 180 | @unlink($fileName); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | return true; | ||
| 187 | } | ||
| 188 | |||
| 189 | /** | ||
| 190 | * Generate thumbs by names with parameters | ||
| 191 | * | ||
| 192 | * @param string $originalFile Path to original file | ||
| 193 | * @param array $thumbParams Settings for uploaded files | ||
| 194 | * @return boolean Output image to save file | ||
| 195 | */ | ||
| 196 | protected function _prepareThumbs($originalFile, $settingParams) | ||
| 374 | } | ||
| 375 | } | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | /** | ||
| 381 | * Get extension from original name | ||
| 382 | * | ||
| 383 | * @param string $originalName Name of original file | ||
| 384 | * @return string Extension of uploaded file | ||
| 385 | */ | ||
| 386 | public function getExtension($originalName) | ||
| 387 |     {
 | ||
| 388 | $fileExtension = pathinfo(mb_strtolower($originalName), PATHINFO_EXTENSION); | ||
| 389 | |||
| 390 |         switch ($fileExtension) {
 | ||
| 391 | case 'jpg': | ||
| 392 | case 'jpeg': | ||
| 393 | case 'pjpg': | ||
| 394 | case 'pjpeg': | ||
| 395 | // Standarize JPEG image file extension | ||
| 396 | return 'jpg'; | ||
| 397 | |||
| 398 | break; | ||
| 399 | default: | ||
| 400 | return $fileExtension; | ||
| 401 | |||
| 402 | break; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | /** | ||
| 407 | * Get position of watermark image | ||
| 408 | * | ||
| 409 | * @param integer $newWidth New width of uploaded image | ||
| 410 | * @param integer $newHeight New height of uploaded image | ||
| 411 | * @param integer $watermarkWidth Original width of watermark image | ||
| 412 | * @param integer $watermarkHeight Original height of watermark image | ||
| 413 | * @param integer $offsetX Horizontal offset | ||
| 414 | * @param integer $offsetY Vertical offset | ||
| 415 | * @param integer $positionValue Value for position watermark, value between 1 and 9 | ||
| 416 | * @return array Coordinates of position watermark | ||
| 417 | */ | ||
| 418 | public function getPosition($newWidth, $newHeight, $watermarkWidth, $watermarkHeight, $offsetX = 0, $offsetY = 0, $positionValue = 1) | ||
| 419 |     {
 | ||
| 420 |         switch (intval($positionValue)) {
 | ||
| 421 | case 1: // Top left | ||
| 422 | return [$offsetX, $offsetY]; | ||
| 423 | |||
| 424 | break; | ||
| 425 | case 2: // Top center | ||
| 426 | return [($newWidth / 2) - ($watermarkWidth / 2), 0 + $offsetY]; | ||
| 427 | |||
| 428 | break; | ||
| 429 | case 3: // Top right | ||
| 430 | return [($newWidth - $watermarkWidth) - $offsetX, 0 + $offsetY]; | ||
| 431 | |||
| 432 | break; | ||
| 433 | case 4: // Middle left | ||
| 434 | return [$offsetX, ($newHeight / 2) - ($watermarkHeight / 2)]; | ||
| 435 | |||
| 436 | break; | ||
| 437 | case 5: // Middle center | ||
| 438 | return [($newWidth / 2) - ($watermarkWidth / 2), ($newHeight / 2) - ($watermarkHeight / 2)]; | ||
| 439 | |||
| 440 | break; | ||
| 441 | case 6: // Middle right | ||
| 442 | return [($newWidth - $watermarkWidth) - $offsetX, ($newHeight / 2) - ($watermarkHeight / 2)]; | ||
| 443 | |||
| 444 | break; | ||
| 445 | case 7: // Bottom left | ||
| 446 | return [$offsetX, ($newHeight - $watermarkHeight) - $offsetY]; | ||
| 447 | |||
| 448 | break; | ||
| 449 | case 8: // Bottom center | ||
| 450 | return [($newWidth / 2) - ($watermarkWidth / 2), ($newHeight - $watermarkHeight) - $offsetY]; | ||
| 451 | |||
| 452 | break; | ||
| 453 | case 9: // Bottom right | ||
| 454 | return [($newWidth - $watermarkWidth) - $offsetX, ($newHeight - $watermarkHeight) - $offsetY]; | ||
| 455 | |||
| 456 | break; | ||
| 457 | default: | ||
| 458 | return [$offsetX, $offsetY]; | ||
| 459 | |||
| 460 | break; | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | /** | ||
| 465 | * Generate random name of uploaded file. | ||
| 466 | * If action is for update with not used file then it will be removed. | ||
| 467 | * | ||
| 468 | * @todo Prepare method for working without primary key field | ||
| 469 | * @todo Generate names of files by user method | ||
| 470 | * @param string $fieldName Name of file field | ||
| 471 | * @return string New name of file | ||
| 472 | */ | ||
| 473 | protected function _prepareName($fieldName) | ||
| 474 |     {
 | ||
| 475 | return Text::uuid() . '_default.' . $this->getExtension($fieldName); | ||
| 476 | } | ||
| 477 | |||
| 478 | /** | ||
| 479 | * Set path to directory for save uploaded files. | ||
| 480 | * If directory isn't exists, will be created with full privileges. | ||
| 481 | * | ||
| 482 | * @param string $dirPath Path to directory | ||
| 483 | * @return string Path to directory | ||
| 484 | */ | ||
| 485 | protected function _prepareDir($dirPath) | ||
| 486 |     {
 | ||
| 487 |         $dirPath = WWW_ROOT . str_replace('/', DS, $dirPath);
 | ||
| 488 | |||
| 489 |         if (!is_dir($dirPath) && mb_strlen($dirPath) > 0) {
 | ||
| 490 | mkdir($dirPath, 0777, true); | ||
| 491 | } | ||
| 492 | |||
| 493 | chmod($dirPath, 0777); | ||
| 494 | |||
| 495 | return $dirPath; | ||
| 496 | } | ||
| 497 | |||
| 498 | /** | ||
| 499 | * Create image dimension by new width. | ||
| 500 | * | ||
| 501 | * @param integer $originalWidth Original width of uploaded image | ||
| 502 | * @param integer $originalHeight Original height of uploaded image | ||
| 503 | * @param integer $newWidth Set new image width | ||
| 504 | * @return array New width and height | ||
| 505 | */ | ||
| 506 | protected function _byWidth($originalWidth, $originalHeight, $newWidth) | ||
| 518 | } | ||
| 519 | |||
| 520 | /** | ||
| 521 | * Create image dimension by new height. | ||
| 522 | * | ||
| 523 | * @param integer $originalWidth Original width of uploaded image | ||
| 524 | * @param integer $originalHeight Original height of uploaded image | ||
| 525 | * @param integer $newHeight Set new image height | ||
| 526 | * @return array New width and height | ||
| 527 | */ | ||
| 528 | protected function _byHeight($originalWidth, $originalHeight, $newHeight) | ||
| 529 |     {
 | ||
| 530 | $newHeight = intval($newHeight); | ||
| 531 | |||
| 532 |         if ($newHeight > $originalHeight) {
 | ||
| 533 | $newHeight = $originalHeight; | ||
| 534 | $newWidth = $originalWidth; | ||
| 535 |         } else {
 | ||
| 536 | $newWidth = intval($newHeight * ($originalWidth / $originalHeight)); | ||
| 537 | } | ||
| 538 | |||
| 539 | return [$newWidth, $newHeight]; | ||
| 540 | } | ||
| 541 | |||
| 542 | /** | ||
| 543 | * Create image dimension by shorter side. | ||
| 544 | * | ||
| 545 | * @param integer $originalWidth Original width of uploaded image | ||
| 546 | * @param integer $originalHeight Original height of uploaded image | ||
| 547 | * @param integer $newWidth Set new image min width | ||
| 548 | * @param integer $newHeight Set new image min height | ||
| 549 | * @return array New width and height | ||
| 550 | */ | ||
| 551 | protected function _byShorter($originalWidth, $originalHeight, $newWidth, $newHeight) | ||
| 552 |     {
 | ||
| 553 | $newWidth = intval($newWidth); | ||
| 554 | $newHeight = intval($newHeight); | ||
| 555 | |||
| 556 |         if ($originalWidth < $originalHeight) {
 | ||
| 557 | list($newWidth, $newHeight) = $this->_byWidth($originalWidth, $originalHeight, $newWidth); | ||
| 558 |         } else {
 | ||
| 559 | list($newWidth, $newHeight) = $this->_byHeight($originalWidth, $originalHeight, $newHeight); | ||
| 560 | } | ||
| 561 | |||
| 562 | return [$newWidth, $newHeight]; | ||
| 563 | } | ||
| 564 | |||
| 565 | /** | ||
| 566 | * Create image dimension by longer side. | ||
| 567 | * | ||
| 568 | * @param integer $originalWidth Original width of uploaded image | ||
| 569 | * @param integer $originalHeight Original height of uploaded image | ||
| 570 | * @param integer $newWidth Set new image max width | ||
| 571 | * @param integer $newHeight Set new image max height | ||
| 572 | * @return array New width and height | ||
| 573 | */ | ||
| 574 | protected function _byLonger($originalWidth, $originalHeight, $newWidth, $newHeight) | ||
| 575 |     {
 | ||
| 576 | $newWidth = intval($newWidth); | ||
| 577 | $newHeight = intval($newHeight); | ||
| 578 | |||
| 579 |         if ($originalWidth > $originalHeight) {
 | ||
| 580 | list($newWidth, $newHeight) = $this->_byWidth($originalWidth, $originalHeight, $newWidth); | ||
| 581 |         } else {
 | ||
| 582 | list($newWidth, $newHeight) = $this->_byHeight($originalWidth, $originalHeight, $newHeight); | ||
| 583 | } | ||
| 584 | |||
| 585 | return [$newWidth, $newHeight]; | ||
| 586 | } | ||
| 587 | |||
| 588 | /** | ||
| 589 | * Create image dimension by fit. | ||
| 590 | * | ||
| 591 | * @param integer $originalWidth Original width of uploaded image | ||
| 592 | * @param integer $originalHeight Original height of uploaded image | ||
| 593 | * @param integer $newWidth Set new image width | ||
| 594 | * @param integer $newHeight Set new image height | ||
| 595 | * @param boolean $originalKeep Save original shape | ||
| 596 | * @return array New width and height and offsets of position with keeping original shape | ||
| 597 | */ | ||
| 598 | protected function _byFit($originalWidth, $originalHeight, $newWidth, $newHeight, $originalKeep = false) | ||
| 599 |     {
 | ||
| 600 | $newWidth = intval($newWidth); | ||
| 601 | $newHeight = intval($newHeight); | ||
| 602 | |||
| 603 | $offsetX = 0; | ||
| 604 | $offsetY = 0; | ||
| 605 | $cropX = 0; | ||
| 606 | $cropY = 0; | ||
| 607 | |||
| 608 |         if ($originalKeep === true) {
 | ||
| 609 |             if ($originalWidth == $originalHeight) {
 | ||
| 610 | $newSizes = $this->_byLonger($originalWidth, $originalHeight, min($newWidth, $newHeight), min($newWidth, $newHeight)); | ||
| 611 |             } else {
 | ||
| 612 | $newSizes = $this->_byLonger($originalWidth, $originalHeight, $newWidth, $newHeight); | ||
| 613 | |||
| 614 |                 if ($newWidth < $newSizes[0] || $newHeight < $newSizes[1]) {
 | ||
| 615 | $newSizes = $this->_byShorter($originalWidth, $originalHeight, $newWidth, $newHeight); | ||
| 616 | } | ||
| 617 | } | ||
| 618 |         } else {
 | ||
| 619 |             if ($originalWidth == $originalHeight) {
 | ||
| 620 | $newSizes = $this->_byShorter($originalWidth, $originalHeight, max($newWidth, $newHeight), max($newWidth, $newHeight)); | ||
| 621 |             } else {
 | ||
| 622 | $newSizes = $this->_byShorter($originalWidth, $originalHeight, $newWidth, $newHeight); | ||
| 623 | |||
| 624 |                 if ($newWidth > $newSizes[0] || $newHeight > $newSizes[1]) {
 | ||
| 625 | $newSizes = $this->_byLonger($originalWidth, $originalHeight, $newWidth, $newHeight); | ||
| 626 | } | ||
| 627 | } | ||
| 628 | } | ||
| 629 | |||
| 630 |         if ($newWidth < $newSizes[0]) {
 | ||
| 631 | $cropX = ($newSizes[0] - $newWidth) / 2; | ||
| 632 |         } else {
 | ||
| 633 | $offsetX = ($newWidth - $newSizes[0]) / 2; | ||
| 634 | } | ||
| 635 | |||
| 636 |         if ($newHeight < $newSizes[1]) {
 | ||
| 637 | $cropY = ($newSizes[1] - $newHeight) / 2; | ||
| 638 |         } else {
 | ||
| 639 | $offsetY = ($newHeight - $newSizes[1]) / 2; | ||
| 640 | } | ||
| 641 | |||
| 642 | return [$newSizes[0], $newSizes[1], $offsetX, $offsetY, $cropX, $cropY]; | ||
| 643 | } | ||
| 644 | |||
| 645 | /** | ||
| 646 | * Create image dimension to square | ||
| 647 | * | ||
| 648 | * @param integer $originalWidth Original width of uploaded image | ||
| 649 | * @param integer $originalHeight Original height of uploaded image | ||
| 650 | * @param integer $newSide Set new image side | ||
| 651 | * @param boolean $originalKeep Save original shape | ||
| 652 | * @return array New width and height with coordinates of crop or offsets of position | ||
| 653 | */ | ||
| 654 | protected function _bySquare($originalWidth, $originalHeight, $newSide, $originalKeep = false) | ||
| 690 | } | ||
| 691 | } |