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.
Passed
Push — filters-dev ( b8c330...c59b06 )
by Ivan
11:21
created

Yml   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 348
Duplicated Lines 2.3 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 51
lcom 1
cbo 9
dl 8
loc 348
rs 8.3206
c 3
b 1
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B init() 0 23 4
B generate() 0 40 5
B getOfferValue() 0 18 5
D prepareProperties() 0 32 9
C getOfferParams() 0 72 9
B generateSectionShop() 0 15 5
A generateSingleOffer() 0 12 2
C offerSimplified() 8 59 11

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 Yml 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 Yml, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace app\modules\shop\components\yml;
3
4
use app\models\ObjectStaticValues;
5
use app\models\Property;
6
use app\models\PropertyStaticValues;
7
use app\modules\shop\events\yml\YmlOffersEvent;
8
use app\modules\shop\helpers\CurrencyHelper;
9
use app\modules\shop\models\Category;
10
use app\modules\shop\models\Currency;
11
use app\modules\shop\models\Product;
12
use devgroup\TagDependencyHelper\ActiveRecordHelper;
13
use yii\base\Component;
14
use app\modules\shop\models\Yml as YmlModel;
15
use yii\caching\TagDependency;
16
use yii\db\Query;
17
use yii\helpers\Json;
18
use yii\helpers\Url;
19
use yii\web\View;
20
21
class Yml extends Component
22
{
23
    const PARAM_TYPE_FIELD = 'field';
24
    const PARAM_TYPE_RELATION = 'relation';
25
    const USE_GZIP = 1;
26
    const USE_OFFER_PARAM = 1;
27
    const USE_ADULT = 1;
28
    const USE_STORE = 1;
29
    const USE_PICKUP = 1;
30
    const USE_DELIVERY = 1;
31
32
    const EVENT_PROCESS_OFFER = 'ymlProcessOffer';
33
34
    /**
35
     * @property \app\modules\shop\models\Yml $model
36
     * @property string $viewFile
37
     * @property Currency $currency
38
     */
39
    protected $model = null;
40
    protected $viewFile = null;
41
    protected $currency = null;
42
43
    static public $_noImg = '';
44
    static public $ymlEavProperties = [];
45
    static public $ymlStaticProperties = [];
46
47
    /**
48
     * @inheritdoc
49
     */
50
    public function __construct(YmlModel $model, $config = [])
51
    {
52
        $this->model = $model;
53
        parent::__construct($config);
54
    }
55
56
    /**
57
     * @inheritdoc
58
     */
59
    public function init()
60
    {
61
        parent::init();
62
63
        if (false === $this->model instanceof YmlModel) {
64
            return false;
65
        }
66
67
        if (null === $this->viewFile) {
68
            $this->viewFile = \Yii::$app->getModule('shop')->getViewPath() . '/backend-yml/generate/yml.php';
69
        }
70
71
        /** @var YmlModel $config */
72
        $config = $this->model;
73
        \Yii::$app->urlManager->setHostInfo($config->shop_url);
74
        $this->currency = CurrencyHelper::findCurrencyByIso($config->currency_id);
75
76
        if (static::USE_OFFER_PARAM == $config->offer_param) {
77
            $this->prepareProperties();
78
        }
79
80
        static::$_noImg = \Yii::$app->getModule('image')->noImageSrc;
81
    }
82
83
    /**
84
     * @return bool
85
     */
86
    public function generate()
87
    {
88
        if (false === $this->model instanceof YmlModel) {
89
            return false;
90
        }
91
92
        /** @var \app\modules\shop\models\Yml $yml */
93
        $config = $this->model;
94
        /** @var View $view */
95
        $view = \Yii::$app->getView();
96
97
        $outputParams = [];
98
        $outputParams['shop'] = $this->generateSectionShop($config);
99
        $outputParams['offers'] = [];
100
101
        $eventOffer = new YmlOffersEvent();
102
        /** @var Product $model */
103
        foreach (Product::find()->where(['active' => 1])->batch() as $offers) {
104
            $eventOffer
105
                ->clearHandled()
106
                ->setOffers(array_reduce($offers, function ($r, $i) use ($config) {
107
                    if (null !== $o = $this->generateSingleOffer($config, $i)) {
108
                        $r[] = $o;
109
                    }
110
                    return $r;
111
                }, []));
112
            $this->trigger(static::EVENT_PROCESS_OFFER, $eventOffer);
113
114
            $outputParams['offers'] = array_merge($outputParams['offers'], array_column($eventOffer->getOffers(), 'result'));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 125 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
115
        }
116
117
        $output = $view->renderFile($this->viewFile, $outputParams);
118
119
        $fileName = \Yii::getAlias('@webroot') . '/' . $config->general_yml_filename;
120
        $result = static::USE_GZIP === $config->use_gzip
121
            ? file_put_contents($fileName . '.gz', gzencode($output), 5)
122
            : file_put_contents($fileName, $output);
123
124
        return false !== $result;
125
    }
126
127
    /**
128
     * @param YmlModel $config
129
     * @param string $name
130
     * @param Product $model
131
     * @param string $default
132
     * @return mixed
133
     */
134
    static public function getOfferValue(YmlModel $config, $name, Product $model, $default = '')
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
135
    {
136
        $param = $config->$name;
137
138
        if (static::PARAM_TYPE_FIELD === $param['type']) {
139
            $field = $param['key'];
140
            $result = $model->$field;
141
        } elseif (static::PARAM_TYPE_RELATION === $param['type']) {
142
            $rel = call_user_func([$model, $param['key']]);
143
            $attr = $param['value'];
144
            $rel = $rel->one();
145
            if (false === empty($rel)) {
146
                $result = $rel->$attr;
147
            }
148
        }
149
150
        return false === empty($result) ? $result : $default;
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
151
    }
152
153
    /**
154
     *
155
     */
156
    private function prepareProperties()
157
    {
158
        $props = Property::getDb()->cache(function ($db) {
0 ignored issues
show
Unused Code introduced by
The parameter $db is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
159
            return Property::find()
160
                ->select('id, name, property_handler_id, key, property_group_id, has_static_values, is_eav, handler_additional_params')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 135 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
161
                ->all();
162
        },
163
            86400,
164
            new TagDependency(['tags' => [
165
                ActiveRecordHelper::getCommonTag(Property::className()),
166
            ]]));
167
        foreach ($props as $one) {
168
            $additionalParams = Json::decode($one['handler_additional_params']);
169
            if (false === empty($additionalParams['use_in_file'])) {
170
                if (1 == $one['is_eav'] && false === isset(self::$ymlEavProperties[$one['id']])) {
171
                    self::$ymlEavProperties[$one['id']] = [
172
                        'name' => $one['name'],
173
                        'unit' => empty($additionalParams['unit']) ? '' : $additionalParams['unit'],
174
                        'key' => $one['key'],
175
                        'group_id' => $one['property_group_id'],
176
                        'handler_id' => $one['property_handler_id'],
177
                    ];
178
                } else if (1 == $one['has_static_values'] && false === isset(self::$ymlStaticProperties[$one['id']])) {
179
                    self::$ymlStaticProperties[$one['id']] = [
180
                        'name' => $one['name'],
181
                        'unit' => empty($additionalParams['unit']) ? '' : $additionalParams['unit'],
182
                    ];
183
                }
184
185
            }
186
        }
187
    }
188
189
    /**
190
     * @param Product $model
191
     * @return array
192
     */
193
    static public function getOfferParams(Product $model)
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
194
    {
195
        $params = [];
196
        $eav = \Yii::$app->getDb()->cache(function ($db) use ($model) {
0 ignored issues
show
Unused Code introduced by
The parameter $db is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
197
            return (new Query())
198
                ->from($model->object->eav_table_name)
199
                ->select(Property::tableName() . '.id, ' . $model->object->eav_table_name . '.value')
200
                ->innerJoin(
201
                    Property::tableName(),
202
                    Property::tableName() . '.property_group_id = ' . $model->object->eav_table_name . '.property_group_id'
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
203
                    . ' AND ' . Property::tableName() . '.key = ' . $model->object->eav_table_name . '.key'
204
                )
205
                ->where([
206
                    'object_model_id' => $model->id,
207
                    $model->object->eav_table_name . '.key' => array_column(static::$ymlEavProperties, 'key'),
208
                    $model->object->eav_table_name . '.property_group_id' => array_column(static::$ymlEavProperties, 'group_id'),
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 129 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
209
                    Property::tableName() . '.id' => array_keys(static::$ymlEavProperties),
210
                ])
211
                ->andWhere(['<>','value', ''])
212
                ->all();
213
        });
214
        foreach ($eav as $prop) {
215
            if (false === isset($prop['id'])) {
216
                continue ;
217
            }
218
219
            $val = htmlspecialchars($prop['value']);
220
            switch (static::$ymlEavProperties[$prop['id']]['handler_id']) {
221
                case 3 :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
222
                    $val = $val == 1 ? Yii::t('yii', 'Yes') : Yii::t('yii', 'No');
223
                    break;
224
            }
225
            $_key = static::$ymlEavProperties[$prop['id']]['name'];
226
            $params[htmlspecialchars(trim($_key))] = [
227
                'unit' => false === empty(static::$ymlEavProperties[$prop['id']]['unit'])
228
                    ? htmlspecialchars(trim(static::$ymlEavProperties[$prop['id']]['unit']))
229
                    : null,
230
                'value' => $val,
231
            ];
232
        }
233
234
        $psv = \Yii::$app->getDb()->cache(function ($db) use ($model) {
0 ignored issues
show
Unused Code introduced by
The parameter $db is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
235
            return  (new Query())
236
                ->from(PropertyStaticValues::tableName())
237
                ->innerJoin(
238
                    ObjectStaticValues::tableName(),
239
                    ObjectStaticValues::tableName() . '.property_static_value_id = ' . PropertyStaticValues::tableName() . '.id'
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 128 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
240
                )
241
                ->where([
242
                    'object_model_id' => $model->id,
243
                    'object_id' => $model->object->id,
244
                    'property_id' => array_keys(static::$ymlStaticProperties)
245
                ])
246
                ->andWhere(['<>','value', ''])
247
                ->all();
248
        });
249
        foreach ($psv as $prop) {
250
            if (false === isset($prop['property_id'])) {
251
                continue ;
252
            }
253
254
            $_key = static::$ymlStaticProperties[$prop['property_id']]['name'];
255
            $params[htmlspecialchars(trim($_key))] = [
256
                'unit' => false === empty(static::$ymlStaticProperties[$prop['property_id']]['unit'])
257
                    ? htmlspecialchars(trim(static::$ymlStaticProperties[$prop['property_id']]['unit']))
258
                    : null,
259
                'value' => htmlspecialchars(trim($prop['value'])),
260
            ];
261
        }
262
263
        return $params;
264
    }
265
266
    /**
267
     * @param YmlModel $config
268
     * @return array
269
     */
270
    private function generateSectionShop(YmlModel $config)
271
    {
272
        return [
273
            'name' => $config->shop_name,
274
            'company' => $config->shop_company,
275
            'url' => $config->shop_url,
276
            'currency' => $this->currency->iso_code,
277
            'categories' => Category::find()->where(['active' => 1])->asArray(),
278
            'store' => static::USE_STORE == $config->shop_store ? 'true' : 'false',
279
            'pickup' => static::USE_PICKUP == $config->shop_pickup ? 'true' : 'false',
280
            'delivery' => static::USE_DELIVERY == $config->shop_delivery ? 'true' : 'false',
281
            'local_delivery_cost' => $config->shop_local_delivery_cost,
282
            'adult' => static::USE_ADULT == $config->shop_adult ? 'true' : 'false',
283
        ];
284
    }
285
286
    /**
287
     * @param YmlModel $config
288
     * @param Product $model
289
     * @return null|array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use null|array<string,OfferTag>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
290
     */
291
    private function generateSingleOffer(YmlModel $config, Product $model)
292
    {
293
        $result = $this->offerSimplified($config, $model);
294
        if (null === $result) {
295
            return null;
296
        }
297
298
        return [
299
            'model' => $model,
300
            'result' => $result,
301
        ];
302
    }
303
304
    /**
305
     * @param YmlModel $config
306
     * @param Product $model
307
     * @return OfferTag|null
308
     */
309
    private function offerSimplified(YmlModel $config, Product $model)
310
    {
311
        $offer = new OfferTag('offer', [], ['id' => $model->id, 'available' => 'true',]);
0 ignored issues
show
Documentation introduced by
array() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
312
313
        $price = static::getOfferValue($config, 'offer_price', $model, 0);
314
        $price = CurrencyHelper::convertCurrencies($price, $model->currency, $this->currency);
315
        if ($price <= 0 || $price >= 1000000000) {
316
            return null;
317
        }
318
319
        $values = [];
320
321
        $name = static::getOfferValue($config, 'offer_name', $model, null);
322
        if (true === empty($name)) {
323
            return null;
324
        }
325 View Code Duplication
        if (mb_strlen($name) > 120) {
326
            $name = mb_substr($name, 0, 120);
327
            $name = mb_substr($name, 0, mb_strrpos($name, ' '));
328
        }
329
        $values[] = new OfferTag('name', htmlspecialchars(trim(strip_tags($name))));
330
        $values[] = new OfferTag('price', $price);
331
        $values[] = new OfferTag('currencyId', $this->currency->iso_code);
332
333
        /** @var Category $category */
334
        if (null === $category = $model->category) {
335
            return null;
336
        }
337
        $values[] = new OfferTag('categoryId', $category->id);
338
        $values[] = new OfferTag('url', Url::toRoute([
339
            '@product',
340
            'model' => $model,
341
            'category_group_id' => $category->category_group_id,
342
        ], true));
343
344
        $picture = static::getOfferValue($config, 'offer_picture', $model, static::$_noImg);
345
        if (static::$_noImg !== $picture) {
346
            $picture = htmlspecialchars(trim($picture, '/'));
347
            $picture = implode('/', array_map('rawurlencode', explode('/', $picture)));
348
            $values[] = new OfferTag('picture', trim($config->shop_url, '/') . '/' . $picture);
349
        }
350
351
        $description = static::getOfferValue($config, 'offer_description', $model, null);
352
        if (false === empty($description)) {
353 View Code Duplication
            if (mb_strlen($description) > 175) {
354
                $description = mb_substr($description, 0, 175);
355
                $description = mb_substr($description, 0, mb_strrpos($description, ' '));
356
            }
357
            $values[] = new OfferTag('description', htmlspecialchars(trim(strip_tags($description))));
358
        }
359
360
        if (static::USE_OFFER_PARAM == $config->offer_param) {
361
            foreach (static::getOfferParams($model) as $k => $v) {
362
                $values[] = new OfferTag('param', $v['value'], ['name' => $k, 'unit' => $v['unit']]);
363
            }
364
        }
365
366
        return $offer->setValue($values);
367
    }
368
}
369