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 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 |
||
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 |
||
|
|||
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); |
||
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); |
||
98 | break; |
||
99 | View Code Duplication | case '~' : |
|
100 | if ($preprocess === false) break; |
||
101 | $replacement = Yii::$app->cache->get($cacheKey); |
||
102 | if ($replacement === false) { |
||
103 | $replacement = static::renderUrl($chunkData, $dependency); |
||
104 | Yii::$app->cache->set( |
||
105 | $cacheKey, |
||
106 | $replacement, |
||
107 | 84600, |
||
108 | $dependency |
||
109 | ); |
||
110 | } |
||
111 | break; |
||
112 | View Code Duplication | case '*': |
|
113 | if ($preprocess === false) { |
||
114 | break; |
||
115 | } |
||
116 | $replacement = Yii::$app->cache->get($cacheKey); |
||
117 | if ($replacement === false) { |
||
118 | $replacement = static::renderProducts($chunkData, $dependency); |
||
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) |
||
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) |
||
211 | |||
212 | /** |
||
213 | * Extracts chunk data from chunk call |
||
214 | * uses regexp to extract param data from placeholder |
||
215 | * [[$chunk <paramName>='<escapedValue>'|'<escapedDefault>' <paramName>=<unescapedValue>|<unescapedDefault>]] |
||
216 | * iterate matches. |
||
217 | * While iterating converts escapedValue and escapedDefault into string, unescapedValue and unescapedDefault - into float |
||
218 | * Returns chunk data array like: |
||
219 | * [ |
||
220 | * 'key' => 'chunkKey', |
||
221 | * 'firstParam'=> 'string value', |
||
222 | * 'firstParam-default'=> 'default string value', |
||
223 | * 'secondParam'=> float value, |
||
224 | * 'secondParam-default'=> default float value, |
||
225 | * ] |
||
226 | * |
||
227 | * @param string $rawChunk |
||
228 | * @return array |
||
229 | */ |
||
230 | private static function sanitizeChunk($rawChunk) |
||
254 | |||
255 | /** |
||
256 | * Compiles single chunk |
||
257 | * uses regexp to find placeholders and extract it's data from chunk value field |
||
258 | * [[<token><paramName>:<format><params>]] |
||
259 | * token switch is for future functionality increase |
||
260 | * now method only recognizes + token and replaces following param with according $arguments array data |
||
261 | * applies formatter according previously defined param values type if needed |
||
262 | * if param name from placeholder was not found in arguments array, placeholder in the compiled chunk will be replaced with empty string |
||
263 | * returns compiled chunk |
||
264 | * |
||
265 | * @param string $content_key Key for caching compiled content version |
||
266 | * @param yii\caching\Dependency $dependency Cache dependency |
||
267 | * @param string $chunk ContentBlock instance |
||
268 | * @param array $arguments Arguments for this chunk from original content |
||
269 | * @param string $key ContentBlock key string to prevent endless recursion |
||
270 | * @return string Result string ready for replacing |
||
271 | */ |
||
272 | private static function compileChunk($chunk, $arguments, $key, $content_key, $dependency) |
||
300 | |||
301 | /** |
||
302 | * Find formatter declarations in chunk placeholders. if find trying to apply |
||
303 | * yii\i18n\Formatter formats see yii\i18n\Formatter for details |
||
304 | * |
||
305 | * @param string $value single placeholder declaration from chunk |
||
306 | * @param string $format |
||
307 | * @param array $params |
||
308 | * @return string|array |
||
309 | */ |
||
310 | private static function applyFormatter($value, $format, $params) |
||
323 | |||
324 | /** |
||
325 | * Fetches single chunk by key from static var |
||
326 | * if is no there - get it from db and push to static array |
||
327 | * |
||
328 | * @param $key string Chunk key field |
||
329 | * @return string Chunk value field |
||
330 | */ |
||
331 | private static function fetchChunkByKey($key) |
||
349 | |||
350 | /** |
||
351 | * preloads chunks with option preload = 1 |
||
352 | * and push it to static array |
||
353 | * |
||
354 | * @return array|void |
||
355 | */ |
||
356 | private static function preloadChunks() |
||
374 | |||
375 | /** |
||
376 | * renders chunks in template files |
||
377 | * @param string $key ContentBlock key |
||
378 | * @param array $params . Array of params to be replaced while render |
||
379 | * @param yii\base\Model $model . Caller model instance to use in caching |
||
380 | * @return mixed |
||
381 | */ |
||
382 | public static function getChunk($key, $params = [], yii\base\Model $model = null) |
||
401 | |||
402 | /** |
||
403 | * renders product item and list. |
||
404 | * possible can render all objects, but need for few logic change |
||
405 | * @param array $chunkData params for select and render |
||
406 | * @param TagDependency $dependency |
||
407 | * @return mixed |
||
408 | */ |
||
409 | private static function renderProducts($chunkData, &$dependency) |
||
562 | } |
||
563 |
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.