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 |
||
18 | abstract class AbstractFeedType implements FeedTypeInterface |
||
19 | { |
||
20 | /** |
||
21 | * @var CachedSourceManager |
||
22 | */ |
||
23 | protected $sourceManager; |
||
24 | |||
25 | /** |
||
26 | * @param CachedSourceManager $sourceManager |
||
27 | */ |
||
28 | 4 | public function __construct(CachedSourceManager $sourceManager) |
|
32 | |||
33 | /** |
||
34 | * @inheritdoc |
||
35 | */ |
||
36 | 4 | View Code Duplication | public function setOptions(OptionsResolver $resolver) |
|
|||
37 | { |
||
38 | 4 | $resolver->setRequired([ |
|
39 | 4 | 'forced', |
|
40 | 'feed', |
||
41 | 'date_locale', |
||
42 | 'number_locale', |
||
43 | 'default_values', |
||
44 | ]); |
||
45 | |||
46 | 4 | $resolver->setAllowedValues('date_locale', ['en', 'nl']); |
|
47 | 4 | $resolver->setAllowedValues('number_locale', ['en', 'nl']); |
|
48 | |||
49 | 4 | $resolver->setAllowedTypes('forced', 'bool'); |
|
50 | 4 | $resolver->setAllowedTypes('feed', Feed::class); |
|
51 | 4 | $resolver->setAllowedTypes('default_values', 'array'); |
|
52 | |||
53 | 4 | $resolver->setDefaults([ |
|
54 | 4 | 'forced' => false, |
|
55 | 'date_locale' => 'en', |
||
56 | 'number_locale' => 'en', |
||
57 | 'default_values' => [], |
||
58 | ]); |
||
59 | 4 | } |
|
60 | |||
61 | /** |
||
62 | * @inheritdoc |
||
63 | */ |
||
64 | 4 | public function build(FeedBuilderInterface $builder, array $options) |
|
65 | { |
||
66 | 4 | $sourceManager = $this->getSourceManager(); |
|
67 | |||
68 | // range 100-200: first make sure we have consistent keys and a valid origin |
||
69 | 4 | $this->addModifierBetween($builder, new FeederTransformer\LowercaseKeysTransformer(), 100, 200); |
|
70 | 4 | $this->addModifierBetween($builder, new FeederTransformer\UnderscoreKeysTransformer(), 100, 200); |
|
71 | 4 | $this->addModifierBetween($builder, new FeederTransformer\StripKeysPunctuationTransformer(), 100, 200); |
|
72 | 4 | $this->addModifierBetween($builder, new FeederTransformer\ExpandAttributesTransformer(), 100, 200); |
|
73 | 4 | $this->addModifierBetween($builder, new FeederTransformer\TrimTransformer(), 100, 200); |
|
74 | 4 | $this->addModifierBetween($builder, new FeederTransformer\StripCommentsTransformer(), 100, 200); |
|
75 | |||
76 | // This transforms the regular ItemBag into our own FeedItemBag, |
||
77 | // which adds logic for the original id/url and modification date. |
||
78 | // We need to do this early on, since some of our filter/transformation |
||
79 | // listeners depend on this. |
||
80 | 4 | $mapper = new FeedItemBagMapper( |
|
81 | 4 | $options['feed'], |
|
82 | 4 | $this->getOriginalIdCallback(), |
|
83 | 4 | $this->getOriginalUrlCallback(), |
|
84 | 4 | $this->getModificationDateCallback() |
|
85 | ); |
||
86 | 4 | $this->addModifierBetween($builder, $mapper, 200, 250); |
|
87 | |||
88 | // range 300-400: perform validation and checks for skipping early on |
||
89 | |||
90 | // validate origin id |
||
91 | 4 | $this->addModifierBetween($builder, new OriginIdValidator(), 300, 400); |
|
92 | |||
93 | // skip blocked sources |
||
94 | 4 | $this->addModifierBetween($builder, new BlockedSourceFilter($sourceManager), 300, 400); |
|
95 | |||
96 | // check for modification dates, but only when not forced |
||
97 | 4 | if ($options['forced'] === false) { |
|
98 | 4 | $this->addModifierBetween($builder, new ModifiedItemFilter($sourceManager), 300, 400); |
|
99 | } |
||
100 | 4 | } |
|
101 | |||
102 | /** |
||
103 | * @inheritdoc |
||
104 | */ |
||
105 | 4 | public function getOriginalIdCallback() |
|
111 | |||
112 | /** |
||
113 | * @inheritdoc |
||
114 | */ |
||
115 | 4 | public function getOriginalUrlCallback() |
|
121 | |||
122 | /** |
||
123 | * @inheritdoc |
||
124 | */ |
||
125 | public function getModificationDateCallback() |
||
126 | { |
||
127 | 4 | return function (ParameterBag $item) { |
|
128 | 4 | if ($date = $item->get($this->getModificationDateField(), null, true)) { |
|
129 | try { |
||
130 | 4 | $datetime = new \DateTime($date); |
|
131 | 4 | if ($datetime->format('H:i:s') === '00:00:00') { |
|
132 | $datetime->setTime(23, 59, 59); |
||
133 | } |
||
134 | |||
135 | 4 | return $datetime; |
|
136 | } catch (\Exception $e) { |
||
137 | } |
||
138 | } |
||
139 | |||
140 | return null; |
||
141 | 4 | }; |
|
142 | } |
||
143 | |||
144 | /** |
||
145 | * Adds the given modifier between the start and end index, if there is a vacant position. |
||
146 | * |
||
147 | * @param FeedBuilderInterface $builder |
||
148 | * @param ModifierInterface $modifier |
||
149 | * @param int $startIndex |
||
150 | * @param int $endIndex |
||
151 | * @param bool $continue |
||
152 | * |
||
153 | * @throws \OutOfBoundsException |
||
154 | */ |
||
155 | 4 | View Code Duplication | protected function addModifierBetween( |
156 | FeedBuilderInterface $builder, |
||
157 | ModifierInterface $modifier, |
||
158 | $startIndex, |
||
159 | $endIndex, |
||
160 | $continue = null |
||
161 | ) { |
||
162 | 4 | for ($position = $startIndex; $position <= $endIndex; ++$position) { |
|
163 | 4 | if (!$builder->hasModifierAt($position)) { |
|
164 | 4 | $builder->addModifier($modifier, $position, $continue); |
|
165 | |||
166 | 4 | return; |
|
167 | } |
||
168 | } |
||
169 | |||
170 | throw new \OutOfBoundsException(sprintf('No position left between %d and %d', $startIndex, $endIndex)); |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * Adds the given transformer between the start and end index, if there is a vacant position. |
||
175 | * |
||
176 | * @param FeedBuilderInterface $builder |
||
177 | * @param TransformerInterface $transformer |
||
178 | * @param string $field |
||
179 | * @param int $startIndex |
||
180 | * @param int $endIndex |
||
181 | * @param bool $continue |
||
182 | * |
||
183 | * @throws \OutOfBoundsException |
||
184 | */ |
||
185 | 4 | protected function addTransformerBetween( |
|
201 | |||
202 | /** |
||
203 | * @return CachedSourceManager |
||
204 | */ |
||
205 | 4 | protected function getSourceManager() |
|
209 | |||
210 | /** |
||
211 | * @return string |
||
212 | */ |
||
213 | abstract protected function getOriginalIdField(); |
||
214 | |||
215 | /** |
||
216 | * @return string |
||
217 | */ |
||
218 | abstract protected function getOriginalUrlField(); |
||
219 | |||
220 | /** |
||
221 | * @return string |
||
222 | */ |
||
223 | abstract protected function getModificationDateField(); |
||
224 | } |
||
225 |
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.