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  | 
            ||
| 8 | class YamlExtractor implements ExtractorInterface  | 
            ||
| 9 | { | 
            ||
| 10 | /**  | 
            ||
| 11 | * Path to the YAML file  | 
            ||
| 12 | * @var string  | 
            ||
| 13 | */  | 
            ||
| 14 | protected $file;  | 
            ||
| 15 | |||
| 16 | /**  | 
            ||
| 17 | * Parsed configuration  | 
            ||
| 18 | * @var array  | 
            ||
| 19 | */  | 
            ||
| 20 | protected $config;  | 
            ||
| 21 | |||
| 22 | /**  | 
            ||
| 23 | * YAML parser  | 
            ||
| 24 | * @var callable  | 
            ||
| 25 | */  | 
            ||
| 26 | public $parser = [Yaml::class, 'parse'];  | 
            ||
| 27 | |||
| 28 | /**  | 
            ||
| 29 | * @param array $options  | 
            ||
| 30 | */  | 
            ||
| 31 | 11 | View Code Duplication | public function __construct(array $options)  | 
            
| 32 |     { | 
            ||
| 33 | 11 |         if (empty($options['file'])) { | 
            |
| 34 | 2 |             throw new \InvalidArgumentException('YAML file path missing'); | 
            |
| 35 | }  | 
            ||
| 36 | |||
| 37 | 11 |         if (!file_exists($options['file'])) { | 
            |
| 38 | 1 |             throw new \InvalidArgumentException('YAML file missing'); | 
            |
| 39 | }  | 
            ||
| 40 | |||
| 41 | 11 | $this->file = $options['file'];  | 
            |
| 42 | 11 | }  | 
            |
| 43 | |||
| 44 | /**  | 
            ||
| 45 |      * {@inheritdoc} | 
            ||
| 46 | */  | 
            ||
| 47 | 2 | public function getConstructorInjections($fqcn)  | 
            |
| 48 |     { | 
            ||
| 49 | 2 | $config = $this->getConfig($fqcn);  | 
            |
| 50 | |||
| 51 | 2 |         if (!isset($config['constructor'])) { | 
            |
| 52 | 1 | return null;  | 
            |
| 53 | }  | 
            ||
| 54 | |||
| 55 | 1 | $injections = new InjectParams();  | 
            |
| 56 | 1 |         foreach ($config['constructor'] as $spec) { | 
            |
| 57 | 1 | $injections->value[] = $this->createInjectionObject($spec);  | 
            |
| 58 | 1 | }  | 
            |
| 59 | |||
| 60 | 1 | return $injections;  | 
            |
| 61 | }  | 
            ||
| 62 | |||
| 63 | /**  | 
            ||
| 64 |      * {@inheritdoc} | 
            ||
| 65 | */  | 
            ||
| 66 | 2 | View Code Duplication | public function getMethodsInjections($fqcn)  | 
            
| 83 | |||
| 84 | /**  | 
            ||
| 85 |      * {@inheritdoc} | 
            ||
| 86 | */  | 
            ||
| 87 | 2 | View Code Duplication | public function getPropertiesInjections($fqcn)  | 
            
| 88 |     { | 
            ||
| 89 | 2 | $config = $this->getConfig($fqcn);  | 
            |
| 90 | |||
| 91 | 2 |         if (!isset($config['properties'])) { | 
            |
| 92 | 1 | return [];  | 
            |
| 93 | }  | 
            ||
| 94 | |||
| 95 | 1 | $injections = [];  | 
            |
| 96 | 1 |         foreach ($config['properties'] as $propertyName => $injection) { | 
            |
| 97 | 1 | $injections[$propertyName] = $this->createInjectionObject($injection);  | 
            |
| 98 | 1 | }  | 
            |
| 99 | |||
| 100 | 1 | return $injections;  | 
            |
| 101 | }  | 
            ||
| 102 | |||
| 103 | /**  | 
            ||
| 104 |      * {@inheritdoc} | 
            ||
| 105 | */  | 
            ||
| 106 | 1 | public function getChangeSet($fqcn)  | 
            |
| 107 |     { | 
            ||
| 108 | 1 | $config = $this->getConfig($fqcn);  | 
            |
| 109 | 1 |         if (!empty($config['fqcn'])) { | 
            |
| 110 | 1 | $fqcn = $config['fqcn'];  | 
            |
| 111 | 1 | }  | 
            |
| 112 | |||
| 113 | 1 | return new ChangeSet($this, $fqcn);  | 
            |
| 114 | }  | 
            ||
| 115 | |||
| 116 | /**  | 
            ||
| 117 | * @param string $fqcn  | 
            ||
| 118 | * @return array  | 
            ||
| 119 | */  | 
            ||
| 120 | 8 | public function getConfig($fqcn)  | 
            |
| 121 |     { | 
            ||
| 122 | 8 |         if (!$this->config) { | 
            |
| 123 | 8 | $config = call_user_func($this->parser, $this->file);  | 
            |
| 124 | 8 | $this->config = $config[$fqcn];  | 
            |
| 125 | 8 | }  | 
            |
| 126 | |||
| 127 | 8 | return $this->config;  | 
            |
| 128 | }  | 
            ||
| 129 | |||
| 130 | /**  | 
            ||
| 131 | * @param array $spec  | 
            ||
| 132 | * @return AnnotationInterface  | 
            ||
| 133 | */  | 
            ||
| 134 | 3 | private function createInjectionObject(array $spec)  | 
            |
| 135 |     { | 
            ||
| 136 | 3 | $injectionFqcn = $spec['type'];  | 
            |
| 137 | 3 | $injection = new $injectionFqcn;  | 
            |
| 138 | 3 | unset($spec['type']);  | 
            |
| 139 | |||
| 140 | 3 |         foreach ($spec as $property => $value) { | 
            |
| 141 | 3 | $injection->$property = $value;  | 
            |
| 142 | 3 | }  | 
            |
| 143 | |||
| 144 | 3 | return $injection;  | 
            |
| 145 | }  | 
            ||
| 146 | |||
| 147 | /**  | 
            ||
| 148 | * @param callable $parser  | 
            ||
| 149 | */  | 
            ||
| 150 | 1 | public function setParser($parser)  | 
            |
| 154 | }  | 
            ||
| 155 |