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 | ||
| 9 | class Configuration implements ConfigurationInterface | ||
| 10 | { | ||
| 11 | /** | ||
| 12 | * Stores supported database drivers. | ||
| 13 | * | ||
| 14 | * @var array | ||
| 15 | */ | ||
| 16 |     private $supportedDrivers = array('orm', 'mongodb', 'propel', 'phpcr'); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * If the kernel is running in debug mode. | ||
| 20 | * | ||
| 21 | * @var bool | ||
| 22 | */ | ||
| 23 | private $debug; | ||
| 24 | |||
| 25 | 27 | public function __construct($debug) | |
| 29 | |||
| 30 | /** | ||
| 31 | * Generates the configuration tree. | ||
| 32 | * | ||
| 33 | * @return TreeBuilder | ||
| 34 | */ | ||
| 35 | 27 | public function getConfigTreeBuilder() | |
| 64 | |||
| 65 | /** | ||
| 66 | * Adds the configuration for the "clients" key. | ||
| 67 | */ | ||
| 68 | 27 | private function addClientsSection(ArrayNodeDefinition $rootNode) | |
| 69 |     { | ||
| 70 | $rootNode | ||
| 71 | 27 |             ->fixXmlConfig('client') | |
| 72 | 27 | ->children() | |
| 73 | 27 |                 ->arrayNode('clients') | |
| 74 | 27 |                     ->useAttributeAsKey('id') | |
| 75 | 27 |                     ->prototype('array') | |
| 76 | 27 | ->performNoDeepMerging() | |
| 77 | // BC - Renaming 'servers' node to 'connections' | ||
| 78 | 27 | ->beforeNormalization() | |
| 79 |                         ->ifTrue(function ($v) { return isset($v['servers']); }) | ||
| 80 |                         ->then(function ($v) { | ||
| 81 | $v['connections'] = $v['servers']; | ||
| 82 | unset($v['servers']); | ||
| 83 | |||
| 84 | return $v; | ||
| 85 | 27 | }) | |
| 86 | 27 | ->end() | |
| 87 | // Elastica names its properties with camel case, support both | ||
| 88 | 27 | ->beforeNormalization() | |
| 89 |                         ->ifTrue(function ($v) { return isset($v['connection_strategy']); }) | ||
| 90 |                         ->then(function ($v) { | ||
| 91 | 4 | $v['connectionStrategy'] = $v['connection_strategy']; | |
| 92 | 4 | unset($v['connection_strategy']); | |
| 93 | |||
| 94 | 4 | return $v; | |
| 95 | 27 | }) | |
| 96 | 27 | ->end() | |
| 97 | // If there is no connections array key defined, assume a single connection. | ||
| 98 | 27 | ->beforeNormalization() | |
| 99 |                         ->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v); }) | ||
| 100 |                         ->then(function ($v) { | ||
| 101 | return array( | ||
| 102 | 26 | 'connections' => array($v), | |
| 103 | ); | ||
| 104 | 27 | }) | |
| 105 | 27 | ->end() | |
| 106 | 27 | ->children() | |
| 107 | 27 |                             ->arrayNode('connections') | |
| 108 | 27 | ->requiresAtLeastOneElement() | |
| 109 | 27 |                                 ->prototype('array') | |
| 110 | 27 |                                     ->fixXmlConfig('header') | |
| 111 | 27 | ->children() | |
| 112 | 27 |                                         ->scalarNode('url') | |
| 113 | 27 | ->validate() | |
| 114 |                                                 ->ifTrue(function ($url) { return $url && substr($url, -1) !== '/'; }) | ||
| 115 |                                                 ->then(function ($url) { return $url.'/'; }) | ||
| 116 | 27 | ->end() | |
| 117 | 27 | ->end() | |
| 118 | 27 |                                         ->scalarNode('host')->end() | |
| 119 | 27 |                                         ->scalarNode('port')->end() | |
| 120 | 27 |                                         ->scalarNode('proxy')->end() | |
| 121 | 27 |                                         ->scalarNode('aws_access_key_id')->end() | |
| 122 | 27 |                                         ->scalarNode('aws_secret_access_key')->end() | |
| 123 | 27 |                                         ->scalarNode('aws_region')->end() | |
| 124 | 27 |                                         ->scalarNode('aws_session_token')->end() | |
| 125 | 27 |                                         ->scalarNode('logger') | |
| 126 | 27 | ->defaultValue($this->debug ? 'fos_elastica.logger' : false) | |
| 127 | 27 |                                             ->treatNullLike('fos_elastica.logger') | |
| 128 | 27 |                                             ->treatTrueLike('fos_elastica.logger') | |
| 129 | 27 | ->end() | |
| 130 | 27 |                                         ->booleanNode('compression')->defaultValue(false)->end() | |
| 131 | 27 |                                         ->arrayNode('headers') | |
| 132 | 27 |                                             ->useAttributeAsKey('name') | |
| 133 | 27 |                                             ->prototype('scalar')->end() | |
| 134 | 27 | ->end() | |
| 135 | 27 |                                         ->scalarNode('transport')->end() | |
| 136 | 27 |                                         ->scalarNode('timeout')->end() | |
| 137 | 27 |                                         ->scalarNode('connectTimeout')->end() | |
| 138 | 27 |                                         ->scalarNode('retryOnConflict') | |
| 139 | 27 | ->defaultValue(0) | |
| 140 | 27 | ->end() | |
| 141 | 27 | ->end() | |
| 142 | 27 | ->end() | |
| 143 | 27 | ->end() | |
| 144 | 27 |                             ->scalarNode('timeout')->end() | |
| 145 | 27 |                             ->scalarNode('connectTimeout')->end() | |
| 146 | 27 |                             ->scalarNode('headers')->end() | |
| 147 | 27 |                             ->scalarNode('connectionStrategy')->defaultValue('Simple')->end() | |
| 148 | 27 | ->end() | |
| 149 | 27 | ->end() | |
| 150 | 27 | ->end() | |
| 151 | 27 | ->end() | |
| 152 | ; | ||
| 153 | 27 | } | |
| 154 | |||
| 155 | /** | ||
| 156 | * Adds the configuration for the "indexes" key. | ||
| 157 | */ | ||
| 158 | 27 | private function addIndexesSection(ArrayNodeDefinition $rootNode) | |
| 191 | |||
| 192 | /** | ||
| 193 | * Returns the array node used for "types". | ||
| 194 | */ | ||
| 195 | 27 | protected function getTypesNode() | |
| 286 | |||
| 287 | /** | ||
| 288 | * Returns the array node used for "properties". | ||
| 289 | */ | ||
| 290 | 27 | protected function getPropertiesNode() | |
| 302 | |||
| 303 | /** | ||
| 304 | * Returns the array node used for "dynamic_templates". | ||
| 305 | */ | ||
| 306 | 27 | public function getDynamicTemplateNode() | |
| 333 | |||
| 334 | /** | ||
| 335 | * Returns the array node used for "_id". | ||
| 336 | */ | ||
| 337 | 27 | View Code Duplication | protected function getIdNode() | 
| 350 | |||
| 351 | /** | ||
| 352 | * Returns the array node used for "_source". | ||
| 353 | */ | ||
| 354 | 27 | protected function getSourceNode() | |
| 377 | |||
| 378 | /** | ||
| 379 | * Returns the array node used for "_boost". | ||
| 380 | */ | ||
| 381 | 27 | protected function getBoostNode() | |
| 395 | |||
| 396 | /** | ||
| 397 | * Returns the array node used for "_routing". | ||
| 398 | */ | ||
| 399 | 27 | View Code Duplication | protected function getRoutingNode() | 
| 413 | |||
| 414 | /** | ||
| 415 | * Returns the array node used for "_parent". | ||
| 416 | */ | ||
| 417 | 27 | protected function getParentNode() | |
| 432 | |||
| 433 | /** | ||
| 434 | * Returns the array node used for "_all". | ||
| 435 | */ | ||
| 436 | 27 | protected function getAllNode() | |
| 450 | |||
| 451 | /** | ||
| 452 | * Returns the array node used for "_timestamp". | ||
| 453 | */ | ||
| 454 | 27 | View Code Duplication | protected function getTimestampNode() | 
| 471 | |||
| 472 | /** | ||
| 473 | * Returns the array node used for "_ttl". | ||
| 474 | */ | ||
| 475 | 27 | View Code Duplication | protected function getTtlNode() | 
| 491 | |||
| 492 | /** | ||
| 493 | * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition | ||
| 494 | */ | ||
| 495 | 27 | protected function getPersistenceNode() | |
| 590 | |||
| 591 | /** | ||
| 592 | * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition | ||
| 593 | */ | ||
| 594 | 27 | protected function getSerializerNode() | |
| 614 | } | ||
| 615 | 
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the parent class: