GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 951ad1...29ba67 )
by Alexey
08:55
created

ImageWriter   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 350
Duplicated Lines 6.86 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 44
c 1
b 0
f 0
lcom 1
cbo 12
dl 24
loc 350
rs 8.3396

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 20 1
A showStatistics() 0 3 1
A writeAll() 18 18 3
A writePartial() 0 7 2
C safeWriteItem() 0 42 8
B write() 6 56 9
A findRecordByNameAndParent() 0 10 1
A checkExistFile() 0 4 1
A deleteOldImages() 0 10 4
A createUniqueFileName() 0 4 1
B createImages() 0 22 5
A createImageRecord() 0 13 2
A updateImageRecord() 0 11 2
A getProductIdByAttribute() 0 16 3
A normalizeFileName() 0 7 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ImageWriter 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 ImageWriter, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 17 and the first side effect is on line 9.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * @author Alexey Tatarinov <[email protected]>
4
 * @link https://github.com/shogodev/argilla/
5
 * @copyright Copyright &copy; 2003-2015 Shogo
6
 * @license http://argilla.ru/LICENSE
7
 */
8
9
Yii::import('frontend.share.helpers.*');
10
Yii::import('backend.modules.product.modules.import.components.exceptions.*');
11
Yii::import('backend.modules.product.modules.import.components.abstracts.AbstractImportWriter');
12
13
/**
14
 * Class ImageWriter
15
 * @property string $sourcePath
16
 */
17
class ImageWriter extends AbstractImportWriter
18
{
19
  /**
20
   * Пропускаем с предупреждением
21
   */
22
  const FILE_ACTION_SKIP_WITH_WARNING = 1;
23
24
  /**
25
   * Пропускаем с без предупреждения(по тихому)
26
   */
27
  const FILE_ACTION_SKIP_SILENT = 2;
28
29
  /**
30
   * Заменяем существующий файл
31
   */
32
  const FILE_ACTION_REPLACE_OLD = 3;
33
34
  /**
35
   * Переименовывает новый файл в уникальное имя
36
   */
37
  const FILE_ACTION_RENAME_NEW_FILE = 4;
38
39
  const DB_ACTION_EXISTING_RECORD_SKIP_WITH_WARNING = 1;
40
41
  const DB_ACTION_EXISTING_RECORD_SKIP_SILENT = 2;
42
43
  //const DB_ACTION_EXISTING_RECORD_REPLACE = 3;
44
45
  /**
46
   * @var int $actionWithSameFiles действия с одинаковыми файлами (FILE_ACTION_SKIP_WITH_WARNING, FILE_ACTION_SKIP_SILENT, FILE_ACTION_REPLACE_OLD)
47
   */
48
  public $actionWithSameFiles = self::FILE_ACTION_SKIP_WITH_WARNING;
49
50
  public $actionWithExistingRecord = self::DB_ACTION_EXISTING_RECORD_SKIP_WITH_WARNING;
51
52
  public $previews = array();
53
54
  public $defaultJpegQuality = 90;
55
56
  /***
57
   * @var bool $phpThumbErrorExceptionToWarning - игнорировать phpThumb исключения
58
   */
59
  public $phpThumbErrorExceptionToWarning = true;
60
61
  /**
62
   * @var EPhpThumb
63
   */
64
  protected $phpThumb;
65
66
  protected $productIdsCache;
67
68
  protected $tables = array(
69
    'product' => '{{product}}',
70
    'productImage' => '{{product_img}}'
71
  );
72
73
  protected $outputPath;
74
75
  protected $sourcePath;
76
77
  /**
78
   * @var CDbCommandBuilder $commandBuilder
79
   */
80
  protected $commandBuilder;
81
82
  public function __construct(ConsoleFileLogger $logger, $sourcePath = 'f/product/src', $outputPath = 'f/product')
83
  {
84
    parent::__construct($logger);
85
86
    $basePath = GlobalConfig::instance()->rootPath;
87
88
    $this->outputPath = $basePath.ImportHelper::wrapInSlashBegin($outputPath);
89
    $this->sourcePath = $basePath.ImportHelper::wrapInSlashBegin($sourcePath);
90
91
    $this->commandBuilder = Yii::app()->db->schema->commandBuilder;
92
93
    $this->phpThumb = Yii::createComponent(array(
94
      'class' => 'ext.phpthumb.EPhpThumb',
95
      'options' => array(
96
        'jpegQuality' => $this->defaultJpegQuality,
97
      ),
98
    ));
99
100
    $this->phpThumb->init();
101
  }
102
103
  public function showStatistics()
104
  {
105
  }
106
107 View Code Duplication
  public function writeAll(array $data)
0 ignored issues
show
Duplication introduced by
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...
108
  {
109
    $itemsAmount = count($data);
110
    if( $itemsAmount == 0 )
111
      return;
112
113
    $progress = new ConsoleProgressBar($itemsAmount);
114
    $this->logger->log('Начало обработки файлов');
115
    $progress->start();
116
    foreach($data as $uniqueAttributeValue => $images)
117
    {
118
      $this->safeWriteItem($uniqueAttributeValue, $images);
119
      $progress->setValueMap('memory', Yii::app()->format->formatSize(memory_get_usage()));
120
      $progress->advance();
121
    }
122
    $progress->finish();
123
    $this->logger->log('Обработка файлов завершена');
124
  }
125
126
  public function writePartial(array $data)
127
  {
128
    foreach($data as $uniqueAttributeValue => $images)
129
    {
130
      $this->safeWriteItem($uniqueAttributeValue, $images);
131
    }
132
  }
133
134
  protected function safeWriteItem($uniqueAttributeValue, $images)
135
  {
136
    try
137
    {
138
      if( !($productId = $this->getProductIdByAttribute($this->uniqueAttribute, $uniqueAttributeValue)) )
139
        throw new WarningException('Не удалсь найти продукт по атрибуту '.$this->uniqueAttribute.' = '.$uniqueAttributeValue);
140
141
      foreach($images as $itemData)
142
      {
143
        $image = $itemData['file'];
144
        $file = $this->sourcePath.ImportHelper::wrapInSlashBegin($image);
145
146
        if( !file_exists($file) )
147
          throw new WarningException('Файл '.$file.' не найден (строка '.$itemData['rowIndex'].')');
148
149
        try
150
        {
151
          $this->beginTransaction();
152
153
          $fileName = pathinfo($file, PATHINFO_BASENAME);
154
          $fileName = $this->normalizeFileName($fileName);
155
156
          $firstImage = reset($images)['file'];
157
          $type = ($image == $firstImage ? 'main' : 'gallery');
158
          $this->write($file, $fileName, $productId, $type);
159
160
          $this->commitTransaction();
161
        }
162
        catch(Exception $e)
163
        {
164
          $this->rollbackTransaction();
165
166
          if( !($e instanceof SilentException) )
167
            throw $e;
168
        }
169
      }
170
    }
171
    catch(WarningException $e)
172
    {
173
      $this->logger->warning($e->getMessage());
174
    }
175
  }
176
177
  protected function write($file, $fileName, $productId, $type)
178
  {
179
    $record = $this->findRecordByNameAndParent($fileName, $productId);
180
    $fileExists = $this->checkExistFile($fileName);
181
182
    if( $fileExists )
183
    {
184
      switch( $this->actionWithSameFiles )
185
      {
186
        case self::FILE_ACTION_SKIP_WITH_WARNING:
187
          throw new WarningException('Файл '.$fileName.' существует (старое имя '.$file.')');
188
          break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
189
190
        case self::FILE_ACTION_SKIP_SILENT:
191
          throw new SilentException('Файл '.$fileName.' существует (старое имя '.$file.')');
192
          break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
193
194
        case self::FILE_ACTION_RENAME_NEW_FILE:
195
          $fileName = $this->createUniqueFileName($fileName);
196
          break;
197
198
        case self::FILE_ACTION_REPLACE_OLD:
199
          break;
200
      }
201
    }
202
203
    if( $record )
204
    {
205
      switch( $this->actionWithExistingRecord )
206
      {
207 View Code Duplication
        case self::DB_ACTION_EXISTING_RECORD_SKIP_WITH_WARNING:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
208
          throw new WarningException('Запись c name = '.$fileName.' и parent = '.$record['parent'].' существует (id = '.$record['id'].')');
209
          break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
210
211 View Code Duplication
        case self::DB_ACTION_EXISTING_RECORD_SKIP_SILENT:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
212
          throw new SilentException('Запись c name = '.$fileName.' и parent = '.$record['parent'].' существует (id = '.$record['id'].')');
213
          break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
214
215
        /*        case self::DB_ACTION_EXISTING_RECORD_REPLACE:
216
          //to-do: Стерать все записи продукта и файлы, и записать новые
217
          if( $fileExists )
218
            $this->deleteOldImages($record['name']);
219
220
          if( $record['name'] != $fileName )
221
            $this->updateImageRecord($record['id'], $productId);
222
223
          $this->createImages($file, $fileName);
224
          break;*/
225
      }
226
    }
227
    else
228
    {
229
      $this->createImageRecord($fileName, $productId, $type);
230
      $this->createImages($file, $fileName);
231
    }
232
  }
233
234
  /**
235
   * @param $fileName
236
   * @param $productId
237
   *
238
   * @return mixed
239
   */
240
  protected function findRecordByNameAndParent($fileName, $productId)
241
  {
242
    $criteria = new CDbCriteria();
243
    $criteria->compare('name', $fileName);
244
    $criteria->compare('parent', $productId);
245
246
    $command = $this->commandBuilder->createFindCommand($this->tables['productImage'], $criteria);
247
248
    return $command->queryRow();
249
  }
250
251
  /**
252
   * @param $fileName
253
   *
254
   * @return bool
255
   */
256
  protected function checkExistFile($fileName)
257
  {
258
    return file_exists($this->outputPath.ImportHelper::wrapInSlashBegin($fileName));
259
  }
260
261
  protected function deleteOldImages($fileName)
262
  {
263
    foreach($this->previews as $preview => $sizes)
264
    {
265
      $filePath = $this->outputPath.ImportHelper::wrapInSlashBegin(($preview === 'origin' ? "" : $preview.'_').$fileName);
266
267
      if( !unlink($filePath) )
268
        throw new WarningException("Ошибка, не удальсь удалить файл ".$filePath);
269
    }
270
  }
271
272
  /**
273
   * @param $fileName
274
   *
275
   * @return string $newFileName
276
   */
277
  protected function createUniqueFileName($fileName)
278
  {
279
    return UploadHelper::prepareFileName($this->outputPath, $fileName);
280
  }
281
282
  protected function createImages($file, $newFileName)
283
  {
284
    foreach($this->previews as $preview => $sizes)
285
    {
286
      $newPath = $this->outputPath.ImportHelper::wrapInSlashBegin(($preview === 'origin' ? "" : $preview.'_').$newFileName);
287
288
      try
289
      {
290
        $thumb = $this->phpThumb->create($file);
291
        $thumb->resize($sizes[0], $sizes[1]);
292
        $thumb->save($newPath);
293
        chmod($newPath, 0775);
294
      }
295
      catch(Exception $e)
296
      {
297
        if( $this->phpThumbErrorExceptionToWarning )
298
          throw new WarningException($e->getMessage());
299
        else
300
          throw $e;
301
      }
302
    }
303
  }
304
305
  /**
306
   * @param $fileName
307
   * @param $productId
308
   * @param $type
309
   *
310
   * @return BProductImg
311
   * @throws CDbException
312
   * @throws WarningException
313
   * @internal param $filePath
314
   * @internal param string $file
315
   */
316
  protected function createImageRecord($fileName, $productId, $type)
317
  {
318
    $command = $this->commandBuilder->createInsertCommand($this->tables['productImage'], array(
319
      'parent' => $productId,
320
      'name' => $fileName,
321
      'type' => $type
322
    ));
323
324
    if( !$command->execute() )
325
    {
326
      throw new WarningException('Ошибака записи файла '.$fileName.' в БД product_id = '.$productId);
327
    }
328
  }
329
330
  protected function updateImageRecord($id, $fileName)
331
  {
332
    $criteria = new CDbCriteria();
333
    $criteria->compare('id', $id);
334
    $command = $this->commandBuilder->createUpdateCommand($this->tables['productImage'], array('name' => $fileName), $criteria);
335
336
    if( !$command->execute() )
337
    {
338
      throw new WarningException('Ошибака обновления записи в БД id = '.$id);
339
    }
340
  }
341
342
  protected function getProductIdByAttribute($attribute, $value)
343
  {
344
    if( is_null($this->productIdsCache) )
345
    {
346
      $this->productIdsCache = array();
347
348
      $criteria = new CDbCriteria();
349
      $criteria->select = array($attribute, 'id');
350
      $command = $this->commandBuilder->createFindCommand($this->tables['product'], $criteria);
351
352
      foreach($command->queryAll() as $data)
353
        $this->productIdsCache[$data['id']] = $data[$attribute];
354
    }
355
356
    return array_search($value, $this->productIdsCache);
357
  }
358
359
  protected function normalizeFileName($file)
360
  {
361
    $name = pathinfo($file, PATHINFO_FILENAME);
362
    $ext = pathinfo($file, PATHINFO_EXTENSION);
363
364
    return strtolower(Utils::translite($name)).'.'.$ext;
365
  }
366
}