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 WebserverSkeleton 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 WebserverSkeleton, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | class WebserverSkeleton extends AbstractSkeleton |
||
12 | { |
||
13 | |||
14 | const NAME = "apache"; |
||
15 | |||
16 | /** |
||
17 | * @param \ArrayObject $project |
||
18 | * |
||
19 | * @return mixed |
||
20 | */ |
||
21 | public function create(\ArrayObject $project) |
||
22 | { |
||
23 | $this->handleAliases($project, $aliases); |
||
24 | // nginx |
||
25 | $this->prepareNginxDirectories($project); |
||
26 | $this->fileSystemProvider->renderConfig($this->fileSystemProvider->getNginxConfigTemplateDir(),$this->fileSystemProvider->getNginxConfigTemplateDir(true),$this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/nginx.d/"); |
||
27 | // apache |
||
28 | $this->prepareApacheDirectories($project); |
||
29 | $this->fileSystemProvider->renderDistConfig($this->fileSystemProvider->getApacheConfigTemplateDir(),$this->fileSystemProvider->getApacheConfigTemplateDir(true),$this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/apache.d/"); |
||
30 | } |
||
31 | |||
32 | /** |
||
33 | * @return mixed |
||
34 | */ |
||
35 | public function preMaintenance() |
||
36 | { |
||
37 | if ($this->isWebserverNginx()) { |
||
38 | $this->processProvider->executeSudoCommand("rm -Rf " . $this->app["config"]["nginx"]["sitesavailable"] . "/*"); |
||
39 | $this->processProvider->executeSudoCommand("rm -Rf " . $this->app["config"]["nginx"]["sitesenabled"] . "/*"); |
||
40 | } else { |
||
41 | $this->processProvider->executeSudoCommand("rm -Rf " . $this->app["config"]["apache"]["sitesavailable"] . "/*"); |
||
42 | $this->processProvider->executeSudoCommand("rm -Rf " . $this->app["config"]["apache"]["sitesenabled"] . "/*"); |
||
43 | } |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * @param \ArrayObject $project |
||
48 | * |
||
49 | * @return mixed |
||
50 | */ |
||
51 | public function maintenance(\ArrayObject $project) |
||
52 | { |
||
53 | if ($this->app["config"]["develmode"] || !file_exists($this->fileSystemProvider->getProjectDirectory($project["name"]) . "/data/current")) { |
||
54 | if (!is_dir($this->fileSystemProvider->getProjectDirectory($project["name"]) . "/data/current")){ |
||
55 | $this->processProvider->executeSudoCommand("rm -f " . $this->fileSystemProvider->getProjectDirectory($project["name"]) . "/data/current"); |
||
56 | $this->processProvider->executeSudoCommand("ln -sf " . $this->fileSystemProvider->getProjectDirectory($project["name"]) . "/data/" . $project["name"] . "/ " . $this->fileSystemProvider->getProjectDirectory($project["name"]) . "/data/current"); |
||
57 | } |
||
58 | } |
||
59 | |||
60 | View Code Duplication | if (PHP_OS == "Darwin") { |
|
|
|||
61 | $this->processProvider->executeSudoCommand('find ' . $this->fileSystemProvider->getProjectDirectory($project["name"]) . '/data/current -type d -name .git -exec cd {} "\;" -exec git config core.filemode false "\;"'); |
||
62 | } else { |
||
63 | $this->processProvider->executeSudoCommand('find ' . $this->fileSystemProvider->getProjectDirectory($project["name"]) . '/data/current -type d -name .git -exec cd {} \; -exec git config core.filemode false \;'); |
||
64 | } |
||
65 | |||
66 | $this->dialogProvider->logConfig("Updating aliases webserver config file"); |
||
67 | $this->generateBasicAliases($project, $aliases); |
||
68 | |||
69 | if ($this->isWebserverNginx()) { |
||
70 | $this->maintenanceNginx($project, $aliases); |
||
71 | } else { |
||
72 | $this->maintenanceApache($project, $aliases); |
||
73 | } |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * @return mixed |
||
78 | */ |
||
79 | public function postMaintenance() |
||
99 | |||
100 | private function writeToFirstHostFile() |
||
101 | { |
||
102 | $this->fileSystemProvider->render("/apache/000firsthost.conf.twig", $this->app["config"]["apache"]["sitesavailable"] . "/000firsthost.conf", array( |
||
103 | 'admin' => $this->app["config"]["apache"]["admin"] |
||
104 | )); |
||
105 | } |
||
106 | |||
107 | private function writeToHostFile() |
||
108 | { |
||
109 | $hostlines = array(); |
||
110 | $dialogProvider = $this->dialogProvider; |
||
111 | $this->fileSystemProvider->projectsLoop(function ($project) use (&$hostlines, $dialogProvider) { |
||
112 | $hostlines[] = $this->app["config"]["webserver"]["localip"] . " " . $project["name"] . "." . $this->app["config"]["webserver"]["hostmachine"] . " www." . $project["name"] . "." . $this->app["config"]["webserver"]["hostmachine"] . "\n"; |
||
113 | }); |
||
114 | $this->dialogProvider->logTask("Updating the /etc/hosts file (add lines)"); |
||
115 | $hostsfile = file("/etc/hosts"); |
||
116 | $resultLines = array(); |
||
117 | $foundSection = false; |
||
118 | $inSection = false; |
||
119 | foreach ($hostsfile as $line) { |
||
120 | if (!$inSection) { |
||
121 | if (strpos($line, "#KDEPLOY_start") === 0) { |
||
122 | $inSection = true; |
||
123 | $foundSection = true; |
||
124 | $resultLines[] = $line; |
||
125 | $resultLines = array_merge($resultLines, $hostlines); |
||
126 | } else { |
||
127 | $resultLines[] = $line; |
||
128 | } |
||
129 | } else { |
||
130 | if (strpos($line, "#KDEPLOY_end") === 0) { |
||
131 | $inSection = false; |
||
132 | $resultLines[] = $line; |
||
133 | } |
||
134 | } |
||
135 | } |
||
136 | if (!$foundSection) { |
||
137 | $resultLines[] = "#KDEPLOY_start autogenerated section. do not edit below this line. do not remove this line.\n"; |
||
138 | $resultLines = array_merge($resultLines, $hostlines); |
||
139 | $resultLines[] = "#KDEPLOY_end autogenerated section. do not edit above this line. do not remove this line.\n"; |
||
140 | } |
||
141 | $this->fileSystemProvider->writeProtectedFile("/etc/hosts", implode("", $resultLines)); |
||
142 | } |
||
143 | |||
144 | /** |
||
145 | * @param \ArrayObject $project |
||
146 | */ |
||
147 | private function removeFromHostFile(\ArrayObject $project) |
||
148 | { |
||
149 | |||
150 | $hostlines[] = $this->app["config"]["webserver"]["localip"] . " " . $project["name"] . "." . $this->app["config"]["webserver"]["hostmachine"] . " www." . $project["name"] . "." . $this->app["config"]["webserver"]["hostmachine"] . "\n"; |
||
151 | |||
152 | $this->dialogProvider->logTask("Updating the /etc/hosts file (remove lines)"); |
||
153 | $hostsfile = file("/etc/hosts"); |
||
154 | $resultLines = array(); |
||
155 | |||
156 | foreach ($hostsfile as $line) { |
||
157 | if (!in_array($line, $hostlines)) { |
||
158 | $resultLines[] = $line; |
||
159 | } |
||
160 | } |
||
161 | |||
162 | $this->fileSystemProvider->writeProtectedFile("/etc/hosts", implode("", $resultLines)); |
||
163 | } |
||
164 | |||
165 | /** |
||
166 | * @return string |
||
167 | */ |
||
168 | public function getName() |
||
172 | |||
173 | /** |
||
174 | * @param \ArrayObject $project |
||
175 | * |
||
176 | * @return mixed |
||
177 | */ |
||
178 | public function preBackup(\ArrayObject $project) |
||
179 | { |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * @param \ArrayObject $project |
||
184 | * |
||
185 | * @return mixed |
||
186 | */ |
||
187 | public function postBackup(\ArrayObject $project) |
||
188 | { |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * @param \ArrayObject $project |
||
193 | * |
||
194 | * @return mixed |
||
195 | */ |
||
196 | public function preRemove(\ArrayObject $project) |
||
197 | { |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * @param \ArrayObject $project |
||
202 | * |
||
203 | * @return mixed |
||
204 | */ |
||
205 | public function postRemove(\ArrayObject $project) |
||
206 | { |
||
207 | $this->removeFromHostFile($project); |
||
208 | |||
209 | if ($this->isWebserverNginx()) { |
||
210 | $this->processProvider->executeSudoCommand("rm -f ".$this->app["config"]["nginx"]["sitesenabled"] . "/" . $project["name"] . ".conf"); |
||
211 | $this->processProvider->executeSudoCommand("rm -f ".$this->app["config"]["nginx"]["sitesavailable"] . "/" . $project["name"] . ".conf"); |
||
212 | } |
||
213 | else{ |
||
214 | $this->processProvider->executeSudoCommand("rm -f ".$this->app["config"]["apache"]["sitesenabled"] . "/" . $project["name"] . ".conf"); |
||
215 | $this->processProvider->executeSudoCommand("rm -f ".$this->app["config"]["apache"]["sitesavailable"] . "/" . $project["name"] . ".conf"); |
||
216 | } |
||
217 | |||
218 | if (PHP_OS == "Darwin") { |
||
219 | $this->processProvider->executeSudoCommand("apachectl -k restart"); |
||
220 | } |
||
221 | else{ |
||
222 | if ($this->isWebserverNginx()) { |
||
223 | $this->processProvider->executeSudoCommand("service nginx reload"); |
||
224 | } else { |
||
225 | $this->processProvider->executeSudoCommand("service apache2 reload"); |
||
226 | } |
||
227 | } |
||
228 | |||
229 | } |
||
230 | |||
231 | /** |
||
232 | * @param \ArrayObject $project |
||
233 | * @param \SimpleXMLElement $config The configuration array |
||
234 | * @return \SimpleXMLElement |
||
235 | */ |
||
236 | public function writeConfig(\ArrayObject $project, \SimpleXMLElement $config) |
||
237 | { |
||
238 | $config = $this->projectConfigProvider->addVar($config, 'project.url', $project["url"]); |
||
239 | if (isset($project["aliases"])) { |
||
240 | $config = $this->projectConfigProvider->addVarWithItems($config, 'project.aliases', $project["aliases"]); |
||
241 | } |
||
242 | |||
243 | return $config; |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * @return string[] |
||
248 | */ |
||
249 | public function dependsOn() |
||
250 | { |
||
251 | return array("base"); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * @param \ArrayObject $project |
||
256 | */ |
||
257 | private function prepareNginxDirectories(\ArrayObject $project) |
||
258 | { |
||
259 | $this->processProvider->executeSudoCommand("mkdir -p " . $this->app["config"]["nginx"]["sitesavailable"]); |
||
260 | $this->processProvider->executeSudoCommand("mkdir -p " . $this->app["config"]["nginx"]["sitesenabled"]); |
||
261 | $this->processProvider->executeSudoCommand("mkdir -p " . $this->fileSystemProvider->getProjectDirectory($project["name"]) . "/apachelogs"); |
||
262 | $this->processProvider->executeSudoCommand("mkdir -p " . $this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/nginx.d"); |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * @param \ArrayObject $project |
||
267 | */ |
||
268 | public function prepareApacheDirectories(\ArrayObject $project) |
||
269 | { |
||
270 | $this->processProvider->executeSudoCommand("mkdir -p " . $this->app["config"]["apache"]["vhostdir"]); |
||
271 | $this->processProvider->executeSudoCommand("mkdir -p " . $this->fileSystemProvider->getProjectDirectory($project["name"]) . "/apachelogs"); |
||
272 | $this->processProvider->executeSudoCommand("mkdir -p " . $this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/apache.d"); |
||
273 | $this->processProvider->executeSudoCommand("mkdir -p " . $this->fileSystemProvider->getProjectDirectory($project["name"]) . "/stats"); |
||
274 | $this->processProvider->executeSudoCommand("chmod -R 777 " . $this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/apache.d/"); |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * @param \ArrayObject $project |
||
279 | * @param $aliases |
||
280 | */ |
||
281 | private function handleAliases(\ArrayObject &$project, &$aliases) |
||
282 | { |
||
283 | // url |
||
284 | $defaultUrl = $project["name"] . ".be"; |
||
285 | $project["url"] = $this->dialogProvider->askFor("Enter the base url", null, $defaultUrl); |
||
286 | // url aliases |
||
287 | $this->generateBasicAliases($project, $aliases); |
||
288 | $aliases = array(); |
||
289 | if ($this->noInteraction) { |
||
290 | $this->dialogProvider->logNotice("--no-iteraction selected, using www." . $project["url"]); |
||
291 | $aliases[] = "www." . $project["url"]; |
||
292 | } else { |
||
293 | while (1 == 1) { |
||
294 | $alias = $this->dialogProvider->askFor("Add an url alias (leave empty to stop adding):"); |
||
295 | if (empty($alias)) { |
||
296 | break; |
||
297 | } else { |
||
298 | $aliases[] = $alias; |
||
299 | } |
||
300 | } |
||
301 | } |
||
302 | $project["aliases"] = $aliases; |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * @param \ArrayObject $project |
||
307 | * @param $aliases |
||
308 | */ |
||
309 | private function generateBasicAliases(\ArrayObject &$project, &$aliases) |
||
310 | { |
||
311 | $hostmachine = $this->app["config"]["webserver"]["hostmachine"]; |
||
312 | $aliases = (isset($project["aliases"])) ? $project["aliases"] : array(); |
||
313 | $aliases[] = $project["url"]; |
||
314 | $aliases[] = $project["name"] . "." . $hostmachine; |
||
315 | $aliases[] = "*." . $project["name"] . "." . $hostmachine; |
||
316 | if ($this->app["config"]["develmode"]) { |
||
317 | $aliases[] = $project["name"] . ".*.xip.io"; |
||
318 | $aliases[] = "*." . $project["name"] . ".*.xip.io"; |
||
319 | } |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * @param \ArrayObject $project |
||
324 | * @return string |
||
325 | */ |
||
326 | private function processConfigFiles(\ArrayObject $project, $configs) |
||
327 | { |
||
328 | $ignoreList = array(); |
||
329 | foreach ($configs as $config) { |
||
330 | /** @var SplFileInfo $config */ |
||
331 | if ($config->getExtension() == "local") { |
||
332 | $ignoreList[] = $config->getBasename('.' . $config->getExtension()); |
||
333 | } |
||
334 | } |
||
335 | |||
336 | $configcontent = ''; |
||
337 | foreach ($configs as $config) { |
||
338 | /** @var SplFileInfo $config */ |
||
339 | if ($config->getExtension() == "dist" ){ |
||
340 | $realPathArray = explode("\n", file_get_contents($config->getRealPath())); |
||
341 | $realPath = $realPathArray[0]; |
||
342 | $path = BASE_DIR . "/templates" . $realPath; |
||
343 | if (!file_exists($path)){ |
||
344 | $this->dialogProvider->logError("There is Apache config in a .dist file, or you mistyped the template path, check " . $config); |
||
345 | } |
||
346 | $content = $this->fileSystemProvider->renderString(file_get_contents($path), array()); |
||
347 | } else { |
||
348 | $realPath = $config->getRealPath(); |
||
349 | $content = file_get_contents($realPath); |
||
350 | } |
||
351 | if ($config->getExtension() != "local" && in_array($config->getBasename('.'. $config->getExtension()),$ignoreList)){ |
||
352 | $configcontent .= "\n#SKIPPED " . $realPath . " because there was a .local file\n\n"; |
||
353 | } else { |
||
354 | $configcontent .= "\n#BEGIN " . $realPath . "\n\n"; |
||
355 | $configcontent .= $this->projectConfigProvider->searchReplacer($content, $project) . "\n"; |
||
356 | $configcontent .= "\n#END " . $realPath . "\n\n"; |
||
357 | } |
||
358 | $this->checkObviousErrors($project, $config, $configcontent); |
||
359 | } |
||
360 | return $configcontent; |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * @param \ArrayObject $project |
||
365 | * @param SplFileInfo $config |
||
366 | * @param string $content |
||
367 | */ |
||
368 | function checkObviousErrors(\ArrayObject $project, SplFileInfo $config, $content){ |
||
369 | // project was not migrated because the 19php.conf file does not contain "proxy:unix:/var/run/php5-fpm" |
||
370 | if (!$this->app["config"]["develmode"] && strpos($config->getFilename(), "19php") !== FALSE && strpos($content, "proxy:unix:/var/run/php-fpm") === FALSE){ |
||
371 | $this->dialogProvider->logWarning("The ".$project["name"]." project was not migrated yet, this will NOT work"); |
||
372 | } |
||
373 | } |
||
374 | |||
375 | /** |
||
376 | * @param $aliases |
||
377 | * @return string |
||
378 | */ |
||
379 | private function generateAliasLine($aliases, $type) |
||
380 | { |
||
381 | $serverName = ($type == 'nginx'?"server_name ":"ServerAlias "); |
||
382 | foreach ($aliases as $alias) { |
||
383 | $serverName .= " " . $alias; |
||
384 | } |
||
385 | $serverName .= ($type == 'nginx'?";\n":"\n"); |
||
386 | return $serverName; |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * @param \ArrayObject $project |
||
391 | * @param $aliases |
||
392 | */ |
||
393 | public function maintenanceNginx(\ArrayObject $project, $aliases) |
||
394 | { |
||
395 | $this->prepareNginxDirectories($project); |
||
396 | $serverName = $this->generateAliasLine($aliases, $this->app["config"]["webserver"]["engine"]); |
||
397 | $this->processProvider->executeSudoCommand("rm -f " . $this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/nginx.d/05servername*"); |
||
398 | $finder = new Finder(); |
||
399 | $finder->files()->in($this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/nginx.d/")->name("01-base*"); |
||
400 | if ($finder->count() == 0) { |
||
401 | $this->fileSystemProvider->writeProtectedFile($this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/nginx.d/05servername", $serverName); |
||
402 | } |
||
403 | $configcontent = $this->processConfigFiles($project, $this->fileSystemProvider->getProjectNginxConfigs($project)); |
||
404 | $this->fileSystemProvider->writeProtectedFile($this->app["config"]["nginx"]["sitesavailable"] . "/" . $project["name"] . ".conf", $configcontent); |
||
405 | } |
||
406 | |||
407 | /** |
||
408 | * @param \ArrayObject $project |
||
409 | * @param $aliases |
||
410 | */ |
||
411 | private function maintenanceApache(\ArrayObject $project, $aliases) |
||
412 | { |
||
413 | $serverAlias = $this->generateAliasLine($aliases, $this->app["config"]["webserver"]["engine"]); |
||
414 | $this->fileSystemProvider->writeProtectedFile($this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/apache.d/05aliases", $serverAlias); |
||
415 | if ($this->app["config"]["develmode"]) { |
||
416 | $this->fileSystemProvider->writeProtectedFile($this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/apache.d/06devmode", "SetEnv APP_ENV dev"); |
||
417 | } else { |
||
418 | $this->processProvider->executeSudoCommand("rm -f " . $this->fileSystemProvider->getProjectConfigDirectory($project["name"]) . "/apache.d/06devmode"); |
||
419 | } |
||
420 | $configcontent = $this->processConfigFiles($project, $this->fileSystemProvider->getProjectApacheConfigs($project)); |
||
421 | if ($this->app["config"]["develmode"]) { |
||
422 | $configcontent = str_replace("-Indexes", "+Indexes", $configcontent); |
||
423 | } |
||
424 | $this->fileSystemProvider->writeProtectedFile($this->app["config"]["apache"]["sitesavailable"] . "/" . $project["name"] . ".conf", $configcontent); |
||
425 | } |
||
426 | |||
427 | /** |
||
428 | * @return bool |
||
429 | */ |
||
430 | private function isWebserverNginx() |
||
434 | |||
435 | } |
||
436 |
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.