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.