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:
1 | <?php |
||
11 | class FeatureContext implements SnippetAcceptingContext |
||
12 | { |
||
13 | /** |
||
14 | * @var string |
||
15 | */ |
||
16 | private $phpBin; |
||
17 | /** |
||
18 | * @var Process |
||
19 | */ |
||
20 | private $process; |
||
21 | /** |
||
22 | * @var string |
||
23 | */ |
||
24 | private $workingDir; |
||
25 | |||
26 | /** |
||
27 | * Cleans test folders in the temporary directory. |
||
28 | * |
||
29 | * @BeforeSuite |
||
30 | * @AfterSuite |
||
31 | */ |
||
32 | public static function cleanTestFolders() |
||
33 | { |
||
34 | if (is_dir($dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'behat-web-api')) { |
||
35 | self::clearDirectory($dir); |
||
36 | } |
||
37 | } |
||
38 | |||
39 | /** |
||
40 | * Prepares test folders in the temporary directory. |
||
41 | * |
||
42 | * @BeforeScenario |
||
43 | */ |
||
44 | public function prepareScenario() |
||
45 | { |
||
46 | $dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'behat-web-api' . DIRECTORY_SEPARATOR . |
||
47 | md5(microtime() * rand(0, 10000)); |
||
48 | |||
49 | mkdir($dir . '/features/bootstrap', 0777, true); |
||
50 | |||
51 | $phpFinder = new PhpExecutableFinder(); |
||
52 | if (false === $php = $phpFinder->find()) { |
||
53 | throw new \RuntimeException('Unable to find the PHP executable.'); |
||
54 | } |
||
55 | $this->workingDir = $dir; |
||
56 | $this->phpBin = $php; |
||
57 | $this->process = new Process(null); |
||
58 | } |
||
59 | |||
60 | /** |
||
61 | * Creates a file with specified name and context in current working dir. |
||
62 | * |
||
63 | * @Given /^(?:there is )?a file named "([^"]*)" with:$/ |
||
64 | * |
||
65 | * @param string $filename name of the file (relative path) |
||
66 | * @param PyStringNode $content PyString string instance |
||
67 | */ |
||
68 | public function aFileNamedWith($filename, PyStringNode $content) |
||
69 | { |
||
70 | $content = strtr((string) $content, array("'''" => '"""')); |
||
71 | $this->createFile($this->workingDir . '/' . $filename, $content); |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Runs behat command with provided parameters |
||
76 | * |
||
77 | * @When /^I run "behat(?: ((?:\"|[^"])*))?"$/ |
||
78 | * |
||
79 | * @param string $argumentsString |
||
80 | */ |
||
81 | public function iRunBehat($argumentsString = '') |
||
82 | { |
||
83 | $argumentsString = strtr($argumentsString, array('\'' => '"')); |
||
84 | |||
85 | $this->process->setWorkingDirectory($this->workingDir); |
||
86 | $this->process->setCommandLine( |
||
87 | sprintf( |
||
88 | '%s %s %s %s', |
||
89 | $this->phpBin, |
||
90 | escapeshellarg(BEHAT_BIN_PATH), |
||
91 | $argumentsString, |
||
92 | strtr('--format-settings=\'{"timer": false}\' --no-colors', array('\'' => '"', '"' => '\"')) |
||
93 | ) |
||
94 | ); |
||
95 | $this->process->start(); |
||
96 | $this->process->wait(); |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Checks whether previously runned command passes|failes with provided output. |
||
101 | * |
||
102 | * @Then /^it should (fail|pass) with:$/ |
||
103 | * |
||
104 | * @param string $success "fail" or "pass" |
||
105 | * @param PyStringNode $text PyString text instance |
||
106 | */ |
||
107 | public function itShouldPassWith($success, PyStringNode $text) |
||
108 | { |
||
109 | $this->itShouldFail($success); |
||
110 | $this->theOutputShouldContain($text); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Checks whether last command output contains provided string. |
||
115 | * |
||
116 | * @Then the output should contain: |
||
117 | * |
||
118 | * @param PyStringNode $text PyString text instance |
||
119 | */ |
||
120 | public function theOutputShouldContain(PyStringNode $text) |
||
124 | |||
125 | private function getExpectedOutput(PyStringNode $expectedText) |
||
126 | { |
||
127 | $text = strtr($expectedText, array('\'\'\'' => '"""')); |
||
128 | |||
129 | // windows path fix |
||
130 | if ('/' !== DIRECTORY_SEPARATOR) { |
||
131 | $text = preg_replace_callback( |
||
132 | '/ features\/[^\n ]+/', function ($matches) { |
||
133 | return str_replace('/', DIRECTORY_SEPARATOR, $matches[0]); |
||
134 | }, $text |
||
135 | ); |
||
136 | $text = preg_replace_callback( |
||
137 | '/\<span class\="path"\>features\/[^\<]+/', function ($matches) { |
||
138 | return str_replace('/', DIRECTORY_SEPARATOR, $matches[0]); |
||
139 | }, $text |
||
140 | ); |
||
141 | $text = preg_replace_callback( |
||
142 | '/\+[fd] [^ ]+/', function ($matches) { |
||
143 | return str_replace('/', DIRECTORY_SEPARATOR, $matches[0]); |
||
144 | }, $text |
||
145 | ); |
||
146 | } |
||
147 | |||
148 | return $text; |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Checks whether previously run command failed|passed. |
||
153 | * |
||
154 | * @Then /^it should (fail|pass)$/ |
||
155 | * |
||
156 | * @param string $success "fail" or "pass" |
||
157 | */ |
||
158 | public function itShouldFail($success) |
||
174 | |||
175 | private function getExitCode() |
||
179 | |||
180 | private function getOutput() |
||
181 | { |
||
182 | $output = $this->process->getErrorOutput() . $this->process->getOutput(); |
||
183 | |||
184 | // Normalize the line endings in the output |
||
185 | if ("\n" !== PHP_EOL) { |
||
191 | |||
192 | private function createFile($filename, $content) |
||
201 | |||
202 | private static function clearDirectory($path) |
||
219 | } |
||
220 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.