Complex classes like InternalCallMapHandler 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 InternalCallMapHandler, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 24 | class InternalCallMapHandler | ||
| 25 | { | ||
| 26 | const PHP_MAJOR_VERSION = 7; | ||
| 27 | const PHP_MINOR_VERSION = 4; | ||
| 28 | const LOWEST_AVAILABLE_DELTA = 71; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * @var ?int | ||
| 32 | */ | ||
| 33 | private static $loaded_php_major_version = null; | ||
| 34 | /** | ||
| 35 | * @var ?int | ||
| 36 | */ | ||
| 37 | private static $loaded_php_minor_version = null; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * @var array<array<int|string,string>>|null | ||
| 41 | */ | ||
| 42 | private static $call_map = null; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * @var array<array<int, TCallable>>|null | ||
| 46 | */ | ||
| 47 | private static $call_map_callables = []; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * @var array<string, list<list<Type\TaintKind::*>>> | ||
| 51 | */ | ||
| 52 | private static $taint_sink_map = []; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * @param string $method_id | ||
| 56 | * @param array<int, PhpParser\Node\Arg> $args | ||
| 57 | * | ||
| 58 | * @return TCallable | ||
| 59 | */ | ||
| 60 | public static function getCallableFromCallMapById( | ||
| 81 | |||
| 82 | /** | ||
| 83 | * @param array<int, TCallable> $callables | ||
| 84 | * @param array<int, PhpParser\Node\Arg> $args | ||
| 85 | * | ||
| 86 | * @return TCallable | ||
| 87 | */ | ||
| 88 | public static function getMatchingCallableFromCallMapOptions( | ||
| 219 | |||
| 220 | /** | ||
| 221 | * @param string $function_id | ||
| 222 | * | ||
| 223 | * @return array|null | ||
| 224 | * @psalm-return array<int, TCallable>|null | ||
| 225 | */ | ||
| 226 | public static function getCallablesFromCallMap($function_id) | ||
| 227 |     { | ||
| 228 | $call_map_key = strtolower($function_id); | ||
| 229 | |||
| 230 |         if (isset(self::$call_map_callables[$call_map_key])) { | ||
| 231 | return self::$call_map_callables[$call_map_key]; | ||
| 232 | } | ||
| 233 | |||
| 234 | $call_map = self::getCallMap(); | ||
| 235 | |||
| 236 |         if (!isset($call_map[$call_map_key])) { | ||
| 237 | return null; | ||
| 238 | } | ||
| 239 | |||
| 240 | $call_map_functions = []; | ||
| 241 | $call_map_functions[] = $call_map[$call_map_key]; | ||
| 242 | |||
| 243 |         for ($i = 1; $i < 10; ++$i) { | ||
| 244 |             if (!isset($call_map[$call_map_key . '\'' . $i])) { | ||
| 245 | break; | ||
| 246 | } | ||
| 247 | |||
| 248 | $call_map_functions[] = $call_map[$call_map_key . '\'' . $i]; | ||
| 249 | } | ||
| 250 | |||
| 251 | $possible_callables = []; | ||
| 252 | |||
| 253 |         foreach ($call_map_functions as $call_map_function_args) { | ||
| 254 | $return_type_string = array_shift($call_map_function_args); | ||
| 255 | |||
| 256 |             if (!$return_type_string) { | ||
| 257 | $return_type = Type::getMixed(); | ||
| 258 |             } else { | ||
| 259 | $return_type = Type::parseString($return_type_string); | ||
| 260 | } | ||
| 261 | |||
| 262 | $function_params = []; | ||
| 263 | |||
| 264 | $arg_offset = 0; | ||
| 265 | |||
| 266 | /** @var string $arg_name - key type changed with above array_shift */ | ||
| 267 |             foreach ($call_map_function_args as $arg_name => $arg_type) { | ||
| 268 | $by_reference = false; | ||
| 269 | $optional = false; | ||
| 270 | $variadic = false; | ||
| 271 | |||
| 272 |                 if ($arg_name[0] === '&') { | ||
| 273 | $arg_name = substr($arg_name, 1); | ||
| 274 | $by_reference = true; | ||
| 275 | } | ||
| 276 | |||
| 277 |                 if (substr($arg_name, -1) === '=') { | ||
| 278 | $arg_name = substr($arg_name, 0, -1); | ||
| 279 | $optional = true; | ||
| 280 | } | ||
| 281 | |||
| 282 |                 if (substr($arg_name, 0, 3) === '...') { | ||
| 283 | $arg_name = substr($arg_name, 3); | ||
| 284 | $variadic = true; | ||
| 285 | } | ||
| 286 | |||
| 287 | $param_type = $arg_type | ||
| 288 | ? Type::parseString($arg_type) | ||
| 289 | : Type::getMixed(); | ||
| 290 | |||
| 291 | $out_type = null; | ||
| 292 | |||
| 293 |                 if (\strlen($arg_name) > 2 && $arg_name[0] === 'w' && $arg_name[1] === '_') { | ||
| 294 | $out_type = $param_type; | ||
| 295 | $param_type = Type::getMixed(); | ||
| 296 | } | ||
| 297 | |||
| 298 | $function_param = new FunctionLikeParameter( | ||
| 299 | $arg_name, | ||
| 300 | $by_reference, | ||
| 301 | $param_type, | ||
| 302 | null, | ||
| 303 | null, | ||
| 304 | $optional, | ||
| 305 | false, | ||
| 306 | $variadic | ||
| 307 | ); | ||
| 308 | |||
| 309 |                 if ($out_type) { | ||
| 310 | $function_param->out_type = $out_type; | ||
| 311 | } | ||
| 312 | |||
| 313 |                 if (isset(self::$taint_sink_map[$call_map_key][$arg_offset])) { | ||
| 314 | $function_param->sinks = self::$taint_sink_map[$call_map_key][$arg_offset]; | ||
| 315 | } | ||
| 316 | |||
| 317 | $function_param->signature_type = null; | ||
| 318 | |||
| 319 | $function_params[] = $function_param; | ||
| 320 | |||
| 321 | $arg_offset++; | ||
| 322 | } | ||
| 323 | |||
| 324 |             $possible_callables[] = new TCallable('callable', $function_params, $return_type); | ||
| 325 | } | ||
| 326 | |||
| 327 | self::$call_map_callables[$call_map_key] = $possible_callables; | ||
| 328 | |||
| 329 | return $possible_callables; | ||
| 330 | } | ||
| 331 | |||
| 332 | /** | ||
| 333 | * Gets the method/function call map | ||
| 334 | * | ||
| 335 | * @return array<string, array<int|string, string>> | ||
|  | |||
| 336 | * @psalm-suppress MixedInferredReturnType as the use of require buggers things up | ||
| 337 | * @psalm-suppress MixedTypeCoercion | ||
| 338 | * @psalm-suppress MixedReturnStatement | ||
| 339 | */ | ||
| 340 | public static function getCallMap() | ||
| 412 | |||
| 413 | /** | ||
| 414 | * @param string $key | ||
| 415 | * | ||
| 416 | * @return bool | ||
| 417 | */ | ||
| 418 | public static function inCallMap($key) | ||
| 422 | } | ||
| 423 | 
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.