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 | abstract class DataFormatter extends Object { |
||
10 | |||
11 | /** |
||
12 | * Set priority from 0-100. |
||
13 | * If multiple formatters for the same extension exist, |
||
14 | * we select the one with highest priority. |
||
15 | * |
||
16 | * @var int |
||
17 | */ |
||
18 | private static $priority = 50; |
||
19 | |||
20 | /** |
||
21 | * Follow relations for the {@link DataObject} instances |
||
22 | * ($has_one, $has_many, $many_many). |
||
23 | * Set to "0" to disable relation output. |
||
24 | * |
||
25 | * @todo Support more than one nesting level |
||
26 | * |
||
27 | * @var int |
||
28 | */ |
||
29 | public $relationDepth = 1; |
||
30 | |||
31 | /** |
||
32 | * Allows overriding of the fields which are rendered for the |
||
33 | * processed dataobjects. By default, this includes all |
||
34 | * fields in {@link DataObject::inheritedDatabaseFields()}. |
||
35 | * |
||
36 | * @var array |
||
37 | */ |
||
38 | protected $customFields = null; |
||
39 | |||
40 | /** |
||
41 | * Allows addition of fields |
||
42 | * (e.g. custom getters on a DataObject) |
||
43 | * |
||
44 | * @var array |
||
45 | */ |
||
46 | protected $customAddFields = null; |
||
47 | |||
48 | /** |
||
49 | * Allows to limit or add relations. |
||
50 | * Only use in combination with {@link $relationDepth}. |
||
51 | * By default, all relations will be shown. |
||
52 | * |
||
53 | * @var array |
||
54 | */ |
||
55 | protected $customRelations = null; |
||
56 | |||
57 | /** |
||
58 | * Fields which should be expicitly excluded from the export. |
||
59 | * Comes in handy for field-level permissions. |
||
60 | * Will overrule both {@link $customAddFields} and {@link $customFields} |
||
61 | * |
||
62 | * @var array |
||
63 | */ |
||
64 | protected $removeFields = null; |
||
65 | |||
66 | /** |
||
67 | * Specifies the mimetype in which all strings |
||
68 | * returned from the convert*() methods should be used, |
||
69 | * e.g. "text/xml". |
||
70 | * |
||
71 | * @var string |
||
72 | */ |
||
73 | protected $outputContentType = null; |
||
74 | |||
75 | /** |
||
76 | * Used to set totalSize properties on the output |
||
77 | * of {@link convertDataObjectSet()}, shows the |
||
78 | * total number of records without the "limit" and "offset" |
||
79 | * GET parameters. Useful to implement pagination. |
||
80 | * |
||
81 | * @var int |
||
82 | */ |
||
83 | protected $totalSize; |
||
84 | |||
85 | /** |
||
86 | * Get a DataFormatter object suitable for handling the given file extension. |
||
87 | * |
||
88 | * @param string $extension |
||
89 | * @return DataFormatter |
||
90 | */ |
||
91 | View Code Duplication | public static function for_extension($extension) { |
|
106 | |||
107 | /** |
||
108 | * Get formatter for the first matching extension. |
||
109 | * |
||
110 | * @param array $extensions |
||
111 | * @return DataFormatter |
||
112 | */ |
||
113 | public static function for_extensions($extensions) { |
||
114 | foreach($extensions as $extension) { |
||
115 | if($formatter = self::for_extension($extension)) return $formatter; |
||
116 | } |
||
117 | |||
118 | return false; |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * Get a DataFormatter object suitable for handling the given mimetype. |
||
123 | * |
||
124 | * @param string $mimeType |
||
125 | * @return DataFormatter |
||
126 | */ |
||
127 | View Code Duplication | public static function for_mimetype($mimeType) { |
|
142 | |||
143 | /** |
||
144 | * Get formatter for the first matching mimetype. |
||
145 | * Useful for HTTP Accept headers which can contain |
||
146 | * multiple comma-separated mimetypes. |
||
147 | * |
||
148 | * @param array $mimetypes |
||
149 | * @return DataFormatter |
||
150 | */ |
||
151 | public static function for_mimetypes($mimetypes) { |
||
152 | foreach($mimetypes as $mimetype) { |
||
153 | if($formatter = self::for_mimetype($mimetype)) return $formatter; |
||
154 | } |
||
155 | |||
156 | return false; |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * @param array $fields |
||
161 | */ |
||
162 | public function setCustomFields($fields) { |
||
165 | |||
166 | /** |
||
167 | * @return array |
||
168 | */ |
||
169 | public function getCustomFields() { |
||
172 | |||
173 | /** |
||
174 | * @param array $fields |
||
175 | */ |
||
176 | public function setCustomAddFields($fields) { |
||
179 | |||
180 | /** |
||
181 | * @param array $relations |
||
182 | */ |
||
183 | public function setCustomRelations($relations) { |
||
186 | |||
187 | /** |
||
188 | * @return array |
||
189 | */ |
||
190 | public function getCustomRelations() { |
||
193 | |||
194 | /** |
||
195 | * @return array |
||
196 | */ |
||
197 | public function getCustomAddFields() { |
||
200 | |||
201 | /** |
||
202 | * @param array $fields |
||
203 | */ |
||
204 | public function setRemoveFields($fields) { |
||
207 | |||
208 | /** |
||
209 | * @return array |
||
210 | */ |
||
211 | public function getRemoveFields() { |
||
214 | |||
215 | public function getOutputContentType() { |
||
218 | |||
219 | /** |
||
220 | * @param int $size |
||
221 | */ |
||
222 | public function setTotalSize($size) { |
||
225 | |||
226 | /** |
||
227 | * @return int |
||
228 | */ |
||
229 | public function getTotalSize() { |
||
232 | |||
233 | /** |
||
234 | * Returns all fields on the object which should be shown |
||
235 | * in the output. Can be customised through {@link self::setCustomFields()}. |
||
236 | * |
||
237 | * @todo Allow for custom getters on the processed object (currently filtered through inheritedDatabaseFields) |
||
238 | * @todo Field level permission checks |
||
239 | * |
||
240 | * @param DataObject $obj |
||
241 | * @return array |
||
242 | */ |
||
243 | protected function getFieldsForObj($obj) { |
||
244 | $dbFields = array(); |
||
245 | |||
246 | // if custom fields are specified, only select these |
||
247 | if(is_array($this->customFields)) { |
||
248 | View Code Duplication | foreach($this->customFields as $fieldName) { |
|
249 | // @todo Possible security risk by making methods accessible - implement field-level security |
||
250 | if($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) { |
||
251 | $dbFields[$fieldName] = $fieldName; |
||
252 | } |
||
253 | } |
||
254 | } else { |
||
255 | // by default, all database fields are selected |
||
256 | $dbFields = $obj->inheritedDatabaseFields(); |
||
257 | } |
||
258 | |||
259 | if(is_array($this->customAddFields)) { |
||
260 | View Code Duplication | foreach($this->customAddFields as $fieldName) { |
|
261 | // @todo Possible security risk by making methods accessible - implement field-level security |
||
262 | if($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) { |
||
263 | $dbFields[$fieldName] = $fieldName; |
||
264 | } |
||
265 | } |
||
266 | } |
||
267 | |||
268 | // add default required fields |
||
269 | $dbFields = array_merge($dbFields, array('ID'=>'Int')); |
||
270 | |||
271 | if(is_array($this->removeFields)) { |
||
272 | $dbFields = array_diff_key($dbFields, array_combine($this->removeFields,$this->removeFields)); |
||
273 | } |
||
274 | |||
275 | return $dbFields; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Return an array of the extensions that this data formatter supports |
||
280 | */ |
||
281 | abstract public function supportedExtensions(); |
||
282 | |||
283 | abstract public function supportedMimeTypes(); |
||
284 | |||
285 | |||
286 | /** |
||
287 | * Convert a single data object to this format. Return a string. |
||
288 | */ |
||
289 | abstract public function convertDataObject(DataObjectInterface $do); |
||
290 | |||
291 | /** |
||
292 | * Convert a data object set to this format. Return a string. |
||
293 | */ |
||
294 | abstract public function convertDataObjectSet(SS_List $set); |
||
295 | |||
296 | /** |
||
297 | * @param string $strData HTTP Payload as string |
||
298 | */ |
||
299 | public function convertStringToArray($strData) { |
||
302 | |||
303 | } |
||
304 |
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.