Complex classes like Host 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 Host, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
24 | class Host extends AbstractHierarchicalComponent implements HostInterface |
||
25 | { |
||
26 | use HostIpTrait; |
||
27 | |||
28 | use HostnameInfoTrait; |
||
29 | |||
30 | use HostnameTrait; |
||
31 | |||
32 | /** |
||
33 | * HierarchicalComponent delimiter |
||
34 | * |
||
35 | * @var string |
||
36 | */ |
||
37 | protected static $separator = '.'; |
||
38 | |||
39 | /** |
||
40 | * Host literal representation |
||
41 | * |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $host; |
||
45 | |||
46 | /** |
||
47 | * Return a new instance when needed |
||
48 | * |
||
49 | * @param array $data |
||
50 | * |
||
51 | * @return static |
||
52 | */ |
||
53 | 45 | protected function newCollectionInstance(array $data) |
|
57 | |||
58 | /** |
||
59 | * return a new instance from an array or a traversable object |
||
60 | * |
||
61 | * @param \Traversable|string[] $data The segments list |
||
62 | * @param int $type one of the constant IS_ABSOLUTE or IS_RELATIVE |
||
63 | * |
||
64 | * @throws InvalidArgumentException If $type is not a recognized constant |
||
65 | * |
||
66 | * @return static |
||
67 | */ |
||
68 | 165 | public static function createFromLabels($data, $type = self::IS_RELATIVE) |
|
86 | |||
87 | /** |
||
88 | * DEPRECATION WARNING! This method will be removed in the next major point release |
||
89 | * |
||
90 | * @deprecated deprecated since version 4.2 |
||
91 | * |
||
92 | * return a new instance from an array or a traversable object |
||
93 | * |
||
94 | * @param \Traversable|string[] $data The segments list |
||
95 | * @param int $type one of the constant IS_ABSOLUTE or IS_RELATIVE |
||
96 | * |
||
97 | * @throws InvalidArgumentException If $type is not a recognized constant |
||
98 | * |
||
99 | * @return static |
||
100 | */ |
||
101 | public static function createFromArray($data, $type = self::IS_RELATIVE) |
||
105 | |||
106 | /** |
||
107 | * New instance |
||
108 | * |
||
109 | * @param null|string $host |
||
110 | */ |
||
111 | 1141 | public function __construct($host = null) |
|
116 | |||
117 | /** |
||
118 | * Returns whether or not the host is an IDN |
||
119 | * |
||
120 | * @return bool |
||
121 | */ |
||
122 | 9 | public function isIdn() |
|
126 | |||
127 | /** |
||
128 | * Returns whether or not the host is an IP address |
||
129 | * |
||
130 | * @return bool |
||
131 | */ |
||
132 | 1075 | public function isIp() |
|
136 | |||
137 | /** |
||
138 | * Returns whether or not the host is an IPv4 address |
||
139 | * |
||
140 | * @return bool |
||
141 | */ |
||
142 | 39 | public function isIpv4() |
|
146 | |||
147 | /** |
||
148 | * Returns whether or not the host is an IPv6 address |
||
149 | * |
||
150 | * @return bool |
||
151 | */ |
||
152 | 39 | public function isIpv6() |
|
156 | |||
157 | /** |
||
158 | * Returns whether or not the host has a ZoneIdentifier |
||
159 | * |
||
160 | * @return bool |
||
161 | * |
||
162 | * @see http://tools.ietf.org/html/rfc6874#section-4 |
||
163 | */ |
||
164 | 12 | public function hasZoneIdentifier() |
|
168 | |||
169 | /** |
||
170 | * Host literal setter |
||
171 | */ |
||
172 | 1075 | protected function setLiteral() |
|
176 | |||
177 | /** |
||
178 | * DEPRECATION WARNING! This method will be removed in the next major point release |
||
179 | * |
||
180 | * @deprecated deprecated since version 4.2 |
||
181 | * |
||
182 | * Returns the instance literal representation |
||
183 | * without encoding |
||
184 | * |
||
185 | * @return string |
||
186 | */ |
||
187 | public function getLiteral() |
||
191 | |||
192 | /** |
||
193 | * validate the submitted data |
||
194 | * |
||
195 | * @param string $str |
||
196 | * |
||
197 | * @return array |
||
198 | */ |
||
199 | 1141 | protected function validate($str) |
|
217 | |||
218 | /** |
||
219 | * Retrieves a single host label. |
||
220 | * |
||
221 | * Retrieves a single host label. If the label offset has not been set, |
||
222 | * returns the default value provided. |
||
223 | * |
||
224 | * @param string $offset the label offset |
||
225 | * @param mixed $default Default value to return if the offset does not exist. |
||
226 | * |
||
227 | * @return mixed |
||
228 | */ |
||
229 | 3 | public function getLabel($offset, $default = null) |
|
237 | |||
238 | /** |
||
239 | * Returns an array representation of the host |
||
240 | * |
||
241 | * @return array |
||
242 | */ |
||
243 | 868 | public function toArray() |
|
247 | |||
248 | /** |
||
249 | * @inheritdoc |
||
250 | */ |
||
251 | 1051 | public function getContent() |
|
263 | |||
264 | /** |
||
265 | * @inheritdoc |
||
266 | */ |
||
267 | 2 | public function __debugInfo() |
|
271 | |||
272 | /** |
||
273 | * @inheritdoc |
||
274 | */ |
||
275 | 15 | public static function __set_state(array $properties) |
|
283 | |||
284 | /** |
||
285 | * Returns a host in his punycode encoded form |
||
286 | * |
||
287 | * This method MUST retain the state of the current instance, and return |
||
288 | * an instance with the host transcoded using to ascii the RFC 3492 rules |
||
289 | * |
||
290 | * @see http://tools.ietf.org/html/rfc3492 |
||
291 | * |
||
292 | * @return static |
||
293 | */ |
||
294 | 108 | public function toAscii() |
|
305 | |||
306 | /** |
||
307 | * Returns a host in his IDN form |
||
308 | * |
||
309 | * This method MUST retain the state of the current instance, and return |
||
310 | * an instance with the host in its IDN form using RFC 3492 rules |
||
311 | * |
||
312 | * @see http://tools.ietf.org/html/rfc3492 |
||
313 | * |
||
314 | * @return static |
||
315 | */ |
||
316 | 81 | public function toUnicode() |
|
324 | |||
325 | /** |
||
326 | * Return a formatted host string |
||
327 | * |
||
328 | * @param \Traversable|string[] $data The segments list |
||
329 | * @param bool $type |
||
330 | * |
||
331 | * @throws InvalidArgumentException If $data is invalid |
||
332 | * |
||
333 | * @return string |
||
334 | */ |
||
335 | 889 | protected static function format($data, $type) |
|
344 | |||
345 | /** |
||
346 | * Return an host without its zone identifier according to RFC6874 |
||
347 | * |
||
348 | * This method MUST retain the state of the current instance, and return |
||
349 | * an instance without the host zone identifier according to RFC6874 |
||
350 | * |
||
351 | * @see http://tools.ietf.org/html/rfc6874#section-4 |
||
352 | * |
||
353 | * @return static |
||
354 | */ |
||
355 | 18 | public function withoutZoneIdentifier() |
|
363 | |||
364 | /** |
||
365 | * Validated the Host Label Count |
||
366 | * |
||
367 | * @param array $labels Host labels |
||
368 | * |
||
369 | * @throws InvalidArgumentException If the validation fails |
||
370 | */ |
||
371 | 889 | protected function assertLabelsCount(array $labels) |
|
377 | |||
378 | /** |
||
379 | * set the FQDN property |
||
380 | * |
||
381 | * @param string $str |
||
382 | * |
||
383 | * @return string |
||
384 | */ |
||
385 | 904 | protected function setIsAbsolute($str) |
|
395 | |||
396 | /** |
||
397 | * @inheritdoc |
||
398 | */ |
||
399 | 30 | public function prepend($component) |
|
406 | |||
407 | /** |
||
408 | * @inheritdoc |
||
409 | */ |
||
410 | 60 | public function append($component) |
|
417 | } |
||
418 |
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.