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 |
||
| 20 | * @var string[] |
||
| 21 | */ |
||
| 22 | protected array $whitelist; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * @var string[] |
||
| 26 | */ |
||
| 27 | protected array $blacklist; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * @var array<string, string> |
||
| 31 | */ |
||
| 32 | protected array $map; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * @psalm-param array{case?: int, map?: array<string, string>, blacklist?: string[], whitelist?: string[], join_table_field_suffix?: bool } $configuration |
||
| 36 | * |
||
| 37 | * @throws RuntimeException |
||
| 38 | */ |
||
| 39 | public function __construct(array $configuration = []) |
||
| 40 | { |
||
| 41 | /** |
||
| 42 | * @psalm-var array{case: int, map: array<string, string>, blacklist: string[], whitelist: string[], join_table_field_suffix: bool } $configuration |
||
| 43 | */ |
||
| 44 | $configuration = array_merge([ |
||
| 45 | 'case' => CASE_LOWER, |
||
| 46 | 'map' => [], |
||
| 47 | 'whitelist' => [], |
||
| 48 | 'blacklist' => [], |
||
| 49 | 'join_table_field_suffix' => true, |
||
| 50 | ], $configuration); |
||
| 51 | |||
| 52 | 25 | if (\count($configuration['whitelist']) > 0 && \count($configuration['blacklist']) > 0) { |
|
| 53 | throw new RuntimeException('You can use whitelist or blacklist or none of mentioned lists, but not booth.'); |
||
| 54 | 25 | } |
|
| 55 | 25 | ||
| 56 | $this->case = $configuration['case']; |
||
| 57 | /** |
||
| 58 | * @psalm-suppress MixedPropertyTypeCoercion |
||
| 59 | */ |
||
| 60 | 25 | $this->map = \array_map(\Closure::bind(function (string $prefix) { |
|
| 61 | return $this->underscore($prefix); |
||
| 62 | 25 | }, $this), $configuration['map']); |
|
| 63 | 1 | $this->blacklist = \array_map(static function (string $class) { |
|
| 64 | return \ltrim($class, '\\'); |
||
| 65 | }, $configuration['blacklist']); |
||
| 66 | 24 | $this->whitelist = \array_map(static function (string $class) { |
|
| 67 | 24 | return \ltrim($class, '\\'); |
|
| 68 | 17 | }, $configuration['whitelist']); |
|
| 69 | 24 | ||
| 70 | 24 | $this->joinTableFieldSuffix = $configuration['join_table_field_suffix']; |
|
| 71 | 1 | } |
|
| 72 | 24 | ||
| 73 | 24 | /** |
|
| 74 | 1 | * {@inheritdoc} |
|
| 75 | 24 | */ |
|
| 76 | 24 | public function classToTableName($className): string |
|
| 77 | 24 | { |
|
| 78 | $prefix = $this->getTableNamePrefix($className); |
||
| 79 | $position = \strrpos($className, '\\'); |
||
| 80 | |||
| 81 | if (false !== $position) { |
||
| 82 | 15 | $className = \substr($className, ($position + 1)); |
|
| 83 | } |
||
| 84 | 15 | ||
| 85 | return $prefix . $this->underscore($className); |
||
| 86 | 15 | } |
|
| 87 | 15 | ||
| 88 | /** |
||
| 89 | * {@inheritdoc} |
||
| 90 | 15 | */ |
|
| 91 | public function propertyToColumnName($propertyName, $className = null): string |
||
| 92 | { |
||
| 93 | return $this->underscore($propertyName); |
||
| 94 | } |
||
| 95 | |||
| 96 | 7 | /** |
|
| 97 | * {@inheritdoc} |
||
| 98 | 7 | */ |
|
| 99 | public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null): string |
||
| 100 | { |
||
| 101 | return $this->underscore($propertyName) . '_' . $this->underscore($embeddedColumnName); |
||
| 102 | } |
||
| 103 | |||
| 104 | 3 | /** |
|
| 105 | * {@inheritdoc} |
||
| 106 | 3 | */ |
|
| 107 | public function referenceColumnName(): string |
||
| 108 | { |
||
| 109 | return $this->case === CASE_UPPER ? 'ID' : 'id'; |
||
| 110 | } |
||
| 111 | |||
| 112 | 7 | /** |
|
| 113 | * {@inheritdoc} |
||
| 114 | 7 | */ |
|
| 115 | public function joinColumnName($propertyName): string |
||
| 116 | { |
||
| 117 | return $this->underscore($propertyName) . '_' . $this->referenceColumnName(); |
||
| 118 | } |
||
| 119 | |||
| 120 | 3 | /** |
|
| 121 | * {@inheritdoc} |
||
| 122 | 3 | */ |
|
| 123 | public function joinTableName($sourceEntity, $targetEntity, $propertyName = null): string |
||
| 124 | { |
||
| 125 | $tableName = $this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity); |
||
| 126 | $suffix = $this->joinTableFieldSuffix && null !== $propertyName ? '_' . $this->propertyToColumnName($propertyName, $sourceEntity) : ''; |
||
| 127 | |||
| 128 | 3 | return $tableName . $suffix; |
|
| 129 | } |
||
| 130 | 3 | ||
| 131 | /** |
||
| 132 | * {@inheritdoc} |
||
| 133 | */ |
||
| 134 | public function joinKeyColumnName($entityName, $referencedColumnName = null): string |
||
| 135 | 3 | { |
|
| 136 | return $this->classToTableName($entityName) . '_' . |
||
| 137 | ($referencedColumnName ? $this->underscore($referencedColumnName) : $this->referenceColumnName()); |
||
| 138 | } |
||
| 139 | |||
| 140 | private function getTableNamePrefix(string $className): string |
||
| 141 | 2 | { |
|
| 142 | $className = \ltrim($className, '\\'); |
||
| 143 | 2 | ||
| 144 | 2 | foreach ($this->blacklist as $blacklist) { |
|
| 145 | if (0 === \strpos($className, $blacklist)) { |
||
| 146 | return ''; |
||
| 147 | } |
||
| 148 | } |
||
| 149 | |||
| 150 | foreach ($this->map as $namespace => $prefix) { |
||
| 151 | if (0 === \strpos($className, $namespace)) { |
||
| 152 | foreach ($this->whitelist as $whitelistedNamespace) { |
||
| 153 | 15 | if (0 === \strpos($className, $whitelistedNamespace)) { |
|
| 154 | return $prefix . '_'; |
||
| 155 | 15 | } |
|
| 156 | } |
||
| 157 | 15 | ||
| 158 | return 0 === \count($this->whitelist) ? ($prefix . '_') : ''; |
||
| 159 | 1 | } |
|
| 160 | 1 | } |
|
| 161 | |||
| 162 | return ''; |
||
| 163 | } |
||
| 164 | 14 | ||
| 165 | private function underscore(string $literal): string |
||
| 166 | 14 | { |
|
| 167 | /** @var string $literal */ |
||
| 168 | 14 | $literal = \preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $literal); |
|
| 169 | |||
| 170 | 1 | if (CASE_UPPER === $this->case) { |
|
| 171 | 1 | return \strtoupper($literal); |
|
| 172 | } |
||
| 173 | |||
| 174 | return \strtolower($literal); |
||
| 175 | 14 | } |
|
| 176 | } |
||
| 177 |