Complex classes like Parser 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 Parser, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 12 | final class Parser |
||
| 13 | { |
||
| 14 | const NULL_VALUE = 'null'; |
||
| 15 | |||
| 16 | /** |
||
| 17 | * @var Lexer |
||
| 18 | */ |
||
| 19 | private $lexer; |
||
| 20 | |||
| 21 | /** |
||
| 22 | * @var ExpanderInitializer |
||
| 23 | */ |
||
| 24 | private $expanderInitializer; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * @param Lexer $lexer |
||
| 28 | */ |
||
| 29 | public function __construct(Lexer $lexer, ExpanderInitializer $expanderInitializer) |
||
| 34 | |||
| 35 | /** |
||
| 36 | * @param string $pattern |
||
| 37 | * @return bool |
||
| 38 | */ |
||
| 39 | public function hasValidSyntax($pattern) |
||
| 48 | |||
| 49 | /** |
||
| 50 | * @param string $pattern |
||
| 51 | * @throws UnknownExpanderException |
||
| 52 | * @return Pattern\TypePattern |
||
| 53 | */ |
||
| 54 | public function parse($pattern) |
||
| 64 | |||
| 65 | /** |
||
| 66 | * @param $pattern |
||
| 67 | * @return AST\Pattern |
||
| 68 | */ |
||
| 69 | public function getAST($pattern) |
||
| 74 | |||
| 75 | /** |
||
| 76 | * Create AST root |
||
| 77 | * |
||
| 78 | * @return AST\Pattern |
||
| 79 | */ |
||
| 80 | private function getPattern() |
||
| 101 | |||
| 102 | /** |
||
| 103 | * @param AST\Pattern $pattern |
||
| 104 | */ |
||
| 105 | private function addExpanderNodes(AST\Pattern $pattern) |
||
| 111 | |||
| 112 | /** |
||
| 113 | * Try to get next expander, return null if there is no expander left |
||
| 114 | * @return AST\Expander|null |
||
| 115 | */ |
||
| 116 | private function getNextExpanderNode() |
||
| 140 | |||
| 141 | /** |
||
| 142 | * @return mixed |
||
| 143 | * @throws PatternException |
||
| 144 | */ |
||
| 145 | private function getExpanderName() |
||
| 155 | |||
| 156 | /** |
||
| 157 | * Add arguments to expander |
||
| 158 | * |
||
| 159 | * @param AST\Expander $expander |
||
| 160 | */ |
||
| 161 | private function addArgumentValues(AST\Expander $expander) |
||
| 177 | |||
| 178 | /** |
||
| 179 | * Try to get next argument. Return false if there are no arguments left before ")" |
||
| 180 | * @return null|mixed |
||
| 181 | */ |
||
| 182 | private function getNextArgumentValue() |
||
| 217 | |||
| 218 | /** |
||
| 219 | * return array |
||
| 220 | */ |
||
| 221 | private function getArrayArgument() |
||
| 238 | |||
| 239 | /** |
||
| 240 | * @param array $array |
||
| 241 | * @return bool |
||
| 242 | * @throws PatternException |
||
| 243 | */ |
||
| 244 | private function getNextArrayElement(array &$array) |
||
| 274 | |||
| 275 | /** |
||
| 276 | * @return bool |
||
| 277 | */ |
||
| 278 | private function isNextCloseParenthesis() |
||
| 285 | |||
| 286 | /** |
||
| 287 | * @param $unexpectedToken |
||
| 288 | * @param null $expected |
||
| 289 | * @throws PatternException |
||
| 290 | */ |
||
| 291 | private function unexpectedSyntaxError($unexpectedToken, $expected = null) |
||
| 300 | |||
| 301 | /** |
||
| 302 | * @param null $expected |
||
| 303 | * @throws PatternException |
||
| 304 | */ |
||
| 305 | private function unexpectedEndOfString($expected = null) |
||
| 314 | |||
| 315 | /** |
||
| 316 | * @return bool |
||
| 317 | */ |
||
| 318 | private function endOfPattern() |
||
| 322 | } |
||
| 323 |
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:
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
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: