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 |
||
34 | class FetchCommand extends Command |
||
35 | { |
||
36 | /** |
||
37 | * @var EventDispatcherInterface |
||
38 | */ |
||
39 | protected $eventDispatcher; |
||
40 | |||
41 | /** |
||
42 | * @var ManagerInterface |
||
43 | */ |
||
44 | protected $manager; |
||
45 | |||
46 | /** |
||
47 | * @var SourcesRegistryInterface |
||
48 | */ |
||
49 | protected $sourcesRegistry; |
||
50 | |||
51 | /** |
||
52 | * @var SymfonyStyle |
||
53 | */ |
||
54 | protected $output; |
||
55 | |||
56 | 9 | public function __construct(EventDispatcherInterface $eventDispatcher, ManagerInterface $manager, SourcesRegistryInterface $sourcesRegistry) |
|
63 | |||
64 | /** |
||
65 | * {@inheritdoc} |
||
66 | */ |
||
67 | 9 | protected function configure() |
|
80 | |||
81 | /** |
||
82 | * {@inheritdoc} |
||
83 | */ |
||
84 | 9 | protected function execute(InputInterface $input, OutputInterface $output) |
|
85 | { |
||
86 | 9 | $this->output = new SymfonyStyle($input, $output); |
|
87 | 9 | $date = (null !== $input->getOption('date')) ? $this->sanitizeDate($input->getOption('date')) : new \DateTime('now'); |
|
88 | 8 | $sources = $this->sanitizeSources($input->getOption('source')); |
|
89 | 7 | $this->output->title(sprintf('Fetching rates for sources "%s" on "%s".', implode('", "', $sources), $date->format('Y-m-d'))); |
|
90 | |||
91 | 7 | $errors = false; |
|
92 | |||
93 | 7 | foreach ($sources as $source) { |
|
|
|||
94 | |||
95 | try { |
||
96 | 7 | $rates = $this->manager->fetch($source, $date); |
|
97 | |||
98 | 7 | if (0 === count($rates)) { |
|
99 | 3 | throw new RuntimeException(sprintf('No rate fetched from source "%s".', $source)); |
|
100 | } |
||
101 | |||
102 | $rows = array_map(function(RateInterface $rate) { |
||
103 | return [ |
||
104 | 6 | $rate->getCurrencyCode(), |
|
105 | 6 | $rate->getRateType(), |
|
106 | 6 | $rate->getValue(), |
|
107 | ]; |
||
108 | 6 | }, $rates); |
|
109 | |||
110 | 6 | $this->output->section(sprintf('Fetched rates for source "%s":', $source)); |
|
111 | 6 | $this->output->table(['Currency code', 'Rate type', 'Value'], $rows); |
|
112 | |||
113 | 6 | View Code Duplication | if (!$input->getOption('silent')) { |
114 | 5 | $this->eventDispatcher->dispatch(FetchEvents::SUCCESS, new GenericEvent($source, ['rates' => $rates])); |
|
115 | } |
||
116 | 3 | } catch (\Exception $e) { |
|
117 | 3 | $this->output->error(sprintf('Could not fetch rates from source "%s" (%s).', $source, $e->getMessage())); |
|
118 | 3 | $errors = true; |
|
119 | |||
120 | 3 | View Code Duplication | if (!$input->getOption('silent')) { |
121 | 2 | $this->eventDispatcher->dispatch(FetchEvents::ERROR, new GenericEvent($source, ['exception' => $e])); |
|
122 | } |
||
123 | } |
||
124 | } |
||
125 | |||
126 | 7 | if ($errors) { |
|
127 | 3 | $this->output->error('Could not fetch all rates.'); |
|
128 | 3 | return -1; |
|
129 | } |
||
130 | |||
131 | 4 | $this->output->success('Rates successfully fetched.'); |
|
132 | 4 | return 0; |
|
133 | } |
||
134 | |||
135 | /** |
||
136 | * Sanitizes a date from console input. |
||
137 | * |
||
138 | * @param string|\DateTime $dateString A date |
||
139 | * |
||
140 | * @return \DateTime |
||
141 | * |
||
142 | * @throws InvalidArgumentException |
||
143 | */ |
||
144 | 2 | protected function sanitizeDate($dateString) |
|
166 | |||
167 | /** |
||
168 | * Clean sources from console input. |
||
169 | * |
||
170 | * @param mixed $sourcesString A sources. |
||
171 | * |
||
172 | * @return array|null |
||
173 | * |
||
174 | * @throws InvalidArgumentException |
||
175 | */ |
||
176 | 8 | protected function sanitizeSources($sourcesString) |
|
199 | } |
||
200 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.