Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like WorkflowDescriptorContext 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 WorkflowDescriptorContext, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
26 | class WorkflowDescriptorContext implements Context, SnippetAcceptingContext |
||
27 | { |
||
28 | /** |
||
29 | * |
||
30 | * @var string |
||
31 | */ |
||
32 | protected $workflowDescriptorNamespace = 'OldTown\Workflow\Loader'; |
||
33 | |||
34 | /** |
||
35 | * Последний созданный дескриптор |
||
36 | * |
||
37 | * @var AbstractDescriptor |
||
38 | */ |
||
39 | protected $lastCreatedDescriptor; |
||
40 | |||
41 | /** |
||
42 | * @var |
||
43 | */ |
||
44 | protected $currentScenario; |
||
45 | |||
46 | /** |
||
47 | * @Given Create descriptor :nameDescriptor |
||
48 | * |
||
49 | * @param $nameDescriptor |
||
50 | * |
||
51 | * @return AbstractDescriptor |
||
52 | * |
||
53 | * @throws \RuntimeException |
||
54 | */ |
||
55 | public function createDescriptor($nameDescriptor) |
||
63 | |||
64 | |||
65 | /** |
||
66 | * @Given Create descriptor :nameDescriptor based on xml: |
||
67 | * |
||
68 | * @param string $nameDescriptor |
||
69 | * @param PyStringNode $xml |
||
70 | * |
||
71 | * @return AbstractDescriptor |
||
72 | * @throws \RuntimeException |
||
73 | */ |
||
74 | public function createDescriptorByNameBasedOnXml($nameDescriptor, PyStringNode $xml) |
||
100 | |||
101 | |||
102 | /** |
||
103 | * @When Create descriptor :nameDescriptor based on xml. I expect exception with the text :expectedExceptionMessage. Xml source: |
||
104 | * |
||
105 | * @param string $nameDescriptor |
||
106 | * @param string $expectedExceptionMessage |
||
107 | * @param PyStringNode $xml |
||
108 | * |
||
109 | * @return AbstractDescriptor |
||
110 | */ |
||
111 | public function createDescriptorByNameBasedOnXmlAndExpectToGetAnExceptionMessage($nameDescriptor, $expectedExceptionMessage, PyStringNode $xml) |
||
122 | |||
123 | /** |
||
124 | * @Then Call a method descriptor :nameMethod, I get the value of :expectedResult |
||
125 | * |
||
126 | * @param $nameMethod |
||
127 | * @param $expectedResult |
||
128 | * |
||
129 | * @throws \RuntimeException |
||
130 | */ |
||
131 | public function callAMethodDescriptorIGetTheValueOf($nameMethod, $expectedResult) |
||
156 | |||
157 | |||
158 | /** |
||
159 | * @Transform /^\(.+?\).+?$/ |
||
160 | * |
||
161 | * @param $expectedResult |
||
162 | * |
||
163 | * @return mixed |
||
164 | * @throws \RuntimeException |
||
165 | */ |
||
166 | public function intelligentTransformArgument($expectedResult) |
||
167 | { |
||
168 | $outputArray = []; |
||
169 | preg_match_all('/^\((.+?)\)(.+?)$/', $expectedResult, $outputArray); |
||
170 | |||
171 | |||
172 | if (3 !== count($outputArray)) { |
||
173 | return $expectedResult; |
||
174 | } |
||
175 | |||
176 | View Code Duplication | if (!(is_array($outputArray[1]) && 1 === count($outputArray[1]))) { |
|
177 | return $expectedResult; |
||
178 | } |
||
179 | |||
180 | |||
181 | View Code Duplication | if (!(is_array($outputArray[2]) && 1 === count($outputArray[2]))) { |
|
182 | return $expectedResult; |
||
183 | } |
||
184 | |||
185 | $originalType = $outputArray[1][0]; |
||
186 | $type = strtolower($originalType); |
||
187 | |||
188 | $value = $outputArray[2][0]; |
||
189 | |||
190 | $result = $value; |
||
191 | switch ($type) { |
||
192 | case 'boolean': |
||
193 | case 'bool': { |
||
194 | $prepareValue = trim($result); |
||
195 | $prepareValue = strtolower($prepareValue); |
||
196 | |||
197 | $falseStrings = [ |
||
198 | '' => '', |
||
199 | 'false' => 'false', |
||
200 | '0' => '0', |
||
201 | ]; |
||
202 | |||
203 | $result = !array_key_exists($prepareValue, $falseStrings); |
||
204 | |||
205 | break; |
||
206 | } |
||
207 | case 'null': { |
||
208 | $result = '(null)null' === $expectedResult ? null : $expectedResult; |
||
209 | break; |
||
210 | } |
||
211 | case '\\domdocument': |
||
212 | case 'domdocument': { |
||
213 | if ('(DOMDocument)domDocument' === $expectedResult) { |
||
214 | $result = new \DOMDocument(); |
||
215 | } |
||
216 | |||
217 | break; |
||
218 | } |
||
219 | default: { |
||
220 | |||
221 | } |
||
222 | } |
||
223 | |||
224 | return $result; |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * @When Call a method descriptor :nameMethod. The arguments of the method: |
||
229 | * |
||
230 | * @param $nameMethod |
||
231 | * @param TableNode $table |
||
232 | * |
||
233 | * @throws RuntimeException |
||
234 | */ |
||
235 | public function callAMethodDescriptorTheArgumentsOfTheMethod($nameMethod, TableNode $table) |
||
264 | |||
265 | |||
266 | /** |
||
267 | * @When Call a method descriptor :nameMethod. I expect to get an exception message :expectedExceptionMessage. The arguments of the method: |
||
268 | * |
||
269 | * @param $nameMethod |
||
270 | * @param $expectedExceptionMessage |
||
271 | * @param TableNode $table |
||
272 | * |
||
273 | */ |
||
274 | public function callAMethodDescriptorIExpectToGetAnExceptionMessageTheArgumentsOfTheMethod($nameMethod, $expectedExceptionMessage, TableNode $table) |
||
306 | |||
307 | /** |
||
308 | * @Then Call a method descriptor :nameMethod. I expect to get an exception :expectedException |
||
309 | * |
||
310 | * @param string $nameMethod |
||
311 | * @param string $expectedException |
||
312 | */ |
||
313 | View Code Duplication | public function callAMethodDescriptorIExpectToGetAnException($nameMethod, $expectedException) |
|
332 | |||
333 | /** |
||
334 | * @Then Call a method descriptor :nameMethod. I expect to get an exception message :expectedExceptionMessage |
||
335 | * |
||
336 | * @param $nameMethod |
||
337 | * @param $expectedExceptionMessage |
||
338 | */ |
||
339 | View Code Duplication | public function callAMethodDescriptorIExpectToGetAnExceptionMessage($nameMethod, $expectedExceptionMessage) |
|
358 | |||
359 | |||
360 | |||
361 | |||
362 | /** |
||
363 | * @Then Call a method descriptor :nameMethod, I get the value of :expectedResult. The arguments of the method: |
||
364 | * |
||
365 | * @param string $nameMethod |
||
366 | * @param string $expectedResult |
||
367 | * @param TableNode $table |
||
368 | * |
||
369 | * @throws \RuntimeException |
||
370 | * |
||
371 | */ |
||
372 | public function callAMethodDescriptorIGetTheValueOfTheArgumentsOfTheMethod($nameMethod, $expectedResult, TableNode $table) |
||
410 | |||
411 | /** |
||
412 | * @When Call a method descriptor :nameMethod |
||
413 | * |
||
414 | * @param $nameMethod |
||
415 | * |
||
416 | * @throws RuntimeException |
||
417 | */ |
||
418 | public function callAMethodDescriptor($nameMethod) |
||
434 | |||
435 | |||
436 | /** |
||
437 | * @Then I save to descriptor xml. Not DomDocument. Compare with xml: |
||
438 | * |
||
439 | * @param PyStringNode $expectedXmlNode |
||
440 | * |
||
441 | * @throws \RuntimeException |
||
442 | * |
||
443 | */ |
||
444 | public function iSaveToDescriptorXmlNotDomDocumentCompareWithXml(PyStringNode $expectedXmlNode) |
||
471 | |||
472 | |||
473 | /** |
||
474 | * @Then I save to descriptor xml. Compare with xml: |
||
475 | * |
||
476 | * @param PyStringNode $expectedXmlNode |
||
477 | * |
||
478 | * @throws \RuntimeException |
||
479 | * |
||
480 | */ |
||
481 | public function iSaveToDescriptorXmlCompareWithXml(PyStringNode $expectedXmlNode) |
||
513 | |||
514 | /** |
||
515 | * @Then I save to descriptor xml. I expect to get an exception :expectedException |
||
516 | * |
||
517 | * @param string $expectedException |
||
518 | */ |
||
519 | View Code Duplication | public function iSaveToDescriptorXmlIExpectToGetAnException($expectedException) |
|
541 | |||
542 | /** |
||
543 | * @Then I save to descriptor xml. I expect to get an exception message :expectedException |
||
544 | * |
||
545 | * @param $expectedExceptionMessage |
||
546 | * |
||
547 | */ |
||
548 | View Code Duplication | public function iSaveToDescriptorXmlIExpectToGetAnExceptionMessage($expectedExceptionMessage) |
|
570 | |||
571 | |||
572 | |||
573 | /** |
||
574 | * @Then I validated descriptor. I expect to get an exception message :expectedExceptionMessage |
||
575 | * |
||
576 | * @param $expectedExceptionMessage |
||
577 | * |
||
578 | */ |
||
579 | View Code Duplication | public function iValidatedDescriptorIExpectToGetAnExceptionMessage($expectedExceptionMessage) |
|
596 | |||
597 | |||
598 | /** |
||
599 | * @Then I validated descriptor. I expect to get an exception :expectedException |
||
600 | * |
||
601 | * @param string $expectedException |
||
602 | */ |
||
603 | View Code Duplication | public function iValidatedDescriptorIExpectToGetAnException($expectedException) |
|
620 | |||
621 | |||
622 | /** |
||
623 | * @BeforeScenario @workflowDescriptor |
||
624 | */ |
||
625 | public function beforeScenarioWithTagWorkflowDescriptor() |
||
629 | |||
630 | /** |
||
631 | * @AfterStep |
||
632 | * |
||
633 | * @param AfterStepScope $scope |
||
634 | */ |
||
635 | public function afterStepWithTagWorkflowDescriptor(AfterStepScope $scope) |
||
647 | |||
648 | /** |
||
649 | * Возвращает последний созданный дескриптор |
||
650 | * |
||
651 | * @return AbstractDescriptor |
||
652 | * |
||
653 | * @throws \RuntimeException |
||
654 | */ |
||
655 | protected function getLastCreatedDescriptor() |
||
664 | |||
665 | |||
666 | /** |
||
667 | * @BeforeScenario |
||
668 | * |
||
669 | * @param BeforeScenarioScope $scope |
||
670 | */ |
||
671 | public function beforeScenario(BeforeScenarioScope $scope) |
||
675 | |||
676 | |||
677 | /** |
||
678 | * @AfterScenario |
||
679 | */ |
||
680 | public function afterScenario() |
||
684 | |||
685 | /** |
||
686 | * Наймспейс в котором находятся дескрипторы Workflow |
||
687 | * |
||
688 | * @return string |
||
689 | */ |
||
690 | public function getWorkflowDescriptorNamespace() |
||
694 | |||
695 | /** |
||
696 | * Фабрика по созданию дескрипторов |
||
697 | * |
||
698 | * @param string $name |
||
699 | * |
||
700 | * @param DOMElement $element |
||
701 | * |
||
702 | * @return AbstractDescriptor |
||
703 | * @throws RuntimeException |
||
704 | */ |
||
705 | protected function factoryDescriptor($name, DOMElement $element = null) |
||
731 | |||
732 | /** |
||
733 | * @Given Get the descriptor using the method of :methodName |
||
734 | * |
||
735 | * @param $methodName |
||
736 | * |
||
737 | * @return AbstractDescriptor |
||
738 | * @throws RuntimeException |
||
739 | */ |
||
740 | public function getTheDescriptorUsingTheMethodOf($methodName) |
||
768 | |||
769 | |||
770 | /** |
||
771 | * @When Get the descriptor using the method of :methodName. The arguments of the method: |
||
772 | * |
||
773 | * @param $methodName |
||
774 | * @param TableNode $table |
||
775 | * |
||
776 | * @return mixed |
||
777 | * @throws \RuntimeException |
||
778 | */ |
||
779 | public function getTheDescriptorUsingTheMethodOfTheArgumentsOfTheMethod($methodName, TableNode $table) |
||
824 | } |
||
825 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: