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 |
||
13 | abstract class Entity |
||
14 | { |
||
15 | |||
16 | /** |
||
17 | * @var array Columns |
||
18 | */ |
||
19 | protected static $columns = []; |
||
20 | |||
21 | /** |
||
22 | * @var array Internal entity properties |
||
23 | */ |
||
24 | protected $properties = []; |
||
25 | |||
26 | /** |
||
27 | * Get entity columns |
||
28 | * |
||
29 | * @return array Entity columns |
||
30 | */ |
||
31 | 1 | public static function getColumns() |
|
32 | { |
||
33 | 1 | $columns = array_merge([ |
|
34 | 'id' => 'integer' |
||
35 | 1 | ], static::$columns); |
|
36 | |||
37 | 1 | $columns = array_merge($columns, [ |
|
38 | 1 | 'modified' => 'DateTime', |
|
39 | 'created' => 'DateTime' |
||
40 | ]); |
||
41 | |||
42 | 1 | return $columns; |
|
43 | } |
||
44 | |||
45 | /** |
||
46 | * Set entity property |
||
47 | * |
||
48 | * @param string $key Entity property name |
||
49 | * @param object|integer|double|string|array|boolean|null $value Entity property value |
||
50 | * |
||
51 | * @throws InvalidEntityPropertyException If entity does not have that property |
||
52 | * @throws InvalidValueTypeForEntityPropertyException If value is of the wrong type |
||
53 | */ |
||
54 | 2 | View Code Duplication | public function set($key, $value) |
62 | |||
63 | /** |
||
64 | * Get entity property |
||
65 | * |
||
66 | * @param string $key Entity property |
||
67 | * |
||
68 | * @return object|integer|double|string|array|boolean|null Entity property value for given key |
||
69 | * |
||
70 | * @throws InvalidEntityPropertyException If entity does not have that property |
||
71 | */ |
||
72 | 2 | View Code Duplication | public function get($key) |
80 | |||
81 | /** |
||
82 | * Return table structur for saving |
||
83 | * Example: `['table_field_name' => $this->fieldName]` |
||
84 | * |
||
85 | * @param bool $includeId Should the ID column be included |
||
86 | * |
||
87 | * @return array |
||
88 | */ |
||
89 | 2 | public function toArray($includeId) |
|
90 | { |
||
91 | 2 | $array = []; |
|
92 | |||
93 | 2 | foreach (self::getColumns() as $column => $type) { |
|
94 | 2 | if ($column === 'id' && !$includeId) { |
|
95 | 1 | continue; |
|
96 | } |
||
97 | |||
98 | 2 | $property = new EntityProperty($type); |
|
99 | |||
100 | 2 | $value = $this->get($column); |
|
101 | 2 | $value = $property->formatValueForDatabase($value); |
|
102 | |||
103 | 2 | $array[":$column"] = $value; |
|
104 | } |
||
105 | |||
106 | 2 | return $array; |
|
107 | } |
||
108 | |||
109 | /** |
||
110 | * Validate property for given value |
||
111 | * |
||
112 | * @param string $key Entity property name |
||
113 | * @param mixed $value Entity property value |
||
114 | * |
||
115 | * @return object|integer|double|string|array|boolean|null Value |
||
116 | * |
||
117 | * @throws InvalidEntityPropertyException If entity does not have that property |
||
118 | * @throws InvalidValueTypeForEntityPropertyException If value is of the wrong type |
||
119 | */ |
||
120 | 3 | protected function validatePropertyForValue($key, $value) |
|
121 | { |
||
122 | 3 | if (!isset(self::getColumns()[$key])) { |
|
123 | 1 | throw new InvalidEntityPropertyException([get_class($this), $key]); |
|
124 | } |
||
125 | |||
126 | /** |
||
127 | * Allow NULL |
||
128 | */ |
||
129 | 3 | if ($value !== null) { |
|
130 | 3 | $valueType = gettype($value); |
|
131 | |||
132 | /** |
||
133 | * Get class if object |
||
134 | */ |
||
135 | 3 | if ($valueType === 'object') { |
|
136 | 3 | $valueType = get_class($value); |
|
137 | } |
||
138 | |||
139 | /** |
||
140 | * Handle alias types |
||
141 | */ |
||
142 | 3 | $columnType = self::getColumns()[$key]; |
|
143 | |||
144 | switch ($columnType) { |
||
145 | 3 | case 'Date': |
|
146 | 3 | $columnType = 'DateTime'; |
|
147 | 3 | break; |
|
148 | } |
||
149 | |||
150 | /** |
||
151 | * Validate type |
||
152 | */ |
||
153 | 3 | if ($valueType !== $columnType) { |
|
154 | 1 | throw new InvalidValueTypeForEntityPropertyException([ |
|
155 | 1 | get_class($this), |
|
156 | 1 | $key, |
|
157 | 1 | $valueType, |
|
158 | 1 | $columnType |
|
159 | ]); |
||
160 | } |
||
161 | } |
||
162 | |||
163 | 3 | return $value; |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * @param int $id |
||
168 | * @param \DateTime $modified |
||
169 | * @param \DateTime $created |
||
170 | */ |
||
171 | public function __construct($id, \DateTime $modified, \DateTime $created) |
||
177 | } |
||
178 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.