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 |
||
23 | class BuilderClassFactory |
||
24 | { |
||
25 | /** |
||
26 | * @var BuilderFactory |
||
27 | */ |
||
28 | private $factory; |
||
29 | |||
30 | /** |
||
31 | * @var \PhpParser\Parser |
||
32 | */ |
||
33 | private $parser; |
||
34 | |||
35 | /** |
||
36 | * @var ClassMetadata |
||
37 | */ |
||
38 | private $subject; |
||
39 | |||
40 | /** |
||
41 | * @var ClassMetadata |
||
42 | */ |
||
43 | private $builder = null; |
||
44 | |||
45 | /** |
||
46 | * @var bool |
||
47 | */ |
||
48 | private $withReturnTypeDeclaration; |
||
49 | |||
50 | public function __construct(ClassMetadata $subject, ClassMetadata $builder = null, $withReturnTypeDeclaration = false) |
||
58 | |||
59 | public function addBuildMethod() |
||
60 | { |
||
61 | if ($this->builder->hasPublicMethod('build', false)) { |
||
62 | throw new LogicException(sprintf('Method "build()" is already defined in "%s".', $this->builder->getName())); |
||
63 | } |
||
64 | |||
65 | $method = $this->factory->method('build') |
||
66 | ->makePublic() |
||
67 | ->addStmts($this->parser->parse('<?php return parent::build();')) |
||
|
|||
68 | ->setDocComment(sprintf( |
||
69 | ' |
||
70 | /** |
||
71 | * Builds new instance of %s from provided arguments. |
||
72 | * |
||
73 | * @return %s |
||
74 | */', $this->subject->getFqcn(), $this->subject->getFqcn())); |
||
75 | |||
76 | if ($this->withReturnTypeDeclaration) { |
||
77 | $method->setReturnType($this->subject->getFqcn()); |
||
78 | } |
||
79 | |||
80 | $this->appendMethod($method->getNode()); |
||
81 | |||
82 | return $this; |
||
83 | } |
||
84 | |||
85 | public function addCreateBuilderMethod() |
||
86 | { |
||
87 | if ($this->builder->hasPublicMethod('createBuilder', false)) { |
||
88 | throw new LogicException(sprintf('Method "createBuilder()" is already defined in "%s".', $this->builder->getFqcn())); |
||
89 | } |
||
90 | |||
91 | if ($this->builder->isAbstract()) { |
||
92 | throw new LogicException(sprintf('Method "createBuilder()" can not be generated for class "%s" since it\'s abstract.', $this->builder->getFqcn())); |
||
93 | } |
||
94 | |||
95 | $method = $this->factory->method('createBuilder') |
||
96 | ->makePublic() |
||
97 | ->makeStatic() |
||
98 | ->addStmts($this->parser->parse('<?php return parent::createBuilder();')) |
||
99 | ->setDocComment(sprintf( |
||
100 | ' |
||
101 | /** |
||
102 | * Produces new builder. |
||
103 | * |
||
104 | * @return %s |
||
105 | * |
||
106 | * @throws \RunOpenCode\AbstractBuilder\Exception\RuntimeException |
||
107 | */', $this->builder->getFqcn())); |
||
108 | |||
109 | if ($this->withReturnTypeDeclaration) { |
||
110 | $method->setReturnType($this->builder->getFqcn()); |
||
111 | } |
||
112 | |||
113 | $this->appendMethod($method->getNode()); |
||
114 | |||
115 | return $this; |
||
116 | } |
||
117 | |||
118 | View Code Duplication | public function addGetObjectFqcnMethod() |
|
119 | { |
||
120 | if ($this->builder->hasPublicMethod('getObjectFqcn', false)) { |
||
121 | throw new LogicException(sprintf('Method "getObjectFqcn()" is already defined in "%s".', $this->builder->getFqcn())); |
||
122 | } |
||
123 | |||
124 | $method = $this->factory->method('getObjectFqcn') |
||
125 | ->makeProtected() |
||
126 | ->addStmts($this->parser->parse(sprintf('<?php return %s::class;', $this->subject->getFqcn()))) |
||
127 | ->setDocComment( |
||
128 | ' |
||
129 | /** |
||
130 | * {@inheritdoc} |
||
131 | */' |
||
132 | ); |
||
133 | |||
134 | if ($this->withReturnTypeDeclaration) { |
||
135 | $method->setReturnType('string'); |
||
136 | } |
||
137 | |||
138 | $this->appendMethod($method->getNode()); |
||
139 | |||
140 | return $this; |
||
141 | } |
||
142 | |||
143 | View Code Duplication | public function addConfigureParametersMethod() |
|
169 | |||
170 | public function addGetter($name, ParameterMetadata $parameter) |
||
191 | |||
192 | public function addSetter($name, ParameterMetadata $parameter) |
||
193 | { |
||
194 | $ast = $parameter->getAst(); |
||
223 | |||
224 | /** |
||
225 | * Initializes new builder class |
||
226 | * |
||
227 | * @param string $filename |
||
228 | * @param string $class |
||
229 | * |
||
230 | * @return FileMetadata |
||
231 | */ |
||
232 | public function initialize($filename, $class) |
||
284 | |||
285 | /** |
||
286 | * Append method to class AST, order by visibility. |
||
287 | * |
||
288 | * @param ClassMethod $method |
||
289 | */ |
||
290 | private function appendMethod(ClassMethod $method) |
||
296 | } |
||
297 | |||
298 |
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.