Complex classes like AttributesCompiler 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 AttributesCompiler, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 5 | abstract class AttributesCompiler extends CompilerFacade |
||
| 6 | { |
||
| 7 | protected function getAttributeDisplayCode($key, $value, $valueCheck) |
||
| 8 | { |
||
| 9 | if ($key === 'style') { |
||
| 10 | $value = preg_replace('/::get(Escaped|Unescaped)Value/', '::get$1Style', $value, 1); |
||
| 11 | } |
||
| 12 | |||
| 13 | return is_null($valueCheck) |
||
| 14 | ? ' ' . $key . '=' . $this->quote . $value . $this->quote |
||
| 15 | : $this->createCode('if (true === ($__value = %1$s)) { ', $valueCheck) |
||
| 16 | . $this->getBooleanAttributeDisplayCode($key) |
||
| 17 | . $this->createCode('} else if (\\Jade\\Compiler::isDisplayable($__value)) { ') |
||
| 18 | . ' ' . $key . '=' . $this->quote . $value . $this->quote |
||
| 19 | . $this->createCode('}'); |
||
| 20 | } |
||
| 21 | |||
| 22 | protected function getBooleanAttributeDisplayCode($key) |
||
| 23 | { |
||
| 24 | return ' ' . $key . ($this->terse |
||
| 25 | ? '' |
||
| 26 | : '=' . $this->quote . $key . $this->quote |
||
| 27 | ); |
||
| 28 | } |
||
| 29 | |||
| 30 | protected function getValueStatement($statements) |
||
| 31 | { |
||
| 32 | return is_string($statements[0]) |
||
| 33 | ? $statements[0] |
||
| 34 | : $statements[0][0]; |
||
| 35 | } |
||
| 36 | |||
| 37 | protected function getAndAttributeCode($attr, &$classes, &$classesCheck) |
||
| 38 | { |
||
| 39 | $addClasses = '""'; |
||
| 40 | if (count($classes) || count($classesCheck)) { |
||
| 41 | foreach ($classes as &$value) { |
||
| 42 | $value = var_export($value, true); |
||
| 43 | } |
||
| 44 | foreach ($classesCheck as $value) { |
||
| 45 | $statements = $this->createStatements($value); |
||
| 46 | $classes[] = $statements[0][0]; |
||
| 47 | } |
||
| 48 | $addClasses = '" " . implode(" ", array(' . implode(', ', $classes) . '))'; |
||
| 49 | $classes = array(); |
||
| 50 | $classesCheck = array(); |
||
| 51 | } |
||
| 52 | $value = empty($attr['value']) ? 'attributes' : $attr['value']; |
||
| 53 | $statements = $this->createStatements($value); |
||
| 54 | |||
| 55 | return $this->createCode( |
||
| 56 | '$__attributes = ' . $this->getValueStatement($statements) . ';' . |
||
| 57 | 'if (is_array($__attributes)) { ' . |
||
| 58 | '$__attributes["class"] = trim(' . |
||
| 59 | '$__classes = (empty($__classes) ? "" : $__classes . " ") . ' . |
||
| 60 | '(isset($__attributes["class"]) ? (is_array($__attributes["class"]) ? implode(" ", $__attributes["class"]) : $__attributes["class"]) : "") . ' . |
||
| 61 | $addClasses . |
||
| 62 | '); ' . |
||
| 63 | 'if (empty($__attributes["class"])) { ' . |
||
| 64 | 'unset($__attributes["class"]); ' . |
||
| 65 | '} ' . |
||
| 66 | '} ' . |
||
| 67 | '\\Jade\\Compiler::displayAttributes($__attributes, ' . var_export($this->quote, true) . ', ' . var_export($this->terse, true) . ');'); |
||
| 68 | } |
||
| 69 | |||
| 70 | protected function getClassAttribute($value, &$classesCheck) |
||
| 71 | { |
||
| 72 | $statements = $this->createStatements($value); |
||
| 73 | $value = is_array($statements[0]) ? $statements[0][0] : $statements[0]; |
||
| 74 | $classesCheck[] = '(is_array($_a = ' . $value . ') ? implode(" ", $_a) : $_a)'; |
||
| 75 | |||
| 76 | return $this->keepNullAttributes ? '' : 'null'; |
||
| 77 | } |
||
| 78 | |||
| 79 | protected function getValueCode($escaped, $value, &$valueCheck) |
||
| 80 | { |
||
| 81 | if ($this->keepNullAttributes) { |
||
| 82 | return $this->escapeIfNeeded($escaped, $value); |
||
| 83 | } |
||
| 84 | |||
| 85 | $valueCheck = $value; |
||
| 86 | |||
| 87 | return $this->escapeIfNeeded($escaped, '$__value'); |
||
| 88 | } |
||
| 89 | |||
| 90 | protected function getAttributeValue($escaped, $key, $value, &$classesCheck, &$valueCheck) |
||
| 91 | { |
||
| 92 | if ($this->isConstant($value)) { |
||
| 93 | $value = trim($value, ' \'"'); |
||
| 94 | |||
| 95 | return $value === 'undefined' ? 'null' : $value; |
||
| 96 | } |
||
| 97 | |||
| 98 | $json = static::parseValue($value); |
||
| 99 | |||
| 100 | if ($key === 'class') { |
||
| 101 | return $json !== null && is_array($json) |
||
| 102 | ? implode(' ', $json) |
||
| 103 | : $this->getClassAttribute($value, $classesCheck); |
||
| 104 | } |
||
| 105 | |||
| 106 | return $this->getValueCode($escaped, $value, $valueCheck); |
||
| 107 | } |
||
| 108 | |||
| 109 | protected function escapeValueIfNeeded($value, $escaped, $valueCheck) |
||
| 110 | { |
||
| 111 | return is_null($valueCheck) && $escaped && !$this->keepNullAttributes |
||
| 112 | ? $this->escapeValue($value) |
||
| 113 | : $value; |
||
| 114 | } |
||
| 115 | |||
| 116 | protected function compileAttributeValue($key, $value, $attr, $valueCheck) |
||
| 117 | { |
||
| 118 | return $value === true || $attr['value'] === true |
||
| 119 | ? $this->getBooleanAttributeDisplayCode($key) |
||
| 120 | : ($value !== false && $attr['value'] !== false && $value !== 'null' && $value !== 'undefined' |
||
| 121 | ? $this->getAttributeDisplayCode( |
||
| 122 | $key, |
||
| 123 | $this->escapeValueIfNeeded($value, $attr['escaped'], $valueCheck), |
||
| 124 | $valueCheck |
||
| 125 | ) |
||
| 126 | : '' |
||
| 127 | ); |
||
| 128 | } |
||
| 129 | |||
| 130 | protected function getAttributeCode($attr, &$classes, &$classesCheck) |
||
| 131 | { |
||
| 132 | $key = trim($attr['name']); |
||
| 133 | |||
| 134 | if ($key === '&attributes') { |
||
| 135 | return $this->getAndAttributeCode($attr, $classes, $classesCheck); |
||
| 136 | } |
||
| 137 | |||
| 138 | $valueCheck = null; |
||
| 139 | $value = trim($attr['value']); |
||
| 140 | |||
| 141 | $value = $this->getAttributeValue($attr['escaped'], $key, $value, $classesCheck, $valueCheck); |
||
| 142 | |||
| 143 | if ($key === 'class') { |
||
| 144 | if ($value !== 'false' && $value !== 'null' && $value !== 'undefined') { |
||
| 145 | array_push($classes, $value); |
||
| 146 | } |
||
| 147 | |||
| 148 | return ''; |
||
| 149 | } |
||
| 150 | |||
| 151 | return $this->compileAttributeValue($key, $value, $attr, $valueCheck); |
||
| 152 | } |
||
| 153 | |||
| 154 | protected function getClassesCode(&$classes, &$classesCheck) |
||
| 155 | { |
||
| 156 | return trim($this->createCode( |
||
| 157 | '$__classes = implode(" ", ' . |
||
| 158 | 'array_unique(explode(" ", (empty($__classes) ? "" : $__classes) . ' . |
||
| 159 | var_export(implode(' ', $classes), true) . ' . ' . |
||
| 160 | 'implode(" ", array(' . implode(', ', $classesCheck) . ')) ' . |
||
| 161 | ')) ' . |
||
| 162 | ');' |
||
| 163 | )); |
||
| 164 | } |
||
| 165 | |||
| 166 | protected function getClassesDisplayCode() |
||
| 167 | { |
||
| 168 | return trim($this->createCode( |
||
| 169 | 'if (!empty($__classes)) { ' . |
||
| 170 | '?> ' . (isset($this->options['classAttribute']) |
||
| 171 | ? $this->options['classAttribute'] |
||
| 172 | : 'class' |
||
| 173 | ) . '=' . $this->quote . '<?php echo $__classes; ?>' . $this->quote . '<?php ' . |
||
| 174 | '} ' . |
||
| 175 | 'unset($__classes); ' |
||
| 176 | )); |
||
| 177 | } |
||
| 178 | |||
| 179 | /** |
||
| 180 | * @param array $attributes |
||
| 181 | */ |
||
| 182 | protected function compileAttributes($attributes) |
||
| 183 | { |
||
| 184 | $items = ''; |
||
| 185 | $classes = array(); |
||
| 186 | $classesCheck = array(); |
||
| 187 | |||
| 188 | foreach ($attributes as $attr) { |
||
| 189 | $items .= $this->getAttributeCode($attr, $classes, $classesCheck); |
||
| 190 | } |
||
| 191 | |||
| 192 | $items .= $this->getClassesCode($classes, $classesCheck); |
||
| 193 | |||
| 194 | $this->buffer($items, false); |
||
| 195 | } |
||
| 196 | } |
||
| 197 |