Complex classes like PicoTwigExtension 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 PicoTwigExtension, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | class PicoTwigExtension extends Twig_Extension |
||
12 | { |
||
13 | /** |
||
14 | * Current instance of Pico |
||
15 | * |
||
16 | * @see PicoTwigExtension::getPico() |
||
17 | * @var Pico |
||
18 | */ |
||
19 | private $pico; |
||
20 | |||
21 | /** |
||
22 | * Constructs a new instance of this Twig extension |
||
23 | * |
||
24 | * @param Pico $pico current instance of Pico |
||
25 | */ |
||
26 | public function __construct(Pico $pico) |
||
30 | |||
31 | /** |
||
32 | * Returns the extensions instance of Pico |
||
33 | * |
||
34 | * @see Pico |
||
35 | * @return Pico the extensions instance of Pico |
||
36 | */ |
||
37 | public function getPico() |
||
41 | |||
42 | /** |
||
43 | * Returns the name of the extension |
||
44 | * |
||
45 | * @see Twig_ExtensionInterface::getName() |
||
46 | * @return string the extension name |
||
47 | */ |
||
48 | public function getName() |
||
52 | |||
53 | /** |
||
54 | * Returns the Twig filters markdown, map and sort_by |
||
55 | * |
||
56 | * @see Twig_ExtensionInterface::getFilters() |
||
57 | * @return Twig_SimpleFilter[] array of Picos Twig filters |
||
58 | */ |
||
59 | public function getFilters() |
||
67 | |||
68 | /** |
||
69 | * Parses a markdown string to HTML |
||
70 | * |
||
71 | * This method is registered as the Twig `markdown` filter. You can use it |
||
72 | * to e.g. parse a meta variable (`{{ meta.description|markdown }}`). |
||
73 | * Don't use it to parse the contents of a page, use the `content` filter |
||
74 | * instead, what ensures the proper preparation of the contents. |
||
75 | * |
||
76 | * @param string $markdown markdown to parse |
||
77 | * @return string parsed HTML |
||
78 | */ |
||
79 | public function markdownFilter($markdown) |
||
90 | |||
91 | /** |
||
92 | * Returns a array with the values of the given key or key path |
||
93 | * |
||
94 | * This method is registered as the Twig `map` filter. You can use this |
||
95 | * filter to e.g. get all page titles (`{{ pages|map("title") }}`). |
||
96 | * |
||
97 | * @param array|Traversable $var variable to map |
||
98 | * @param mixed $mapKeyPath key to map; either a scalar or a |
||
99 | * array interpreted as key path (i.e. ['foo', 'bar'] will return all |
||
100 | * $item['foo']['bar'] values) |
||
101 | * @return array mapped values |
||
102 | */ |
||
103 | public function mapFilter($var, $mapKeyPath) |
||
119 | |||
120 | /** |
||
121 | * Sorts an array by one of its keys or a arbitrary deep sub-key |
||
122 | * |
||
123 | * This method is registered as the Twig `sort_by` filter. You can use this |
||
124 | * filter to e.g. sort the pages array by a arbitrary meta value. Calling |
||
125 | * `{{ pages|sort_by("meta:nav"|split(":")) }}` returns all pages sorted by |
||
126 | * the meta value `nav`. Please note the `"meta:nav"|split(":")` part of |
||
127 | * the example. The sorting algorithm will never assume equality of two |
||
128 | * values, it will then fall back to the original order. The result is |
||
129 | * always sorted in ascending order, apply Twigs `reverse` filter to |
||
130 | * achieve a descending order. |
||
131 | * |
||
132 | * @param array|Traversable $var variable to sort |
||
133 | * @param mixed $sortKeyPath key to use for sorting; either |
||
134 | * a scalar or a array interpreted as key path (i.e. ['foo', 'bar'] |
||
135 | * will sort $var by $item['foo']['bar']) |
||
136 | * @param string $fallback specify what to do with items |
||
137 | * which don't contain the specified sort key; use "bottom" (default) |
||
138 | * to move those items to the end of the sorted array, "top" to rank |
||
139 | * them first, or "keep" to keep the original order of those items |
||
140 | * @return array sorted array |
||
141 | */ |
||
142 | public function sortByFilter($var, $sortKeyPath, $fallback = 'bottom') |
||
185 | |||
186 | /** |
||
187 | * Returns the value of a variable item specified by a scalar key or a |
||
188 | * arbitrary deep sub-key using a key path |
||
189 | * |
||
190 | * @param array|Traversable|ArrayAccess|object $var base variable |
||
191 | * @param mixed $keyPath scalar key or a |
||
192 | * array interpreted as key path (when passing e.g. ['foo', 'bar'], |
||
193 | * the method will return $var['foo']['bar']) specifying the value |
||
194 | * @return mixed the requested |
||
195 | * value or NULL when the given key or key path didn't match |
||
196 | */ |
||
197 | public static function getKeyOfVar($var, $keyPath) |
||
238 | } |
||
239 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.