Complex classes like Processor 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 Processor, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | class Processor { |
||
15 | |||
16 | /** |
||
17 | * Flag for unnamed default parameters used in Processor::setFunctionParams() to determine that |
||
18 | * a parameter should not have a named fallback. |
||
19 | * |
||
20 | * @since 0.4.13 |
||
21 | */ |
||
22 | const PARAM_UNNAMED = 1; |
||
23 | |||
24 | /** |
||
25 | * @var Param[] |
||
26 | */ |
||
27 | private $params; |
||
28 | |||
29 | /** |
||
30 | * Associative array containing parameter names (keys) and their user-provided data (values). |
||
31 | * This list is needed because additional parameter definitions can be added to the $parameters |
||
32 | * field during validation, so we can't determine in advance if a parameter is unknown. |
||
33 | * @var string[] |
||
34 | */ |
||
35 | private $rawParameters = []; |
||
36 | |||
37 | /** |
||
38 | * Array containing the names of the parameters to handle, ordered by priority. |
||
39 | * @var string[] |
||
40 | */ |
||
41 | private $paramsToHandle = []; |
||
42 | |||
43 | /** |
||
44 | * @var IParamDefinition[] |
||
45 | */ |
||
46 | private $paramDefinitions = []; |
||
47 | |||
48 | /** |
||
49 | * @var ProcessingError[] |
||
50 | */ |
||
51 | private $errors = []; |
||
52 | |||
53 | private $options; |
||
54 | |||
55 | 15 | public function __construct( Options $options ) { |
|
58 | |||
59 | /** |
||
60 | * Constructs and returns a Validator object based on the default options. |
||
61 | * |
||
62 | * @since 1.0 |
||
63 | * |
||
64 | * @return Processor |
||
65 | */ |
||
66 | 4 | public static function newDefault(): self { |
|
69 | |||
70 | /** |
||
71 | * Constructs and returns a Validator object based on the provided options. |
||
72 | * |
||
73 | * @since 1.0 |
||
74 | * |
||
75 | * @param Options $options |
||
76 | * |
||
77 | * @return Processor |
||
78 | */ |
||
79 | 11 | public static function newFromOptions( Options $options ): self { |
|
82 | |||
83 | /** |
||
84 | * Returns the options used by this Validator object. |
||
85 | * |
||
86 | * @since 1.0 |
||
87 | * |
||
88 | * @return Options |
||
89 | */ |
||
90 | 1 | public function getOptions(): Options { |
|
93 | |||
94 | /** |
||
95 | * Determines the names and values of all parameters. Also takes care of default parameters. |
||
96 | * After that the resulting parameter list is passed to Processor::setParameters |
||
97 | * |
||
98 | * @since 0.4 |
||
99 | * |
||
100 | * @param string[] $rawParams |
||
101 | * @param array $parameterDefinitions |
||
102 | * @param array $defaultParams array of strings or array of arrays to define which parameters can be used unnamed. |
||
103 | * The second value in array-form is reserved for flags. Currently, Processor::PARAM_UNNAMED determines that |
||
104 | * the parameter has no name which can be used to set it. Therefore all these parameters must be set before |
||
105 | * any named parameter. The effect is, that '=' within the string won't confuse the parameter anymore like |
||
106 | * it would happen with default parameters that still have a name as well. |
||
107 | */ |
||
108 | 1 | public function setFunctionParams( array $rawParams, array $parameterDefinitions, array $defaultParams = [] ) { |
|
109 | 1 | $parameters = []; |
|
110 | |||
111 | 1 | $nr = 0; |
|
112 | 1 | $defaultNr = 0; |
|
113 | 1 | $lastUnnamedDefaultNr = -1; |
|
114 | |||
115 | /* |
||
116 | * Find last parameter with self::PARAM_UNNAMED set. Tread all parameters in front as |
||
117 | * the flag were set for them as well to ensure that there can't be any unnamed params |
||
118 | * after the first named param. Wouldn't be possible to determine which unnamed value |
||
119 | * belongs to which parameter otherwise. |
||
120 | */ |
||
121 | 1 | for( $i = count( $defaultParams ) - 1; $i >= 0 ; $i-- ) { |
|
122 | $dflt = $defaultParams[$i]; |
||
123 | if( is_array( $dflt ) && !empty( $dflt[1] ) && ( $dflt[1] | self::PARAM_UNNAMED ) ) { |
||
124 | $lastUnnamedDefaultNr = $i; |
||
125 | break; |
||
126 | } |
||
127 | } |
||
128 | |||
129 | 1 | foreach ( $rawParams as $arg ) { |
|
130 | // Only take into account strings. If the value is not a string, |
||
131 | // it is not a raw parameter, and can not be parsed correctly in all cases. |
||
132 | 1 | if ( is_string( $arg ) ) { |
|
133 | 1 | $parts = explode( '=', $arg, ( $nr <= $lastUnnamedDefaultNr ? 1 : 2 ) ); |
|
134 | |||
135 | // If there is only one part, no parameter name is provided, so try default parameter assignment. |
||
136 | // Default parameters having self::PARAM_UNNAMED set for having no name alias go here in any case. |
||
137 | 1 | if ( count( $parts ) == 1 ) { |
|
138 | // Default parameter assignment is only possible when there are default parameters! |
||
139 | if ( count( $defaultParams ) > 0 ) { |
||
140 | $defaultParam = array_shift( $defaultParams ); |
||
141 | if( is_array( $defaultParam ) ) { |
||
142 | $defaultParam = $defaultParam[0]; |
||
143 | } |
||
144 | $defaultParam = strtolower( $defaultParam ); |
||
145 | |||
146 | $parameters[$defaultParam] = [ |
||
147 | 'original-value' => trim( $parts[0] ), |
||
148 | 'default' => $defaultNr, |
||
149 | 'position' => $nr |
||
150 | ]; |
||
151 | $defaultNr++; |
||
152 | } |
||
153 | else { |
||
|
|||
154 | // It might be nice to have some sort of warning or error here, as the value is simply ignored. |
||
155 | } |
||
156 | } else { |
||
157 | 1 | $paramName = trim( strtolower( $parts[0] ) ); |
|
158 | |||
159 | 1 | $parameters[$paramName] = [ |
|
160 | 1 | 'original-value' => trim( $parts[1] ), |
|
161 | 'default' => false, |
||
162 | 1 | 'position' => $nr |
|
163 | ]; |
||
164 | |||
165 | // Let's not be evil, and remove the used parameter name from the default parameter list. |
||
166 | // This code is basically a remove array element by value algorithm. |
||
167 | 1 | $newDefaults = []; |
|
168 | |||
169 | 1 | foreach( $defaultParams as $defaultParam ) { |
|
170 | if ( $defaultParam != $paramName ) $newDefaults[] = $defaultParam; |
||
171 | } |
||
172 | |||
173 | 1 | $defaultParams = $newDefaults; |
|
174 | } |
||
175 | } |
||
176 | |||
177 | 1 | $nr++; |
|
178 | } |
||
179 | |||
180 | 1 | $this->setParameters( $parameters, $parameterDefinitions ); |
|
181 | 1 | } |
|
182 | |||
183 | /** |
||
184 | * Loops through a list of provided parameters, resolves aliasing and stores errors |
||
185 | * for unknown parameters and optionally for parameter overriding. |
||
186 | * |
||
187 | * @param array $parameters Parameter name as key, parameter value as value |
||
188 | * @param IParamDefinition[] $paramDefinitions List of parameter definitions. Either ParamDefinition objects or equivalent arrays. |
||
189 | */ |
||
190 | 13 | public function setParameters( array $parameters, array $paramDefinitions ) { |
|
208 | |||
209 | /** |
||
210 | * @param string $message |
||
211 | * @param mixed $tags string or array |
||
212 | * @param integer $severity |
||
213 | */ |
||
214 | 4 | private function registerNewError( $message, $tags = [], $severity = ProcessingError::SEVERITY_NORMAL ) { |
|
224 | |||
225 | 5 | private function registerError( ProcessingError $error ) { |
|
230 | |||
231 | /** |
||
232 | * Validates and formats all the parameters (but aborts when a fatal error occurs). |
||
233 | * |
||
234 | * @since 0.4 |
||
235 | * @deprecated since 1.0, use processParameters |
||
236 | */ |
||
237 | public function validateParameters() { |
||
240 | |||
241 | 9 | public function processParameters(): ProcessingResult { |
|
257 | |||
258 | 9 | private function newProcessingResult(): ProcessingResult { |
|
291 | |||
292 | /** |
||
293 | * Does the actual parameter processing. |
||
294 | */ |
||
295 | 9 | private function doParamProcessing() { |
|
304 | |||
305 | 8 | private function processOneParam() { |
|
306 | 8 | $paramName = array_shift( $this->paramsToHandle ); |
|
307 | 8 | $definition = $this->paramDefinitions[$paramName]; |
|
308 | |||
309 | 8 | $param = new Param( $definition ); |
|
310 | |||
311 | 8 | $setUserValue = $this->attemptToSetUserValue( $param ); |
|
312 | |||
313 | // If the parameter is required but not provided, register a fatal error and stop processing. |
||
314 | 8 | if ( !$setUserValue && $param->isRequired() ) { |
|
315 | 2 | $this->registerNewError( |
|
316 | 2 | "Required parameter '$paramName' is missing", // FIXME: i18n validator_error_required_missing |
|
317 | 2 | [ $paramName, 'missing' ], |
|
318 | 2 | ProcessingError::SEVERITY_FATAL |
|
319 | ); |
||
320 | 2 | return; |
|
321 | } |
||
322 | |||
323 | 6 | $this->params[$param->getName()] = $param; |
|
324 | |||
325 | 6 | $initialSet = $this->paramDefinitions; |
|
326 | |||
327 | 6 | $param->process( $this->paramDefinitions, $this->params, $this->options ); |
|
328 | |||
329 | 6 | foreach ( $param->getErrors() as $error ) { |
|
330 | 3 | $this->registerError( $error ); |
|
331 | } |
||
332 | |||
333 | 6 | if ( $param->hasFatalError() ) { |
|
334 | 1 | return; |
|
335 | } |
||
336 | |||
337 | 5 | $this->getParamsToProcess( $initialSet, $this->paramDefinitions ); |
|
338 | 5 | } |
|
339 | |||
340 | /** |
||
341 | * Gets an ordered list of parameters to process. |
||
342 | * @throws \UnexpectedValueException |
||
343 | */ |
||
344 | 9 | private function getParamsToProcess( array $initialParamSet, array $resultingParamSet ) { |
|
362 | |||
363 | /** |
||
364 | * @param IParamDefinition[] $paramDefinitions |
||
365 | * @param string[] $paramsToHandle |
||
366 | * |
||
367 | * @return array |
||
368 | */ |
||
369 | 9 | private function getParameterNamesInEvaluationOrder( array $paramDefinitions, array $paramsToHandle ): array { |
|
397 | |||
398 | /** |
||
399 | * Tries to find a matching user provided value and, when found, assigns it |
||
400 | * to the parameter, and removes it from the raw values. Returns a boolean |
||
401 | * indicating if there was any user value set or not. |
||
402 | */ |
||
403 | 8 | private function attemptToSetUserValue( Param $param ): bool { |
|
421 | |||
422 | /** |
||
423 | * Returns the parameters. |
||
424 | * |
||
425 | * @since 0.4 |
||
426 | * @deprecated since 1.0 |
||
427 | * |
||
428 | * @return IParam[] |
||
429 | */ |
||
430 | public function getParameters(): array { |
||
433 | |||
434 | /** |
||
435 | * Returns a single parameter. |
||
436 | * |
||
437 | * @since 0.4 |
||
438 | * @deprecated since 1.0 |
||
439 | * |
||
440 | * @param string $parameterName The name of the parameter to return |
||
441 | * |
||
442 | * @return IParam |
||
443 | */ |
||
444 | public function getParameter( string $parameterName ): IParam { |
||
447 | |||
448 | /** |
||
449 | * Returns an associative array with the parameter names as key and their |
||
450 | * corresponding values as value. |
||
451 | */ |
||
452 | public function getParameterValues(): array { |
||
461 | |||
462 | /** |
||
463 | * @return ProcessingError[] |
||
464 | */ |
||
465 | 9 | public function getErrors(): array { |
|
468 | |||
469 | /** |
||
470 | * @return string[] |
||
471 | */ |
||
472 | public function getErrorMessages(): array { |
||
481 | |||
482 | /** |
||
483 | * Returns if there where any errors during validation. |
||
484 | */ |
||
485 | public function hasErrors(): bool { |
||
488 | |||
489 | /** |
||
490 | * Returns false when there are no fatal errors or an ProcessingError when one is found. |
||
491 | * |
||
492 | * @return ProcessingError|boolean false |
||
493 | */ |
||
494 | 9 | public function hasFatalError() { |
|
503 | |||
504 | } |
||
505 |
This check looks for the
else
branches ofif
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
else
branches can be removed.could be turned into
This is much more concise to read.