| Total Complexity | 44 |
| Total Lines | 248 |
| Duplicated Lines | 19.76 % |
| Changes | 0 | ||
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 TextMenuContentObject 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.
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 TextMenuContentObject, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 22 | class TextMenuContentObject extends AbstractMenuContentObject |
||
| 23 | { |
||
| 24 | /** |
||
| 25 | * Calls procesItemStates() so that the common configuration for the menu items are resolved into individual configuration per item. |
||
| 26 | * Sets the result for the new "normal state" in $this->result |
||
| 27 | * |
||
| 28 | * @see AbstractMenuContentObject::procesItemStates() |
||
| 29 | */ |
||
| 30 | View Code Duplication | public function generate() |
|
| 31 | { |
||
| 32 | $NOconf = []; |
||
| 33 | $splitCount = count($this->menuArr); |
||
| 34 | if ($splitCount) { |
||
| 35 | list($NOconf) = $this->procesItemStates($splitCount); |
||
| 36 | } |
||
| 37 | if (!empty($this->mconf['debugItemConf'])) { |
||
| 38 | echo '<h3>$NOconf:</h3>'; |
||
| 39 | debug($NOconf); |
||
| 40 | } |
||
| 41 | $this->result = $NOconf; |
||
| 42 | } |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Traverses the ->result array of menu items configuration (made by ->generate()) and renders each item. |
||
| 46 | * During the execution of this function many internal methods prefixed "extProc_" from this class is called and |
||
| 47 | * many of these are for now dummy functions. |
||
| 48 | * An instance of ContentObjectRenderer is also made and for each menu item rendered it is loaded with |
||
| 49 | * the record for that page so that any stdWrap properties that applies will have the current menu items record available. |
||
| 50 | * |
||
| 51 | * @return string The HTML for the menu (returns result through $this->extProc_finish(); ) |
||
| 52 | */ |
||
| 53 | public function writeMenu() |
||
| 54 | { |
||
| 55 | if (empty($this->result)) { |
||
| 56 | return ''; |
||
| 57 | } |
||
| 58 | |||
| 59 | $this->WMresult = ''; |
||
| 60 | $this->INPfixMD5 = substr(md5(microtime() . 'tmenu'), 0, 4); |
||
|
|
|||
| 61 | $this->WMmenuItems = count($this->result); |
||
| 62 | $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class); |
||
| 63 | $this->WMsubmenuObjSuffixes = $typoScriptService->explodeConfigurationForOptionSplit(['sOSuffix' => $this->mconf['submenuObjSuffixes']], $this->WMmenuItems); |
||
| 64 | $this->extProc_init(); |
||
| 65 | foreach ($this->result as $key => $val) { |
||
| 66 | $GLOBALS['TSFE']->register['count_HMENU_MENUOBJ']++; |
||
| 67 | $GLOBALS['TSFE']->register['count_MENUOBJ']++; |
||
| 68 | // Initialize the cObj with the page record of the menu item |
||
| 69 | $this->WMcObj->start($this->menuArr[$key], 'pages'); |
||
| 70 | $this->I = []; |
||
| 71 | $this->I['key'] = $key; |
||
| 72 | $this->I['INPfix'] = ($this->imgNameNotRandom ? '' : '_' . $this->INPfixMD5) . '_' . $key; |
||
| 73 | $this->I['val'] = $val; |
||
| 74 | $this->I['title'] = isset($this->I['val']['stdWrap.']) ? $this->WMcObj->stdWrap($this->getPageTitle($this->menuArr[$key]['title'], $this->menuArr[$key]['nav_title']), $this->I['val']['stdWrap.']) : $this->getPageTitle($this->menuArr[$key]['title'], $this->menuArr[$key]['nav_title']); |
||
| 75 | $this->I['uid'] = $this->menuArr[$key]['uid']; |
||
| 76 | $this->I['mount_pid'] = $this->menuArr[$key]['mount_pid']; |
||
| 77 | $this->I['pid'] = $this->menuArr[$key]['pid']; |
||
| 78 | $this->I['spacer'] = $this->menuArr[$key]['isSpacer']; |
||
| 79 | // Set access key |
||
| 80 | View Code Duplication | if ($this->mconf['accessKey']) { |
|
| 81 | $this->I['accessKey'] = $this->accessKey($this->I['title']); |
||
| 82 | } else { |
||
| 83 | $this->I['accessKey'] = []; |
||
| 84 | } |
||
| 85 | // Make link tag |
||
| 86 | $this->I['val']['ATagParams'] = $this->WMcObj->getATagParams($this->I['val']); |
||
| 87 | View Code Duplication | if (isset($this->I['val']['additionalParams.'])) { |
|
| 88 | $this->I['val']['additionalParams'] = $this->WMcObj->stdWrap($this->I['val']['additionalParams'], $this->I['val']['additionalParams.']); |
||
| 89 | } |
||
| 90 | $this->I['linkHREF'] = $this->link($key, $this->I['val']['altTarget'], $this->mconf['forceTypeValue']); |
||
| 91 | if (empty($this->I['linkHREF'])) { |
||
| 92 | $this->I['val']['doNotLinkIt'] = 1; |
||
| 93 | } |
||
| 94 | // Title attribute of links: |
||
| 95 | $titleAttrValue = isset($this->I['val']['ATagTitle.']) ? $this->WMcObj->stdWrap($this->I['val']['ATagTitle'], $this->I['val']['ATagTitle.']) . $this->I['accessKey']['alt'] : $this->I['val']['ATagTitle'] . $this->I['accessKey']['alt']; |
||
| 96 | if ($titleAttrValue !== '') { |
||
| 97 | $this->I['linkHREF']['title'] = $titleAttrValue; |
||
| 98 | } |
||
| 99 | |||
| 100 | // Calling extra processing function |
||
| 101 | $this->extProc_beforeLinking($key); |
||
| 102 | // stdWrap for doNotLinkIt |
||
| 103 | View Code Duplication | if (isset($this->I['val']['doNotLinkIt.'])) { |
|
| 104 | $this->I['val']['doNotLinkIt'] = $this->WMcObj->stdWrap($this->I['val']['doNotLinkIt'], $this->I['val']['doNotLinkIt.']); |
||
| 105 | } |
||
| 106 | // Compile link tag |
||
| 107 | if (!$this->I['val']['doNotLinkIt']) { |
||
| 108 | $this->I['val']['doNotLinkIt'] = 0; |
||
| 109 | } |
||
| 110 | if (!$this->I['spacer'] && $this->I['val']['doNotLinkIt'] != 1) { |
||
| 111 | $this->setATagParts(); |
||
| 112 | } else { |
||
| 113 | $this->I['A1'] = ''; |
||
| 114 | $this->I['A2'] = ''; |
||
| 115 | } |
||
| 116 | // ATagBeforeWrap processing: |
||
| 117 | if ($this->I['val']['ATagBeforeWrap']) { |
||
| 118 | $wrapPartsBefore = explode('|', $this->I['val']['linkWrap']); |
||
| 119 | $wrapPartsAfter = ['', '']; |
||
| 120 | } else { |
||
| 121 | $wrapPartsBefore = ['', '']; |
||
| 122 | $wrapPartsAfter = explode('|', $this->I['val']['linkWrap']); |
||
| 123 | } |
||
| 124 | if ($this->I['val']['stdWrap2'] || isset($this->I['val']['stdWrap2.'])) { |
||
| 125 | $stdWrap2 = isset($this->I['val']['stdWrap2.']) ? $this->WMcObj->stdWrap('|', $this->I['val']['stdWrap2.']) : '|'; |
||
| 126 | $wrapPartsStdWrap = explode($this->I['val']['stdWrap2'] ? $this->I['val']['stdWrap2'] : '|', $stdWrap2); |
||
| 127 | } else { |
||
| 128 | $wrapPartsStdWrap = ['', '']; |
||
| 129 | } |
||
| 130 | // Make before, middle and after parts |
||
| 131 | $this->I['parts'] = []; |
||
| 132 | $this->I['parts']['before'] = $this->getBeforeAfter('before'); |
||
| 133 | $this->I['parts']['stdWrap2_begin'] = $wrapPartsStdWrap[0]; |
||
| 134 | // stdWrap for doNotShowLink |
||
| 135 | View Code Duplication | if (isset($this->I['val']['doNotShowLink.'])) { |
|
| 136 | $this->I['val']['doNotShowLink'] = $this->WMcObj->stdWrap($this->I['val']['doNotShowLink'], $this->I['val']['doNotShowLink.']); |
||
| 137 | } |
||
| 138 | if (!$this->I['val']['doNotShowLink']) { |
||
| 139 | $this->I['parts']['notATagBeforeWrap_begin'] = $wrapPartsAfter[0]; |
||
| 140 | $this->I['parts']['ATag_begin'] = $this->I['A1']; |
||
| 141 | $this->I['parts']['ATagBeforeWrap_begin'] = $wrapPartsBefore[0]; |
||
| 142 | $this->I['parts']['title'] = $this->I['title']; |
||
| 143 | $this->I['parts']['ATagBeforeWrap_end'] = $wrapPartsBefore[1]; |
||
| 144 | $this->I['parts']['ATag_end'] = $this->I['A2']; |
||
| 145 | $this->I['parts']['notATagBeforeWrap_end'] = $wrapPartsAfter[1]; |
||
| 146 | } |
||
| 147 | $this->I['parts']['stdWrap2_end'] = $wrapPartsStdWrap[1]; |
||
| 148 | $this->I['parts']['after'] = $this->getBeforeAfter('after'); |
||
| 149 | // Passing I to a user function |
||
| 150 | if ($this->mconf['IProcFunc']) { |
||
| 151 | $this->I = $this->userProcess('IProcFunc', $this->I); |
||
| 152 | } |
||
| 153 | // Merge parts + beforeAllWrap |
||
| 154 | $this->I['theItem'] = implode('', $this->I['parts']); |
||
| 155 | $this->I['theItem'] = $this->extProc_beforeAllWrap($this->I['theItem'], $key); |
||
| 156 | // allWrap: |
||
| 157 | $allWrap = isset($this->I['val']['allWrap.']) ? $this->WMcObj->stdWrap($this->I['val']['allWrap'], $this->I['val']['allWrap.']) : $this->I['val']['allWrap']; |
||
| 158 | $this->I['theItem'] = $this->WMcObj->wrap($this->I['theItem'], $allWrap); |
||
| 159 | View Code Duplication | if ($this->I['val']['subst_elementUid']) { |
|
| 160 | $this->I['theItem'] = str_replace('{elementUid}', $this->I['uid'], $this->I['theItem']); |
||
| 161 | } |
||
| 162 | // allStdWrap: |
||
| 163 | View Code Duplication | if (is_array($this->I['val']['allStdWrap.'])) { |
|
| 164 | $this->I['theItem'] = $this->WMcObj->stdWrap($this->I['theItem'], $this->I['val']['allStdWrap.']); |
||
| 165 | } |
||
| 166 | // Calling extra processing function |
||
| 167 | $this->extProc_afterLinking($key); |
||
| 168 | } |
||
| 169 | return $this->extProc_finish(); |
||
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Generates the before* and after* images for TMENUs |
||
| 174 | * |
||
| 175 | * @param string $pref Can be "before" or "after" and determines which kind of image to create (basically this is the prefix of the TypoScript properties that are read from the ->I['val'] array |
||
| 176 | * @return string The resulting HTML of the image, if any. |
||
| 177 | */ |
||
| 178 | public function getBeforeAfter($pref) |
||
| 179 | { |
||
| 180 | $res = ''; |
||
| 181 | if ($imgInfo = $this->WMcObj->getImgResource($this->I['val'][$pref . 'Img'], $this->I['val'][$pref . 'Img.'])) { |
||
| 182 | $theName = $this->imgNamePrefix . $this->I['uid'] . $this->I['INPfix'] . $pref; |
||
| 183 | $name = ' ' . $this->nameAttribute . '="' . $theName . '"'; |
||
| 184 | $GLOBALS['TSFE']->imagesOnPage[] = $imgInfo[3]; |
||
| 185 | $res = '<img' . ' src="' . $GLOBALS['TSFE']->absRefPrefix . $imgInfo[3] . '"' . ' width="' . $imgInfo[0] . '"' . ' height="' . $imgInfo[1] . '"' . $name . ($this->I['val'][$pref . 'ImgTagParams'] ? ' ' . $this->I['val'][$pref . 'ImgTagParams'] : '') . $this->parent_cObj->getBorderAttr(' border="0"'); |
||
| 186 | if (!strstr($res, 'alt="')) { |
||
| 187 | // Adding alt attribute if not set. |
||
| 188 | $res .= ' alt=""'; |
||
| 189 | } |
||
| 190 | $res .= ' />'; |
||
| 191 | if ($this->I['val'][$pref . 'ImgLink']) { |
||
| 192 | $res = $this->I['A1'] . $res . $this->I['A2']; |
||
| 193 | } |
||
| 194 | } |
||
| 195 | $processedPref = isset($this->I['val'][$pref . '.']) ? $this->WMcObj->stdWrap($this->I['val'][$pref], $this->I['val'][$pref . '.']) : $this->I['val'][$pref]; |
||
| 196 | if (isset($this->I['val'][$pref . 'Wrap'])) { |
||
| 197 | return $this->WMcObj->wrap($res . $processedPref, $this->I['val'][$pref . 'Wrap']); |
||
| 198 | } |
||
| 199 | return $res . $processedPref; |
||
| 200 | } |
||
| 201 | |||
| 202 | /** |
||
| 203 | * Called right before the traversing of $this->result begins. |
||
| 204 | * Can be used for various initialization |
||
| 205 | * |
||
| 206 | * @access private |
||
| 207 | * @see writeMenu() |
||
| 208 | */ |
||
| 209 | public function extProc_init() |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * Called right before the creation of the link for the menu item |
||
| 215 | * |
||
| 216 | * @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found |
||
| 217 | * @access private |
||
| 218 | * @see writeMenu() |
||
| 219 | */ |
||
| 220 | public function extProc_beforeLinking($key) |
||
| 221 | { |
||
| 222 | } |
||
| 223 | |||
| 224 | /** |
||
| 225 | * Called right after the creation of links for the menu item. This is also the last function call before the while-loop traversing menu items goes to the next item. |
||
| 226 | * This function MUST set $this->WMresult.=[HTML for menu item] to add the generated menu item to the internal accumulation of items. |
||
| 227 | * |
||
| 228 | * @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found |
||
| 229 | * @access private |
||
| 230 | * @see writeMenu() |
||
| 231 | */ |
||
| 232 | View Code Duplication | public function extProc_afterLinking($key) |
|
| 233 | { |
||
| 234 | // Add part to the accumulated result + fetch submenus |
||
| 235 | if (!$this->I['spacer']) { |
||
| 236 | $this->I['theItem'] .= $this->subMenu($this->I['uid'], $this->WMsubmenuObjSuffixes[$key]['sOSuffix']); |
||
| 237 | } |
||
| 238 | $part = isset($this->I['val']['wrapItemAndSub.']) ? $this->WMcObj->stdWrap($this->I['val']['wrapItemAndSub'], $this->I['val']['wrapItemAndSub.']) : $this->I['val']['wrapItemAndSub']; |
||
| 239 | $this->WMresult .= $part ? $this->WMcObj->wrap($this->I['theItem'], $part) : $this->I['theItem']; |
||
| 240 | } |
||
| 241 | |||
| 242 | /** |
||
| 243 | * Called before the "allWrap" happens on the menu item. |
||
| 244 | * |
||
| 245 | * @param string $item The current content of the menu item, $this->I['theItem'], passed along. |
||
| 246 | * @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found |
||
| 247 | * @return string The modified version of $item, going back into $this->I['theItem'] |
||
| 248 | * @access private |
||
| 249 | * @see writeMenu() |
||
| 250 | */ |
||
| 251 | public function extProc_beforeAllWrap($item, $key) |
||
| 254 | } |
||
| 255 | |||
| 256 | /** |
||
| 257 | * Called before the writeMenu() function returns (only if a menu was generated) |
||
| 258 | * |
||
| 259 | * @return string The total menu content should be returned by this function |
||
| 260 | * @access private |
||
| 261 | * @see writeMenu() |
||
| 262 | */ |
||
| 263 | View Code Duplication | public function extProc_finish() |
|
| 272 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.