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 ( 61aacd...05f06e )
by Alexey
18:53
created

RetailCrm   B

Complexity

Total Complexity 53

Size/Duplication

Total Lines 327
Duplicated Lines 18.35 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 53
c 1
b 0
f 0
lcom 1
cbo 7
dl 60
loc 327
rs 7.4757

14 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 16 1
B createCallback() 23 23 4
B createOrder() 24 24 4
A registerEventEndExportIcml() 0 13 2
A onEndExport() 0 10 1
A onError() 0 9 3
A exceptionShutdown() 0 15 4
A increaseExportProductCounter() 0 4 1
A getLogger() 0 4 1
A createDebugReport() 0 11 2
C setCustomerData() 13 59 15
B configure() 0 25 5
C sendOrder() 0 35 8
A flushContent() 0 8 2

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 RetailCrm 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 RetailCrm, 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 29 and the first side effect is on line 17.

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
 * Пример подключения в frontend.php:
9
 * 'components' => array(
10
 *   ...
11
 *   'retailCrm' => array(
12
 *     'class' => 'ext.retailcrm.RetailCrm',
13
 *   ),
14
 *   ...
15
 * )
16
 */
17
Yii::import('frontend.components.cli.*');
18
Yii::import('ext.retailcrm.components.RetailCrmDataManager');
19
20
Yii::import('ext.retailcrm.components.lib.ApiClient', true);
21
Yii::import('ext.retailcrm.components.lib.Http.Client', true);
22
Yii::import('ext.retailcrm.components.lib.Exception.CurlException', true);
23
Yii::import('ext.retailcrm.components.lib.Exception.InvalidJsonException', true);
24
Yii::import('ext.retailcrm.components.lib.Response.ApiResponse', true);
25
26
/**
27
 * Class RetailCrm
28
 */
