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:
Complex classes like FrontMatterDocument often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use FrontMatterDocument, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
20 | abstract class FrontMatterDocument extends PermalinkDocument implements |
||
|
|||
21 | \ArrayAccess, |
||
22 | JailedDocumentInterface, |
||
23 | WritableDocumentInterface |
||
24 | { |
||
25 | const TEMPLATE = "---\n%s\n---\n\n%s"; |
||
26 | |||
27 | /** |
||
28 | * The names of FrontMatter keys that are specially defined for all Documents |
||
29 | * |
||
30 | * @var array |
||
31 | */ |
||
32 | public static $specialFrontMatterKeys = array( |
||
33 | 'filename', 'basename' |
||
34 | ); |
||
35 | |||
36 | protected static $whiteListFunctions = array( |
||
37 | 'getPermalink', 'getRedirects', 'getTargetFile', 'getName', 'getFilePath', 'getRelativeFilePath', 'getContent', |
||
38 | 'getExtension', 'getFrontMatter' |
||
39 | ); |
||
40 | |||
41 | /** |
||
42 | * An array to keep track of collection or data dependencies used inside of a Twig template. |
||
43 | * |
||
44 | * $dataDependencies['collections'] = array() |
||
45 | * $dataDependencies['data'] = array() |
||
46 | * |
||
47 | * @var array |
||
48 | */ |
||
49 | protected $dataDependencies; |
||
50 | |||
51 | /** |
||
52 | * FrontMatter values that can be injected or set after the file has been parsed. Values in this array will take |
||
53 | * precedence over values in $frontMatter. |
||
54 | * |
||
55 | * @var array |
||
56 | */ |
||
57 | protected $writableFrontMatter; |
||
58 | |||
59 | /** |
||
60 | * A list of Front Matter values that should not be returned directly from the $frontMatter array. Values listed |
||
61 | * here have dedicated functions that handle those Front Matter values and the respective functions should be called |
||
62 | * instead. |
||
63 | * |
||
64 | * @var string[] |
||
65 | */ |
||
66 | protected $frontMatterBlacklist; |
||
67 | |||
68 | /** |
||
69 | * Set to true if the front matter has already been evaluated with variable interpolation. |
||
70 | * |
||
71 | * @var bool |
||
72 | */ |
||
73 | protected $frontMatterEvaluated; |
||
74 | |||
75 | /** |
||
76 | * @var Parser |
||
77 | */ |
||
78 | protected $frontMatterParser; |
||
79 | |||
80 | /** |
||
81 | * An array containing the Yaml of the file. |
||
82 | * |
||
83 | * @var array |
||
84 | */ |
||
85 | protected $frontMatter; |
||
86 | |||
87 | /** |
||
88 | * Set to true if the body has already been parsed as markdown or any other format. |
||
89 | * |
||
90 | * @var bool |
||
91 | */ |
||
92 | protected $bodyContentEvaluated; |
||
93 | |||
94 | /** |
||
95 | * Only the body of the file, i.e. the content. |
||
96 | * |
||
97 | * @var string |
||
98 | */ |
||
99 | protected $bodyContent; |
||
100 | |||
101 | /** |
||
102 | * The number of lines that Twig template errors should offset. |
||
103 | * |
||
104 | * @var int |
||
105 | */ |
||
106 | private $lineOffset; |
||
107 | |||
108 | /** |
||
109 | * ContentItem constructor. |
||
110 | * |
||
111 | * @param string $filePath The path to the file that will be parsed into a ContentItem |
||
112 | * |
||
113 | * @throws FileNotFoundException The given file path does not exist |
||
114 | * @throws IOException The file was not a valid ContentItem. This would meam there was no front matter or |
||
115 | * no body |
||
116 | */ |
||
117 | 116 | public function __construct($filePath) |
|
124 | |||
125 | /** |
||
126 | * Return the body of the Content Item. |
||
127 | * |
||
128 | * @return string |
||
129 | */ |
||
130 | abstract public function getContent(); |
||
131 | |||
132 | /** |
||
133 | * The number of lines that are taken up by FrontMatter and white space. |
||
134 | * |
||
135 | * @return int |
||
136 | */ |
||
137 | final public function getLineOffset() |
||
141 | |||
142 | /** |
||
143 | * Get the name of the item, which is just the filename without the extension. |
||
144 | * |
||
145 | * @return string |
||
146 | */ |
||
147 | 62 | final public function getName() |
|
151 | |||
152 | /** |
||
153 | * Check whether this object has a reference to a collection or data item. |
||
154 | * |
||
155 | * @param string $namespace 'collections' or 'data' |
||
156 | * @param string $needle |
||
157 | * |
||
158 | * @return bool |
||
159 | */ |
||
160 | final public function hasTwigDependency($namespace, $needle) |
||
164 | |||
165 | /** |
||
166 | * Read the file, and parse its contents. |
||
167 | */ |
||
168 | 115 | final public function refreshFileContent() |
|
220 | |||
221 | /** |
||
222 | * Get all of the references to either DataItems or ContentItems inside of given string. |
||
223 | * |
||
224 | * @param string $filter 'collections' or 'data' |
||
225 | */ |
||
226 | 104 | private function findTwigDataDependencies($filter) |
|
235 | |||
236 | // |
||
237 | // Permalink and redirect functionality |
||
238 | // |
||
239 | |||
240 | 39 | final protected function buildPermalink() |
|
267 | |||
268 | // |
||
269 | // WritableFrontMatter Implementation |
||
270 | // |
||
271 | |||
272 | /** |
||
273 | * {@inheritdoc} |
||
274 | */ |
||
275 | 7 | final public function evaluateFrontMatter($variables = null) |
|
283 | |||
284 | /** |
||
285 | * {@inheritdoc} |
||
286 | */ |
||
287 | 29 | final public function getFrontMatter($evaluateYaml = true) |
|
300 | |||
301 | /** |
||
302 | * {@inheritdoc} |
||
303 | */ |
||
304 | 2 | final public function hasExpandedFrontMatter() |
|
308 | |||
309 | /** |
||
310 | * {@inheritdoc. |
||
311 | */ |
||
312 | final public function appendFrontMatter(array $frontMatter) |
||
319 | |||
320 | /** |
||
321 | * {@inheritdoc. |
||
322 | */ |
||
323 | final public function deleteFrontMatter($key) |
||
332 | |||
333 | /** |
||
334 | * {@inheritdoc. |
||
335 | */ |
||
336 | 2 | final public function setFrontMatter(array $frontMatter) |
|
345 | |||
346 | /** |
||
347 | * Evaluate an array of data for FrontMatter variables. This function will modify the array in place. |
||
348 | * |
||
349 | * @param array $yaml An array of data containing FrontMatter variables |
||
350 | * |
||
351 | * @throws YamlVariableUndefinedException A FrontMatter variable used does not exist |
||
352 | */ |
||
353 | 30 | private function evaluateYaml(&$yaml) |
|
368 | |||
369 | // |
||
370 | // ArrayAccess Implementation |
||
371 | // |
||
372 | |||
373 | /** |
||
374 | * {@inheritdoc} |
||
375 | */ |
||
376 | public function offsetSet($offset, $value) |
||
385 | |||
386 | /** |
||
387 | * {@inheritdoc} |
||
388 | */ |
||
389 | 31 | public function offsetExists($offset) |
|
400 | |||
401 | /** |
||
402 | * {@inheritdoc} |
||
403 | */ |
||
404 | public function offsetUnset($offset) |
||
408 | |||
409 | /** |
||
410 | * {@inheritdoc} |
||
411 | */ |
||
412 | 48 | public function offsetGet($offset) |
|
433 | } |
||
434 |
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.