Complex classes like PathValidator 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 PathValidator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class PathValidator |
||
19 | { |
||
20 | /** |
||
21 | * Crypto engine. |
||
22 | * |
||
23 | * @var Crypto $_crypto |
||
24 | */ |
||
25 | protected $_crypto; |
||
26 | |||
27 | /** |
||
28 | * Path validation configuration. |
||
29 | * |
||
30 | * @var PathValidationConfig $_config |
||
31 | */ |
||
32 | protected $_config; |
||
33 | |||
34 | /** |
||
35 | * Certification path. |
||
36 | * |
||
37 | * @var Certificate[] $_certificates |
||
38 | */ |
||
39 | protected $_certificates; |
||
40 | |||
41 | /** |
||
42 | * Certification path trust anchor. |
||
43 | * |
||
44 | * @var Certificate $_trustAnchor |
||
45 | */ |
||
46 | protected $_trustAnchor; |
||
47 | |||
48 | /** |
||
49 | * Constructor |
||
50 | * |
||
51 | * @param Crypto $crypto |
||
52 | * @param PathValidationConfig $config |
||
53 | * @param Certificate ...$certificates |
||
54 | */ |
||
55 | 46 | public function __construct(Crypto $crypto, PathValidationConfig $config, |
|
56 | Certificate ...$certificates) { |
||
57 | 46 | if (!count($certificates)) { |
|
58 | 1 | throw new \LogicException("No certificates."); |
|
59 | } |
||
60 | 45 | $this->_crypto = $crypto; |
|
61 | 45 | $this->_config = $config; |
|
62 | 45 | $this->_certificates = $certificates; |
|
63 | // if trust anchor is explicitly given in configuration |
||
64 | 45 | if ($config->hasTrustAnchor()) { |
|
65 | 1 | $this->_trustAnchor = $config->trustAnchor(); |
|
66 | 1 | } else { |
|
67 | 44 | $this->_trustAnchor = $certificates[0]; |
|
68 | } |
||
69 | 45 | } |
|
70 | |||
71 | /** |
||
72 | * Validate certification path. |
||
73 | * |
||
74 | * @throws PathValidationException |
||
75 | * @return PathValidationResult |
||
76 | */ |
||
77 | 45 | public function validate() { |
|
78 | 45 | $n = count($this->_certificates); |
|
79 | 45 | $state = ValidatorState::initialize($this->_config, $this->_trustAnchor, |
|
80 | 45 | $n); |
|
81 | 45 | for ($i = 0; $i < $n; ++$i) { |
|
82 | 44 | $state = $state->withIndex($i + 1); |
|
83 | 44 | $cert = $this->_certificates[$i]; |
|
84 | // process certificate (section 6.1.3.) |
||
85 | 44 | $state = $this->_processCertificate($state, $cert); |
|
86 | 43 | if (!$state->isFinal()) { |
|
87 | // prepare next certificate (section 6.1.4.) |
||
88 | 43 | $state = $this->_prepareNext($state, $cert); |
|
89 | 39 | } |
|
90 | 39 | } |
|
91 | 31 | if (!isset($cert)) { |
|
92 | 1 | throw new \LogicException("No certificates."); |
|
93 | } |
||
94 | // wrap-up (section 6.1.5.) |
||
95 | 30 | $state = $this->_wrapUp($state, $cert); |
|
96 | // return outputs |
||
97 | 28 | return $state->getResult($this->_certificates); |
|
98 | } |
||
99 | |||
100 | /** |
||
101 | * Apply basic certificate processing according to RFC 5280 section 6.1.3. |
||
102 | * |
||
103 | * @link https://tools.ietf.org/html/rfc5280#section-6.1.3 |
||
104 | * @param ValidatorState $state |
||
105 | * @param Certificate $cert |
||
106 | * @throws PathValidationException |
||
107 | * @return ValidatorState |
||
108 | */ |
||
109 | 44 | private function _processCertificate(ValidatorState $state, |
|
110 | Certificate $cert) { |
||
111 | // (a.1) verify signature |
||
112 | 44 | $this->_verifySignature($state, $cert); |
|
113 | // (a.2) check validity period |
||
114 | 44 | $this->_checkValidity($cert); |
|
115 | // (a.3) check that certificate is not revoked |
||
116 | 43 | $this->_checkRevocation($cert); |
|
|
|||
117 | // (a.4) check issuer |
||
118 | 43 | $this->_checkIssuer($state, $cert); |
|
119 | // (b)(c) if certificate is self-issued and it is not |
||
120 | // the final certificate in the path, skip this step |
||
121 | 43 | if (!($cert->isSelfIssued() && !$state->isFinal())) { |
|
122 | // (b) check permitted subtrees |
||
123 | 35 | $this->_checkPermittedSubtrees($state, $cert); |
|
124 | // (c) check excluded subtrees |
||
125 | 35 | $this->_checkExcludedSubtrees($state, $cert); |
|
126 | 35 | } |
|
127 | 43 | $extensions = $cert->tbsCertificate()->extensions(); |
|
128 | 43 | if ($extensions->hasCertificatePolicies()) { |
|
129 | // (d) process policy information |
||
130 | 26 | if ($state->hasValidPolicyTree()) { |
|
131 | 14 | $state = $state->validPolicyTree()->processPolicies($state, |
|
132 | 14 | $cert); |
|
133 | 14 | } |
|
134 | 26 | } else { |
|
135 | // (e) certificate policies extension not present, |
||
136 | // set the valid_policy_tree to NULL |
||
137 | 31 | $state = $state->withoutValidPolicyTree(); |
|
138 | } |
||
139 | // (f) check that explicit_policy > 0 or valid_policy_tree is set |
||
140 | 43 | if (!($state->explicitPolicy() > 0 || $state->hasValidPolicyTree())) { |
|
141 | 3 | throw new PathValidationException("No valid policies."); |
|
142 | } |
||
143 | 43 | return $state; |
|
144 | } |
||
145 | |||
146 | /** |
||
147 | * Apply preparation for the certificate i+1 according to rfc5280 section |
||
148 | * 6.1.4. |
||
149 | * |
||
150 | * @link https://tools.ietf.org/html/rfc5280#section-6.1.4 |
||
151 | * @param ValidatorState $state |
||
152 | * @param Certificate $cert |
||
153 | * @return ValidatorState |
||
154 | */ |
||
155 | 43 | private function _prepareNext(ValidatorState $state, Certificate $cert) { |
|
156 | // (a)(b) if policy mappings extension is present |
||
157 | 43 | $state = $this->_preparePolicyMappings($state, $cert); |
|
158 | // (c) assign working_issuer_name |
||
159 | 42 | $state = $state->withWorkingIssuerName( |
|
160 | 42 | $cert->tbsCertificate() |
|
161 | 42 | ->subject()); |
|
162 | // (d)(e)(f) |
||
163 | 42 | $state = $this->_setPublicKeyState($state, $cert); |
|
164 | // (g) if name constraints extension is present |
||
165 | 42 | $state = $this->_prepareNameConstraints($state, $cert); |
|
166 | // (h) if certificate is not self-issued |
||
167 | 42 | if (!$cert->isSelfIssued()) { |
|
168 | 21 | $state = $this->_prepareNonSelfIssued($state); |
|
169 | 21 | } |
|
170 | // (i) if policy constraints extension is present |
||
171 | 42 | $state = $this->_preparePolicyConstraints($state, $cert); |
|
172 | // (j) if inhibit any policy extension is present |
||
173 | 42 | $state = $this->_prepareInhibitAnyPolicy($state, $cert); |
|
174 | // (k) check basic constraints |
||
175 | 42 | $this->_processBasicContraints($cert); |
|
176 | // (l) verify max_path_length |
||
177 | 40 | $state = $this->_verifyMaxPathLength($state, $cert); |
|
178 | // (m) check pathLenContraint |
||
179 | 40 | $state = $this->_processPathLengthContraint($state, $cert); |
|
180 | // (n) check key usage |
||
181 | 40 | $this->_checkKeyUsage($cert); |
|
182 | // (o) process relevant extensions |
||
183 | 39 | $state = $this->_processExtensions($state, $cert); |
|
184 | 39 | return $state; |
|
185 | } |
||
186 | |||
187 | /** |
||
188 | * Apply wrap-up procedure according to RFC 5280 section 6.1.5. |
||
189 | * |
||
190 | * @link https://tools.ietf.org/html/rfc5280#section-6.1.5 |
||
191 | * @param ValidatorState $state |
||
192 | * @param Certificate $cert |
||
193 | * @throws PathValidationException |
||
194 | */ |
||
195 | 30 | private function _wrapUp(ValidatorState $state, Certificate $cert) { |
|
196 | 30 | $tbs_cert = $cert->tbsCertificate(); |
|
197 | 30 | $extensions = $tbs_cert->extensions(); |
|
198 | // (a) |
||
199 | 30 | if ($state->explicitPolicy() > 0) { |
|
200 | 26 | $state = $state->withExplicitPolicy($state->explicitPolicy() - 1); |
|
201 | 26 | } |
|
202 | // (b) |
||
203 | 30 | if ($extensions->hasPolicyConstraints()) { |
|
204 | 13 | $ext = $extensions->policyConstraints(); |
|
205 | 13 | if ($ext->hasRequireExplicitPolicy() && |
|
206 | 13 | $ext->requireExplicitPolicy() == 0) { |
|
207 | 1 | $state = $state->withExplicitPolicy(0); |
|
208 | 1 | } |
|
209 | 13 | } |
|
210 | // (c)(d)(e) |
||
211 | 30 | $state = $this->_setPublicKeyState($state, $cert); |
|
212 | // (f) process relevant extensions |
||
213 | 30 | $state = $this->_processExtensions($state, $cert); |
|
214 | // (g) intersection of valid_policy_tree and the initial-policy-set |
||
215 | 30 | $state = $this->_calculatePolicyIntersection($state); |
|
216 | // check that explicit_policy > 0 or valid_policy_tree is set |
||
217 | 30 | if (!($state->explicitPolicy() > 0 || $state->hasValidPolicyTree())) { |
|
218 | 2 | throw new PathValidationException("No valid policies."); |
|
219 | } |
||
220 | // path validation succeeded |
||
221 | 28 | return $state; |
|
222 | } |
||
223 | |||
224 | /** |
||
225 | * Update working_public_key, working_public_key_parameters and |
||
226 | * working_public_key_algorithm state variables from certificate. |
||
227 | * |
||
228 | * @param ValidatorState $state |
||
229 | * @param Certificate $cert |
||
230 | * @return ValidatorState |
||
231 | */ |
||
232 | 42 | private function _setPublicKeyState(ValidatorState $state, Certificate $cert) { |
|
253 | |||
254 | /** |
||
255 | * Verify certificate signature. |
||
256 | * |
||
257 | * @param ValidatorState $state |
||
258 | * @param Certificate $cert |
||
259 | * @throws PathValidationException |
||
260 | */ |
||
261 | 44 | private function _verifySignature(ValidatorState $state, Certificate $cert) { |
|
262 | try { |
||
263 | 44 | $valid = $cert->verify($this->_crypto, $state->workingPublicKey()); |
|
264 | 44 | } catch (\RuntimeException $e) { |
|
265 | 1 | throw new PathValidationException( |
|
266 | 1 | "Failed to verify signature: " . $e->getMessage(), null, $e); |
|
267 | } |
||
268 | 44 | if (!$valid) { |
|
269 | 2 | throw new PathValidationException( |
|
270 | 2 | "Certificate signature doesn't match."); |
|
271 | } |
||
272 | 44 | } |
|
273 | |||
274 | /** |
||
275 | * Check certificate validity. |
||
276 | * |
||
277 | * @param Certificate $cert |
||
278 | * @throws PathValidationException |
||
279 | */ |
||
280 | 44 | private function _checkValidity(Certificate $cert) { |
|
294 | |||
295 | /** |
||
296 | * Check certificate revocation. |
||
297 | * |
||
298 | * @param Certificate $cert |
||
299 | */ |
||
300 | 43 | private function _checkRevocation(Certificate $cert) { |
|
303 | |||
304 | /** |
||
305 | * Check certificate issuer. |
||
306 | * |
||
307 | * @param ValidatorState $state |
||
308 | * @param Certificate $cert |
||
309 | * @throws PathValidationException |
||
310 | */ |
||
311 | 43 | private function _checkIssuer(ValidatorState $state, Certificate $cert) { |
|
318 | |||
319 | /** |
||
320 | * |
||
321 | * @param ValidatorState $state |
||
322 | * @param Certificate $cert |
||
323 | */ |
||
324 | 35 | private function _checkPermittedSubtrees(ValidatorState $state, |
|
329 | |||
330 | /** |
||
331 | * |
||
332 | * @param ValidatorState $state |
||
333 | * @param Certificate $cert |
||
334 | */ |
||
335 | 35 | private function _checkExcludedSubtrees(ValidatorState $state, |
|
340 | |||
341 | /** |
||
342 | * Apply policy mappings handling for the preparation step. |
||
343 | * |
||
344 | * @param ValidatorState $state |
||
345 | * @param Certificate $cert |
||
346 | * @throws PathValidationException |
||
347 | * @return ValidatorState |
||
348 | */ |
||
349 | 43 | private function _preparePolicyMappings(ValidatorState $state, |
|
365 | |||
366 | /** |
||
367 | * Apply name constraints handling for the preparation step. |
||
368 | * |
||
369 | * @param ValidatorState $state |
||
370 | * @param Certificate $cert |
||
371 | * @return ValidatorState |
||
372 | */ |
||
373 | 42 | private function _prepareNameConstraints(ValidatorState $state, |
|
381 | |||
382 | /** |
||
383 | * Apply preparation for a non-self-signed certificate. |
||
384 | * |
||
385 | * @param ValidatorState $state |
||
386 | * @return ValidatorState |
||
387 | */ |
||
388 | 21 | private function _prepareNonSelfIssued(ValidatorState $state) { |
|
404 | |||
405 | /** |
||
406 | * Apply policy constraints handling for the preparation step. |
||
407 | * |
||
408 | * @param ValidatorState $state |
||
409 | * @param Certificate $cert |
||
410 | * @return ValidatorState |
||
411 | */ |
||
412 | 42 | private function _preparePolicyConstraints(ValidatorState $state, |
|
431 | |||
432 | /** |
||
433 | * Apply inhibit any-policy handling for the preparation step. |
||
434 | * |
||
435 | * @param ValidatorState $state |
||
436 | * @param Certificate $cert |
||
437 | * @return ValidatorState |
||
438 | */ |
||
439 | 42 | private function _prepareInhibitAnyPolicy(ValidatorState $state, |
|
450 | |||
451 | /** |
||
452 | * Verify maximum certification path length for the preparation step. |
||
453 | * |
||
454 | * @param ValidatorState $state |
||
455 | * @param Certificate $cert |
||
456 | * @throws PathValidationException |
||
457 | * @return ValidatorState |
||
458 | */ |
||
459 | 40 | private function _verifyMaxPathLength(ValidatorState $state, |
|
470 | |||
471 | /** |
||
472 | * Check key usage extension for the preparation step. |
||
473 | * |
||
474 | * @param Certificate $cert |
||
475 | * @throws PathValidationException |
||
476 | */ |
||
477 | 40 | private function _checkKeyUsage(Certificate $cert) { |
|
486 | |||
487 | /** |
||
488 | * |
||
489 | * @param ValidatorState $state |
||
490 | * @param Certificate $cert |
||
491 | * @return ValidatorState |
||
492 | */ |
||
493 | 1 | private function _processNameConstraints(ValidatorState $state, |
|
498 | |||
499 | /** |
||
500 | * Process basic constraints extension. |
||
501 | * |
||
502 | * @param Certificate $cert |
||
503 | * @throws PathValidationException |
||
504 | */ |
||
505 | 42 | private function _processBasicContraints(Certificate $cert) { |
|
519 | |||
520 | /** |
||
521 | * Process pathLenConstraint. |
||
522 | * |
||
523 | * @param ValidatorState $state |
||
524 | * @param Certificate $cert |
||
525 | * @return ValidatorState |
||
526 | */ |
||
527 | 40 | private function _processPathLengthContraint(ValidatorState $state, |
|
540 | |||
541 | /** |
||
542 | * |
||
543 | * @param ValidatorState $state |
||
544 | * @param Certificate $cert |
||
545 | * @return ValidatorState |
||
546 | */ |
||
547 | 39 | private function _processExtensions(ValidatorState $state, Certificate $cert) { |
|
551 | |||
552 | /** |
||
553 | * |
||
554 | * @param ValidatorState $state |
||
555 | * @return ValidatorState |
||
556 | */ |
||
557 | 30 | private function _calculatePolicyIntersection(ValidatorState $state) { |
|
576 | } |
||
577 |
PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.
Let’s take a look at an example:
If we look at the
getEmail()
method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:On the hand, if we look at the
setEmail()
, this method _has_ side-effects. In the following case, we could not remove the method call: