Complex classes like FunctionCall 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 FunctionCall, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 16 | class FunctionCall extends AbstractExpressionCompiler |
||
| 17 | { |
||
| 18 | protected $name = 'PhpParser\Node\Expr\FuncCall'; |
||
| 19 | |||
| 20 | /** |
||
| 21 | * @param \PhpParser\Node\Expr\FuncCall $expr |
||
| 22 | * @param Context $context |
||
| 23 | * @return CompiledExpression |
||
| 24 | */ |
||
| 25 | 8 | protected function compile($expr, Context $context) |
|
|
|
|||
| 26 | { |
||
| 27 | 8 | $expressionCompiler = $context->getExpressionCompiler(); |
|
| 28 | 8 | $fNameExpression = $expressionCompiler->compile($expr->name); |
|
| 29 | |||
| 30 | 8 | if ($fNameExpression->isCallable()) { |
|
| 31 | $value = $fNameExpression->getValue(); |
||
| 32 | if ($value && $value instanceof ClosureDefinition) { |
||
| 33 | return $value->run($this->parseArgs($expr, clone $context), $context); |
||
| 34 | } |
||
| 35 | } |
||
| 36 | |||
| 37 | 8 | if ($fNameExpression->isString() && $fNameExpression->isCorrectValue()) { |
|
| 38 | 8 | $name = $fNameExpression->getValue(); |
|
| 39 | 8 | } else { |
|
| 40 | 1 | $context->debug( |
|
| 41 | 'Unexpected function name type ' . $fNameExpression->getTypeName(), |
||
| 42 | $expr->name |
||
| 43 | ); |
||
| 44 | |||
| 45 | return new CompiledExpression; |
||
| 46 | } |
||
| 47 | |||
| 48 | 8 | $compiler = $context->application->compiler; |
|
| 49 | |||
| 50 | 8 | $exists = false; |
|
| 51 | 8 | $namespace = null; |
|
| 52 | |||
| 53 | 8 | if ($context->scope) { |
|
| 54 | 8 | $namespace = $context->scope->getNamespace(); |
|
| 55 | 8 | } |
|
| 56 | |||
| 57 | 8 | if ($namespace === null) { |
|
| 58 | 1 | $functionDefinition = $compiler->getFunction($name); |
|
| 59 | 1 | } else { |
|
| 60 | 8 | $functionDefinition = $compiler->getFunctionNS($name, $namespace); |
|
| 61 | } |
||
| 62 | |||
| 63 | 8 | if (!$functionDefinition) { |
|
| 64 | 8 | $exists = function_exists($name); |
|
| 65 | 8 | } |
|
| 66 | |||
| 67 | 8 | if ($functionDefinition) { |
|
| 68 | if (!$functionDefinition->isCompiled()) { |
||
| 69 | $functionDefinition->compile(clone $context); |
||
| 70 | } |
||
| 71 | |||
| 72 | $exists = true; |
||
| 73 | } |
||
| 74 | |||
| 75 | 8 | $arguments = $this->parseArgs($expr, clone $context); |
|
| 76 | |||
| 77 | 8 | if (!$functionDefinition) { |
|
| 78 | 8 | $reflector = new Reflector(Reflector::manuallyFactory()); |
|
| 79 | 8 | $functionReflection = $reflector->getFunction($name); |
|
| 80 | 8 | if ($functionReflection) { |
|
| 81 | 3 | $argumentsSuccessPass = true; |
|
| 82 | |||
| 83 | 3 | if (count($arguments) > 0) { |
|
| 84 | 3 | foreach ($arguments as $key => $argument) { |
|
| 85 | 3 | $parameter = $functionReflection->getParameter($key); |
|
| 86 | 3 | if (!$parameter) { |
|
| 87 | $argumentsSuccessPass = false; |
||
| 88 | |||
| 89 | /** |
||
| 90 | * @todo Think a little bit more about it |
||
| 91 | */ |
||
| 92 | continue; |
||
| 93 | } |
||
| 94 | |||
| 95 | 3 | switch ($parameter->getType()) { |
|
| 96 | 3 | case CompiledExpression::MIXED: |
|
| 97 | //continue |
||
| 98 | 2 | break; |
|
| 99 | 2 | case CompiledExpression::INTEGER: |
|
| 100 | 2 | switch ($argument->getType()) { |
|
| 101 | 2 | case CompiledExpression::INTEGER: |
|
| 102 | 2 | break; |
|
| 103 | default: |
||
| 104 | $argumentsSuccessPass = false; |
||
| 105 | break; |
||
| 106 | 2 | } |
|
| 107 | 2 | break; |
|
| 108 | case CompiledExpression::DOUBLE: |
||
| 109 | switch ($argument->getType()) { |
||
| 110 | case CompiledExpression::DOUBLE: |
||
| 111 | break; |
||
| 112 | default: |
||
| 113 | $argumentsSuccessPass = false; |
||
| 114 | break; |
||
| 115 | } |
||
| 116 | break; |
||
| 117 | case CompiledExpression::NUMBER: |
||
| 118 | switch ($argument->getType()) { |
||
| 119 | case CompiledExpression::INTEGER: |
||
| 120 | case CompiledExpression::STRING: |
||
| 121 | case CompiledExpression::NUMBER: |
||
| 122 | break; |
||
| 123 | default: |
||
| 124 | $argumentsSuccessPass = false; |
||
| 125 | break; |
||
| 126 | } |
||
| 127 | break; |
||
| 128 | case CompiledExpression::RESOURCE: |
||
| 129 | switch ($argument->getType()) { |
||
| 130 | case CompiledExpression::RESOURCE: |
||
| 131 | break; |
||
| 132 | default: |
||
| 133 | $argumentsSuccessPass = false; |
||
| 134 | break; |
||
| 135 | } |
||
| 136 | break; |
||
| 137 | case CompiledExpression::ARR: |
||
| 138 | switch ($argument->getType()) { |
||
| 139 | case CompiledExpression::ARR: |
||
| 140 | break; |
||
| 141 | default: |
||
| 142 | $argumentsSuccessPass = false; |
||
| 143 | break; |
||
| 144 | } |
||
| 145 | break; |
||
| 146 | case CompiledExpression::STRING: |
||
| 147 | switch ($argument->getType()) { |
||
| 148 | case CompiledExpression::STRING: |
||
| 149 | break; |
||
| 150 | default: |
||
| 151 | $argumentsSuccessPass = false; |
||
| 152 | break; |
||
| 153 | } |
||
| 154 | break; |
||
| 155 | case CompiledExpression::OBJECT: |
||
| 156 | switch ($argument->getType()) { |
||
| 157 | case CompiledExpression::OBJECT: |
||
| 158 | break; |
||
| 159 | default: |
||
| 160 | $argumentsSuccessPass = false; |
||
| 161 | break; |
||
| 162 | } |
||
| 163 | break; |
||
| 164 | case CompiledExpression::BOOLEAN: |
||
| 165 | switch ($argument->getType()) { |
||
| 166 | case CompiledExpression::OBJECT: |
||
| 167 | break; |
||
| 168 | default: |
||
| 169 | $argumentsSuccessPass = false; |
||
| 170 | break; |
||
| 171 | } |
||
| 172 | break; |
||
| 173 | case CompiledExpression::CALLABLE_TYPE: |
||
| 174 | switch ($argument->getType()) { |
||
| 175 | case CompiledExpression::CALLABLE_TYPE: |
||
| 176 | break; |
||
| 177 | case CompiledExpression::STRING: |
||
| 178 | /** |
||
| 179 | * @todo We need additional check on it |
||
| 180 | */ |
||
| 181 | break; |
||
| 182 | /** |
||
| 183 | * array($this, 'method') |
||
| 184 | */ |
||
| 185 | case CompiledExpression::ARR: |
||
| 186 | /** |
||
| 187 | * @todo We need additional check on it |
||
| 188 | */ |
||
| 189 | break; |
||
| 190 | default: |
||
| 191 | $argumentsSuccessPass = false; |
||
| 192 | break; |
||
| 193 | } |
||
| 194 | break; |
||
| 195 | default: |
||
| 196 | $argumentsSuccessPass = false; |
||
| 197 | break; |
||
| 198 | 3 | } |
|
| 199 | 3 | } |
|
| 200 | 3 | } |
|
| 201 | |||
| 202 | 3 | if (count($arguments) < $functionReflection->getNumberOfRequiredParameters()) { |
|
| 203 | $argumentsSuccessPass = false; |
||
| 204 | } |
||
| 205 | |||
| 206 | 3 | if ($argumentsSuccessPass && $functionReflection->isRunnable()) { |
|
| 207 | 3 | array_walk( |
|
| 208 | 3 | $arguments, |
|
| 209 | 3 | function (&$item) { |
|
| 210 | /** @var CompiledExpression $item */ |
||
| 211 | 3 | $item = $item->getValue(); |
|
| 212 | 3 | } |
|
| 213 | 3 | ); |
|
| 214 | |||
| 215 | 3 | return new CompiledExpression( |
|
| 216 | 3 | $functionReflection->getReturnType(), |
|
| 217 | 3 | $functionReflection->run($arguments) |
|
| 218 | 3 | ); |
|
| 219 | } |
||
| 220 | |||
| 221 | return new CompiledExpression($functionReflection->getReturnType()); |
||
| 222 | } |
||
| 223 | 7 | } |
|
| 224 | |||
| 225 | 7 | if (!$exists) { |
|
| 226 | $context->notice( |
||
| 227 | 'undefined-fcall', |
||
| 228 | sprintf('Function %s() does not exist', $expr->name->parts[0]), |
||
| 229 | $expr |
||
| 230 | ); |
||
| 231 | } |
||
| 232 | |||
| 233 | 7 | return new CompiledExpression(); |
|
| 234 | } |
||
| 235 | |||
| 236 | /** |
||
| 237 | * @param \PhpParser\Node\Expr\FuncCall $expr |
||
| 238 | * @return CompiledExpression[] |
||
| 239 | */ |
||
| 240 | 8 | protected function parseArgs($expr, Context $context) |
|
| 250 | } |
||
| 251 |
A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.
You can also find more information in the “Code” section of your repository.