29
class RetailCrm extends CApplicationComponent
30
{
31
  public $debug = false;
32
33
  /**
34
   * @var string $url
35
   */
36
  protected $url;
37
38
  /**
39
   * @var string $apiKey
40
   */
41
  protected $apiKey;
42
43
  /**
44
   * @var boolean $enabled
45
   */
46
  protected $enabled;
47
48
  /**
49
   * @var boolean $log
50
   */
51
  protected $log;
52
53
  /**
54
   * @var ConsoleFileLogger $logger
55
   */
56
  public $logger;
57
58
  /**
59
   * @var RetailCrmDataManager $retailCrmDataManager
60
   */
61
  protected $retailCrmDataManager; 
62
63
  protected $exportProductCounter;
64
65
  protected $beginExportTime;
66
67
  /**
68
   * @var RetailCrm\ApiClient $apiClient
69
   */
70
  private $apiClient;
71
72
  public function init()
73
  {
74
    parent::init();
75
76
    $this->retailCrmDataManager = new RetailCrmDataManager();
77
78
    $this->configure();
79
80
    $this->logger = new ConsoleFileLogger('retail_crm.log');
81
    $this->logger->showLog = false;
82
83
    $this->apiClient = new RetailCrm\ApiClient(
84
      $this->url,
85
      $this->apiKey
86
    );
87
  }
88
89 View Code Duplication
  public function createCallback(Callback $model)
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...
90
  {
91
    if( !$this->enabled )
92
      return;
93
94
    $this->flushContent();
95
96
    try
97
    {
98
      $data = $this->retailCrmDataManager->getCallbackData($model);
99
    }
100
    catch(CException $e)
0 ignored issues
show
Bug introduced by
The class CException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
101
    {
102
      $this->logger->error('Ошибка в формировании данных для RetailCrm. '.$e->getMessage());
103
      return;
104
    }
105
    $this->setCustomerData($data);
106
107
    if( $retailCrmOrderId = $this->sendOrder($data, $model) )
0 ignored issues
show
Unused Code introduced by
$retailCrmOrderId is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
108
    {
109
      $this->retailCrmDataManager->setRetailCrmUrl($model, $data['number'], $this->url);
110
    }
111
  }
112
113 View Code Duplication
  public function createOrder(Order $model)
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...
114
  {
115
    if( !$this->enabled )
116
      return;
117
118
    $this->flushContent();
119
120
    try
121
    {
122
      $data = $this->retailCrmDataManager->getOrderData($model);
123
    }
124
    catch(CException $e)
0 ignored issues
show
Bug introduced by
The class CException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
125
    {
126
      $this->logger->error('Ошибка в формировании данных для RetailCrm. '.$e->getMessage());
127
      return;
128
    }
129
130
    $this->setCustomerData($data);
131
    if( $retailCrmOrderId = $this->sendOrder($data, $model) )
132
    {
133
      $this->retailCrmDataManager->updateOrderStatus($model);
134
      $this->retailCrmDataManager->setRetailCrmUrl($model, $retailCrmOrderId, $this->url);
135
    }
136
  }
137
138
  public function registerEventEndExportIcml()
139
  {
140
    if( Yii::app()->request->getParam('force') != 'force' )
141
      return;
142
143
    $this->beginExportTime = microtime(true);
144
    $this->logger->log('Начало экспорта icml');
145
    $this->logger->flush();
146
    Yii::app()->attachEventHandler('onEndRequest', array($this, 'onEndExport'));
147
    Yii::app()->attachEventHandler('onException', array($this, 'onError'));
148
    Yii::app()->attachEventHandler('onError', array($this, 'onError'));
149
    register_shutdown_function(array($this, 'exceptionShutdown'));
150
  }
151
152
  public function onEndExport($event)
153
  {
154
    $time = microtime(true) - $this->beginExportTime;
155
    $logMessage = 'Экспорт icml завершен за '.sprintf("%.1f", $time).' с.'.PHP_EOL;
156
    $logMessage .= 'Обработано '.$this->exportProductCounter.' продуктов.'.PHP_EOL;
157
    $logMessage .= 'Максимальное использовано памяти '.Yii::app()->format->formatSize(memory_get_peak_usage());
158
159
    $this->logger->log($logMessage);
160
    $this->logger->flush();
161
  }
162
163
  public function onError($event)
164
  {
165
    if( isset($event->exception) )
166
      $this->logger->error($event->exception->getMessage());
167
    else if( isset($event->message) )
168
      $this->logger->error($event->message);
169
    else
170
      $this->logger->error("Не известная ошибка");
171
  }
172
173
  public function exceptionShutdown()
174
  {
175
    $error = error_get_last();
176
177
    if (is_array($error) != FALSE)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
178
    {
179
      if (isset($error['type']) != FALSE)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
180
      {
181
        if ($error['type'] == 1)
182
        {
183
          $this->logger->error("Фатальная ошибка: ".$error['message']);
184
        }
185
      }
186
    }
187
  }
188
189
  public function increaseExportProductCounter()
190
  {
191
    $this->exportProductCounter++;
192
  }
193
194
  /**
195
   * @return ConsoleFileLogger
196
   */
197
  public function getLogger()
198
  {
199
    return $this->logger;
200
  }
201
202
  public function createDebugReport($attributes, $offerCounter)
203
  {
204
    if( $this->debug )
205
    {
206
      $log = 'Product id = '.$attributes['id'].' process'.PHP_EOL;
207
      $log .= 'Processed items '.$offerCounter.' Date '.date('d.m.Y H:i:s').PHP_EOL;
208
      $log .= 'Usage memory is '.round(memory_get_usage() / 1024).' KBt'.PHP_EOL;
209
      $log .= 'Peak usage memory is '.round(memory_get_peak_usage() / 1024).' KBt'.PHP_EOL;
210
      file_put_contents(Yii::getPathOfAlias('frontend.runtime').'/retail_crm_debug.log', array($log));
211
    }
212
  }
213
214
  /**
215
   * @param array $data
216
   *
217
   * @return null
218
   */
219
  private function setCustomerData(&$data = array())
220
  {
221
    $filter = array();
222
223
    if( !empty($data['phone']) )
224
      $filter['name'] = $data['phone'];
225
    else if( !empty($data['email']) )
226
      $filter['email'] = $data['email'];
227 View Code Duplication
    else
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...
228
    {
229
      $nameArray = array();
230
      if( !empty($data['lastName']) )
231
        $nameArray[] = $data['lastName'];
232
      if( !empty($data['firstName']) )
233
        $nameArray[] = $data['firstName'];
234
      if( !empty($data['patronymic']) )
235
        $nameArray[] = $data['patronymic'];
236
237
      if( !empty($nameArray) )
238
        $filter['name'] = implode(' ', $nameArray);
239
    }
240
241
    if( empty($filter) )
242
      return;
243
244
    try
245
    {
246
      $response = $this->apiClient->customersList($filter);
247
    }
248
    catch(\RetailCrm\Exception\CurlException $e)
249
    {
250
      if( $this->log )
251
        $this->logger->error("Сетевые проблемы. Ошибка подключения к retailCRM: ".$e->getMessage());
252
253
      return;
254
    }
255
256
    if( $response->isSuccessful() )
257
    {
258
      if( !($user = Arr::reset($response->customers)) )
0 ignored issues
show
Documentation introduced by
The property customers does not exist on object<RetailCrm\Response\ApiResponse>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
259
        return;
260
261
      if( isset($user['customerId']) )
262
      {
263
        $data['customerId'] = $user['customerId'];
264
      }
265
    }
266
    else
267
    {
268
      if( $this->log )
269
      {
270
        $errorMessage = "Ошибка при запросе пользователей: [Статус HTTP-ответа ".$response->getStatusCode()."] ".$response->getErrorMsg().'.';
0 ignored issues
show
Documentation Bug introduced by
The method getErrorMsg does not exist on object<RetailCrm\Response\ApiResponse>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
271
        if( $response->offsetExists('errors') )
272
          $errorMessage .= ' '.$response->offsetGet('errors');
273
274
        $this->logger->error($errorMessage);
275
      }
276
    }
277
  }
278
279
  private function configure()
280
  {
281
    $configPath = Yii::getPathOfAlias('frontend.config.retail_crm').'.php';
282
    if( file_exists($configPath) )
283
    {
284
      $config = require($configPath);
285
      $this->url = $config['url'];
286
      $this->apiKey = $config['apiKey'];
287
288
      if( isset($config['idPrefix']) )
289
        $this->retailCrmDataManager->idPrefix = $config['idPrefix'];
290
291
      if( isset($config['log']) )
292
        $this->log = $config['log'];
293
294
      if( isset($config['debug']) )
295
        $this->debug = $config['debug'];
296
297
      $this->enabled = $config['enabled'];
298
    }
299
    else
300
    {
301
      throw new CHttpException('500', 'Не найден кофигурационный файл retail_crm.php в папке config');
302
    }
303
  }
304
305
  /**
306
   * @param array $data
307
   * @param CActiveRecord $model
308
   *
309
   * @return null|string $retailCrmId
310
   */
311
  private function sendOrder(array $data, CActiveRecord $model)
312
  {
313
    try
314
    {
315
      $response = $this->apiClient->ordersCreate($data);
316
    }
317
    catch(\RetailCrm\Exception\CurlException $e)
318
    {
319
      if( $this->log )
320
        $this->logger->error("Сетевые проблемы. Ошибка подключения к retailCRM: ".$e->getMessage());
321
322
      return null;
323
    }
324
325
    if( $response->isSuccessful() && 201 === $response->getStatusCode() )
326
    {
327
      if( $this->log )
328
        $this->logger->log('Заказ ('.get_class($model).') успешно создан id = '.$model->id.' retail_crm_id = '.$response->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RetailCrm\Response\ApiResponse>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
329
330
      return $response->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<RetailCrm\Response\ApiResponse>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
331
    }
332
    else
333
    {
334
      if( $this->log )
335
      {
336
        $errorMessage = "Ошибка создания заказа(".get_class($model).") id = {$model->id}: [Статус HTTP-ответа ".$response->getStatusCode()."] ".$response->getErrorMsg().'.';
0 ignored issues
show
Documentation Bug introduced by
The method getErrorMsg does not exist on object<RetailCrm\Response\ApiResponse>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
337
        if( $response->offsetExists('errors') )
338
          $errorMessage .= ' '.print_r($response->offsetGet('errors'), true);
339
340
        $this->logger->error($errorMessage);
341
      }
342
    }
343
344
    return null;
345
  }
346
347
  private function flushContent()
348
  {
349
    if( function_exists('fastcgi_finish_request') )
350
    {
351
      session_write_close();
352
      fastcgi_finish_request();
353
    }
354
  }
355
}