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.
Test Setup Failed
Push — filters ( 233a3a...88ffb1 )
by Alexander
29:32
created

ContentBlockHelper   D

Complexity

Total Complexity 71

Size/Duplication

Total Lines 524
Duplicated Lines 12.02 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 9
Bugs 3 Features 0
Metric Value
wmc 71
lcom 1
cbo 14
dl 63
loc 524
rs 4.026
c 9
b 3
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A compileContentString() 0 7 1
C processData() 18 69 15
A replaceForms() 0 21 4
C renderUrl() 21 33 8
B sanitizeChunk() 6 24 6
B compileChunk() 0 28 5
A applyFormatter() 0 13 4
A fetchChunkByKey() 0 18 2
A preloadChunks() 0 18 2
A getChunk() 0 19 4
F renderProducts() 18 153 20

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

1
<?php
2
namespace app\modules\core\helpers;
3
4
use app;
5
use app\models\Property;
6
use app\models\PropertyStaticValues;
7
use app\modules\core\models\ContentBlock;
8
use app\modules\page\models\Page;
9
use app\modules\shop\models\Category;
10
use app\modules\shop\models\Product;
11
use devgroup\TagDependencyHelper\ActiveRecordHelper;
12
use yii\caching\TagDependency;
13
use yii\helpers\ArrayHelper;
14
use yii\helpers\Url;
15
use yii;
16
17
/**
18
 * Class ContentBlockHelper
19
 * Main public static method compileContentString() uses submethods to extract chunk calls from model content field,
20
 * fetch chunks from data base table, then compile it and replace chunk calls with compiled chunks data
21
 * Example chunk call in model content field should be like: [[$chunk param='value'|'default value' param2=42]].
22
 * Chunk declaration should be like : <p>String: [[+param]]</p> <p>Float: [[+param2:format, param1, param2]]</p>
23
 * All supported formats you can find at Yii::$app->formatter
24
 *
25
 * @package app\modules\core\helpers
26
 */
27
class ContentBlockHelper
28
{
29
    private static $chunksByKey = [];
30
31
    /**
32
     * Compiles content string by injecting chunks into content
33
     * Preloads chunks which have preload = 1
34
     *
35
     * @param  string $content Original content with chunk calls
36
     * @param  string $content_key Key for caching compiled content version
37
     * @param  yii\caching\Dependency $dependency Cache dependency
38
     * @return string Compiled content with injected chunks
39
     */
40
    public static function compileContentString($content, $content_key, $dependency)
41
    {
42
        self::preloadChunks();
43
        $output = self::processData($content, $content_key, $dependency);
44
        $output = self::processData($output, $content_key, $dependency, '', false);
45
        return $output;
46
    }
47
48
    /**
49
     * Finding chunk calls with regexp
50
     * Iterate matches
51
     * While iterating:
52
     * Extracts single chunk data with sanitizeChunk() method
53
     * Fetches chunk by key using fetchChunkByKey(), who returns chunk value by key from static array if exists, otherwise from db
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 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...
54
     * Compiles single chunk using compileChunk() method
55
     * Replaces single chunk call with compiled chunk data in the model content
56
     *
57
     * @param  string $content_key Key for caching compiled content version
58
     * @param  yii\caching\Dependency $dependency Cache dependency
59
     * @param  bool $preprocess flag to separate rendering non cacheable chunks such as Form
60
     * @param  string $content
61
     * @param  string $chunk_key ContentBlock key string to prevent endless recursion
62
     * @return string
63
     */
64
    private static function processData($content, $content_key, $dependency, $chunk_key = '', $preprocess = true)
65
    {
66
        $matches = [];
67
        $replacement = '';
68
        preg_match_all('%\[\[([^\]\[]+)\]\]%ui', $content, $matches);
69
        if (!empty($matches[0])) {
70
            foreach ($matches[0] as $k => $rawChunk) {
71
                $chunkData = self::sanitizeChunk($rawChunk);
72
                if ($chunkData['key'] == $chunk_key) {
73
                    $content = str_replace($matches[0][$k], '', $content);
74
                    continue;
75
                }
76
                $cacheKey = $content_key . $chunkData['key'] . serialize($chunkData);
77
                switch ($chunkData['token']) {
78
                    case '$':
79
                        if ($preprocess === false) break;
80
                        $chunk = self::fetchChunkByKey($chunkData['key']);
81
                        $replacement = Yii::$app->cache->get($cacheKey);
82
                        if ($replacement === false) {
83
                            $replacement = static::compileChunk($chunk, $chunkData, $chunkData['key'], $content_key, $dependency);
0 ignored issues
show
Bug introduced by
Since compileChunk() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of compileChunk() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 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...
84
                            Yii::$app->cache->set(
85
                                $cacheKey,
86
                                $replacement,
87
                                84600,
88
                                $dependency
89
                            );
90
                        }
91
                        break;
92
                    case '%':
93
                        if ($preprocess === true) {
94
                            $replacement = $rawChunk;
95
                            break;
96
                        }
97
                        $replacement = static::replaceForms($chunkData);
0 ignored issues
show
Bug introduced by
Since replaceForms() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of replaceForms() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
98
                        break;
99
                    case '~' :
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...
100
                        if ($preprocess === false) break;
101
                        $replacement = Yii::$app->cache->get($cacheKey);
102 View Code Duplication
                        if ($replacement === false) {
1 ignored issue
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...
103
                            $replacement = static::renderUrl($chunkData, $dependency);
0 ignored issues
show
Bug introduced by
Since renderUrl() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of renderUrl() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
Compatibility introduced by
$dependency of type object<yii\caching\Dependency> is not a sub-type of object<yii\caching\TagDependency>. It seems like you assume a child class of the class yii\caching\Dependency to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
104
                            Yii::$app->cache->set(
105
                                $cacheKey,
106
                                $replacement,
107
                                84600,
108
                                $dependency
109
                            );
110
                        }
111
                        break;
112
                    case '*':
113
                        if ($preprocess === false) {
114
                            break;
115
                        }
116
                        $replacement = Yii::$app->cache->get($cacheKey);
117 View Code Duplication
                        if ($replacement === false) {
1 ignored issue
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...
118
                            $replacement = static::renderProducts($chunkData, $dependency);
0 ignored issues
show
Bug introduced by
Since renderProducts() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of renderProducts() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
Compatibility introduced by
$dependency of type object<yii\caching\Dependency> is not a sub-type of object<yii\caching\TagDependency>. It seems like you assume a child class of the class yii\caching\Dependency to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
119
                            Yii::$app->cache->set(
120
                                $cacheKey,
121
                                $replacement,
122
                                84600,
123
                                $dependency
124
                            );
125
                        }
126
                        break;
127
                }
128
                $content = str_replace($matches[0][$k], $replacement, $content);
129
            }
130
        }
131
        return $content;
132
    }
133
134
    /**
135
     * @param array $chunkData
136
     * @return mixed
137
     */
138
    private static function replaceForms($chunkData)
139
    {
140
        $regexp = '/^(?P<formId>\d+)(#(?P<id>[\w\d\-_]+))?(;(?P<isModal>isModal))?$/Usi';
141
        return preg_replace_callback(
142
            $regexp,
143
            function ($matches) {
144
                if (isset($matches['formId'])) {
145
                    $params = ['formId' => intval($matches['formId'])];
146
                    if (isset($matches['id'])) {
147
                        $params['id'] = $matches['id'];
148
                    }
149
                    if (isset($matches['isModal'])) {
150
                        $params['isModal'] = true;
151
                    }
152
                    return app\widgets\form\Form::widget($params);
153
                }
154
                return '';
155
            },
156
            $chunkData['key']
157
        );
158
    }
159
160
    /**
161
     * renders url according to given data
162
     * @param $chunkData
163
     * @param TagDependency $dependency
164
     * @return string
165
     */
166
    private static function renderUrl($chunkData, &$dependency)
167
    {
168
        $expression = '%(?P<objectName>[^#]+?)#(?P<objectId>[\d]+?)$%';
169
        $output = '';
170
        preg_match($expression, $chunkData['key'], $m);
171
        if (true === isset($m['objectName'], $m['objectId'])) {
172
            $id = (int)$m['objectId'];
173
            switch (strtolower($m['objectName'])) {
174 View Code Duplication
                case "page" :
1 ignored issue
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...
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...
175
                    if (null !== $model = Page::findById($id)) {
176
                        $dependency->tags [] = ActiveRecordHelper::getCommonTag(Page::className());
177
                        $dependency->tags [] = $model->objectTag();
178
                        $output = Url::to(['@article', 'id' => $id]);
179
                    }
180
                    break;
181 View Code Duplication
                case "category" :
1 ignored issue
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...
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...
182
                    if (null !== $model = Category::findById($id)) {
183
                        $dependency->tags [] = ActiveRecordHelper::getCommonTag(Category::className());
184
                        $dependency->tags [] = $model->objectTag();
0 ignored issues
show
Documentation Bug introduced by
The method objectTag does not exist on object<app\modules\shop\models\Category>? 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...
185
                        $output = Url::to(['@category', 'last_category_id' => $id]);
186
                    }
187
                    break;
188 View Code Duplication
                case "product" :
1 ignored issue
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...
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...
189
                    if (null !== $model = app\modules\shop\models\Product::findById($id)) {
190
                        $dependency->tags [] = ActiveRecordHelper::getCommonTag(Product::className());
191
                        $dependency->tags [] = $model->objectTag();
0 ignored issues
show
Documentation Bug introduced by
The method objectTag does not exist on object<app\modules\shop\models\Product>? 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...
192
                        $output = Url::to(['@product', 'model' => $model]);
193
                    }
194
                    break;
195
            }
196
        }
197
        return $output;
198
    }
199
200
    /**
201
     * Extracts chunk data from chunk call
202
     * uses regexp to extract param data from placeholder
203
     * [[$chunk <paramName>='<escapedValue>'|'<escapedDefault>' <paramName>=<unescapedValue>|<unescapedDefault>]]
204
     * iterate matches.
205
     * While iterating converts escapedValue and escapedDefault into string, unescapedValue and unescapedDefault - into float
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...
206
     * Returns chunk data array like:
207
     *  [
208
     *      'key' => 'chunkKey',
209
     *      'firstParam'=> 'string value',
210
     *      'firstParam-default'=> 'default string value',
211
     *      'secondParam'=> float value,
212
     *      'secondParam-default'=> default float value,
213
     *  ]
214
     *
215
     * @param string $rawChunk
216
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<*,string|double>.

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...
217
     */
218
    private static function sanitizeChunk($rawChunk)
219
    {
220
        $chunk = [];
221
        preg_match('%(?P<chunkToken>[^\w\[]?)([^\s\]\[]+)[\s\]]%', $rawChunk, $keyMatches);
222
        $chunk['token'] = $keyMatches['chunkToken'];
223
        $chunk['key'] = $keyMatches[2];
224
        $expression = "#\s*(?P<paramName>[\\w\\d]*)=(('(?P<escapedValue>.*[^\\\\])')|(?P<unescapedValue>.*))(\\|(('(?P<escapedDefault>.*[^\\\\])')|(?P<unescapedDefault>.*)))?[\\]\\s]#uUi";
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 188 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...
225
        preg_match_all($expression, $rawChunk, $matches);
226
        foreach ($matches['paramName'] as $key => $paramName) {
227
            if (!empty($matches['escapedValue'][$key])) {
228
                $chunk[$paramName] = strval($matches['escapedValue'][$key]);
229
            }
230
            if (!empty($matches['unescapedValue'][$key])) {
231
                $chunk[$paramName] = floatval($matches['unescapedValue'][$key]);
232
            }
233 View Code Duplication
            if (!empty($matches['escapedDefault'][$key])) {
234
                $chunk[$paramName . '-default'] = strval($matches['escapedDefault'][$key]);
235
            }
236 View Code Duplication
            if (!empty($matches['unescapedDefault'][$key])) {
237
                $chunk[$paramName . '-default'] = floatval($matches['unescapedDefault'][$key]);
238
            }
239
        }
240
        return $chunk;
241
    }
242
243
    /**
244
     * Compiles single chunk
245
     * uses regexp to find placeholders and extract it's data from chunk value field
246
     * [[<token><paramName>:<format><params>]]
247
     * token switch is for future functionality increase
248
     * now method only recognizes + token and replaces following param with according $arguments array data
249
     * applies formatter according previously defined param values type if needed
250
     * if param name from placeholder was not found in arguments array, placeholder in the compiled chunk will be replaced with empty string
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 140 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...
251
     * returns compiled chunk
252
     *
253
     * @param  string $content_key Key for caching compiled content version
254
     * @param  yii\caching\Dependency $dependency Cache dependency
255
     * @param  string $chunk ContentBlock instance
256
     * @param  array $arguments Arguments for this chunk from original content
257
     * @param  string $key ContentBlock key string to prevent endless recursion
258
     * @return string Result string ready for replacing
259
     */
260
    private static function compileChunk($chunk, $arguments, $key, $content_key, $dependency)
261
    {
262
        $matches = [];
263
        preg_match_all('%\[\[(?P<token>[\+\*])(?P<paramName>[^\s\:\]]+)\:?(?P<format>[^\,\]]+)?\,?(?P<params>[^\]]+)?\]\]%ui', $chunk, $matches);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 145 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...
264
        foreach ($matches[0] as $k => $rawParam) {
265
            $token = $matches['token'][$k];
266
            $paramName = trim($matches['paramName'][$k]);
267
            $format = trim($matches['format'][$k]);
268
            $params = preg_replace('%[\s]%', '', $matches['params'][$k]);
269
            $params = explode(',', $params);
270
            switch ($token) {
271
                case '+':
272
                    if (array_key_exists($paramName, $arguments)) {
273
                        $replacement = static::applyFormatter($arguments[$paramName], $format, $params);
0 ignored issues
show
Bug introduced by
Since applyFormatter() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of applyFormatter() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
274
                        $chunk = str_replace($matches[0][$k], $replacement, $chunk);
275
                    } else if (array_key_exists($paramName . '-default', $arguments)) {
276
                        $replacement = static::applyFormatter($arguments[$paramName . '-default'], $format, $params);
0 ignored issues
show
Bug introduced by
Since applyFormatter() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of applyFormatter() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
277
                        $chunk = str_replace($matches[0][$k], $replacement, $chunk);
278
                    } else {
279
                        $chunk = str_replace($matches[0][$k], '', $chunk);
280
                    }
281
                    break;
282
                default:
283
                    $chunk = str_replace($matches[0][$k], '', $chunk);
284
            }
285
        }
286
        return self::processData($chunk, $content_key, $dependency, $key);
287
    }
288
289
    /**
290
     * Find formatter declarations in chunk placeholders. if find trying to apply
291
     * yii\i18n\Formatter formats see yii\i18n\Formatter for details
292
     *
293
     * @param string $value single placeholder declaration from chunk
294
     * @param string $format
295
     * @param array $params
296
     * @return string|array
297
     */
298
    private static function applyFormatter($value, $format, $params)
299
    {
300
        if (false === method_exists(Yii::$app->formatter, $format) || empty($format)) {
301
            return $value;
302
        }
303
        array_unshift($params, $value);
304
        try {
305
            $formattedValue = call_user_func_array([Yii::$app->formatter, $format], $params);
306
        } catch (\Exception $e) {
307
            $formattedValue = $value;
308
        }
309
        return $formattedValue;
310
    }
311
312
    /**
313
     * Fetches single chunk by key from static var
314
     * if is no there - get it from db and push to static array
315
     *
316
     * @param $key string Chunk key field
317
     * @return string Chunk value field
318
     */
319
    private static function fetchChunkByKey($key)
320
    {
321
        if (!array_key_exists($key, static::$chunksByKey)) {
322
            $dependency = new TagDependency([
323
                'tags' => [
324
                    ActiveRecordHelper::getCommonTag(ContentBlock::className()),
325
                ]
326
            ]);
327
            static::$chunksByKey[$key] = ContentBlock::getDb()->cache(function ($db) use ($key) {
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...
328
                $chunk = ContentBlock::find()
329
                    ->where(['key' => $key])
330
                    ->asArray()
331
                    ->one();
332
                return static::$chunksByKey[$key] = $chunk['value'];
333
            }, 86400, $dependency);
334
        }
335
        return static::$chunksByKey[$key];
336
    }
337
338
    /**
339
     * preloads chunks with option preload  = 1
340
     * and push it to static array
341
     *
342
     * @return array|void
343
     */
344
    private static function preloadChunks()
345
    {
346
        if (is_null(static::$chunksByKey)) {
347
            $dependency = new TagDependency([
348
                'tags' => [
349
                    ActiveRecordHelper::getCommonTag(ContentBlock::className()),
350
                ]
351
            ]);
352
            static::$chunksByKey = ContentBlock::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...
Documentation Bug introduced by
It seems like \app\modules\core\models... }, 86400, $dependency) of type * is incompatible with the declared type array of property $chunksByKey.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
353
                $chunks = ContentBlock::find()
354
                    ->where(['preload' => 1])
355
                    ->asArray()
356
                    ->all();
357
                return ArrayHelper::map($chunks, 'key', 'value');
358
            }, 86400, $dependency);
359
        }
360
        return static::$chunksByKey;
361
    }
362
363
    /**
364
     * renders chunks in template files
365
     * @param string $key ContentBlock key
366
     * @param array $params . Array of params to be replaced while render
367
     * @param yii\base\Model $model . Caller model instance to use in caching
0 ignored issues
show
Documentation introduced by
Should the type for parameter $model not be null|yii\base\Model?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
368
     * @return mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string.

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...
369
     */
370
    public static function getChunk($key, $params = [], yii\base\Model $model = null)
371
    {
372
        if (null === $rawChunk = self::fetchChunkByKey($key)) {
373
            return '';
374
        }
375
        $tags = [
376
            ActiveRecordHelper::getCommonTag(app\modules\core\models\ContentBlock::className()),
377
        ];
378
        $content_key = 'templateChunkRender' . $key;
379
        if (null !== $model) {
380
            $content_key .= $model->id;
381
            $tags[] = ActiveRecordHelper::getObjectTag(get_class($model), $model->id);
382
        }
383
        $dependency = new TagDependency(['tags' => $tags]);
384
        if (false === empty($params)) {
385
            $rawChunk = self::compileChunk($rawChunk, $params, $key, $content_key, $dependency);
386
        }
387
        return self::compileContentString($rawChunk, $content_key, $dependency);
388
    }
389
390
    /**
391
     * renders product item and list.
392
     * possible can render all objects, but need for few logic change
393
     * @param array $chunkData params for select and render
394
     * @param TagDependency $dependency
395
     * @return mixed
396
     */
397
    private static function renderProducts($chunkData, &$dependency)
398
    {
399
        $params = [
400
            'itemView' => Yii::$app->getModule('shop')->itemView,
401
            'type' => 'show',
402
            'object' => 'product',
403
            'where' => [],
404
            'limit' => 0,
405
            'listView' => Yii::$app->getModule('shop')->listView,
406
        ];
407
        switch ($chunkData['key']) {
408
            case 'product':
409
                if (ArrayHelper::keyExists('sku', $chunkData)) {
410
                    $params['where'] = ['sku' => $chunkData['sku']];
411
                }
412
                break;
413
            case 'productList':
414
                $params['type'] = 'list';
415
                break;
416
            default:
417
                $expression = '%(?P<objectName>[^#]+?)#(?P<objectId>[\d]+?)$%';
418
                if (preg_match($expression, $chunkData['key'], $matches)) {
419
                    $params['where']['id'] = $matches['objectId'];
420
                }
421
                break;
422
        }
423
        switch ($params['object']) {
424
            case 'product':
425
                $dependency->tags[] = ActiveRecordHelper::getCommonTag(Product::className());
426
                $query = Product::find();
427
                if (!empty($chunkData['categoryId'])) {
428
                    $query->leftJoin(
429
                        '{{%product_category}}',
430
                        Product::tableName() . '.id = {{%product_category}}.object_model_id'
431
                    )->andWhere(['{{%product_category}}.category_id' => $chunkData['categoryId']]);
432
                    $dependency->tags[] = ActiveRecordHelper::getCommonTag(Category::className());
433
                    $dependency->tags[] = ActiveRecordHelper::getObjectTag(
434
                        Category::className(),
435
                        $chunkData['categoryId']
436
                    );
437
                }
438
                if (!empty($chunkData['property'])) {
439
                    $expression = '%(?P<propertyKey>[^:]+?):(?P<propertyValue>.+?)$%';
440
                    if (preg_match($expression, $chunkData['property'], $matches)) {
441
                        $property = Property::findOne(['key' => $matches['propertyKey']]);
442
                        if (!is_null($property)) {
443
                            /** @var Property $property */
444
                            $dependency->tags[] = ActiveRecordHelper::getCommonTag(Property::className());
445
                            $dependency->tags[] = $property->objectTag();
0 ignored issues
show
Documentation Bug introduced by
The method objectTag does not exist on object<app\models\Property>? 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...
446
                            if ($property->is_eav == 1) {
447
                                $query->leftJoin(
448
                                    '{{%product_eav}}',
449
                                    Product::tableName() . '.id = {{%product_eav}}.object_model_id'
450
                                )->andWhere(
451
                                    [
452
                                        '{{%product_eav}}.key' => $matches['propertyKey'],
453
                                        '{{%product_eav}}.value' => $matches['propertyValue']
454
                                    ]
455
                                );
456
                            } elseif ($property->has_static_values == 1) {
457
                                $psv = PropertyStaticValues::findOne(
458
                                    [
459
                                        'property_id' => $property->id,
460
                                        'value' => $matches['propertyValue']
461
                                    ]
462
                                );
463
                                if (!is_null($psv)) {
464
                                    $dependency->tags[] = ActiveRecordHelper::getCommonTag(
465
                                        PropertyStaticValues::className()
466
                                    );
467
                                    $dependency->tags[] = $psv->objectTag();
0 ignored issues
show
Bug introduced by
The method objectTag cannot be called on $psv (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
468
                                    $query->leftJoin(
469
                                        '{{%object_static_values}}',
470
                                        Product::tableName() . '.id = {{%object_static_values}}.object_model_id'
471
                                    )->andWhere(
472
                                        [
473
                                            'object_id' => 3,
474
                                            '{{%object_static_values}}.property_static_value_id' => $psv->id,
475
                                        ]
476
                                    );
477
                                } else {
478
                                    return '';
479
                                }
480
                            }
481
                            /** @todo add column_stored */
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
482
                        } else {
483
                            return '';
484
                        }
485
                    }
486
                }
487
                break;
488
            default:
489
                $query = Product::find();
490
                break;
491
        }
492
        $params = ArrayHelper::merge($params, array_intersect_key($chunkData, $params));
493
        if (!empty($params['where'])) {
494
            $query->andWhere($params['where']);
495
        }
496
        if (!empty($params['limit'])) {
497
            $query->limit($params['limit']);
498
        }
499
500
        if ($params['type'] === 'list') {
501
            $view = $params['listView'];
502
            $objects = $query->all();
503
            foreach ($objects as $object) {
504
                $dependency->tags[] = $object->objectTag();
505
            }
506
            switch ($params['object']) {
507
                case 'product':
508
                    $viewParams = ['products' => $objects];
509
                    break;
510
                default:
511
                    $viewParams = ['products' => $objects];
512
                    break;
513
            }
514
        } else {
515
            $view = $params['itemView'];
516
            $object = $query->one();
517
            if (is_null($object)) {
518
                return '';
519
            }
520
            $dependency->tags[] = $object->objectTag();
521
            switch ($params['object']) {
522 View Code Duplication
                case 'product':
1 ignored issue
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...
523
                    $viewParams = [
524
                        'product' => $object,
525
                        'url' => Url::to(
526
                            [
527
                                '@product',
528
                                'model' => $object,
529
                                'category_group_id' => $object->getMainCategory()->category_group_id,
530
                            ]
531
                        )
532
                    ];
533
                    break;
534 View Code Duplication
                default:
1 ignored issue
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...
535
                    $viewParams = [
536
                        'product' => $object,
537
                        'url' => Url::to(
538
                            [
539
                                '@product',
540
                                'model' => $object,
541
                                'category_group_id' => $object->getMainCategory()->category_group_id,
542
                            ]
543
                        )
544
                    ];
545
                    break;
546
            }
547
        }
548
        return Yii::$app->view->render($view, $viewParams);
549
    }
550
}
551