Complex classes like XmlElement 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 XmlElement, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 40 | class XmlElement |
||
| 41 | { |
||
| 42 | use Accessors; |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Settings for tiding up XML output |
||
| 46 | * |
||
| 47 | * @var array |
||
| 48 | */ |
||
| 49 | public static $tidy = [ |
||
| 50 | 'indent' => true, |
||
| 51 | 'input-xml' => true, |
||
| 52 | 'output-xml' => true, |
||
| 53 | 'drop-empty-paras' => false, |
||
| 54 | 'wrap' => 0 |
||
| 55 | ]; |
||
| 56 | |||
| 57 | /** @var string */ |
||
| 58 | private $_localName; |
||
| 59 | /** @var null|string|false */ |
||
| 60 | private $_prefix = null; |
||
| 61 | |||
| 62 | /** @var array */ |
||
| 63 | private $_namespaces = []; |
||
| 64 | /** @var array */ |
||
| 65 | private $_attributes = []; |
||
| 66 | |||
| 67 | /** |
||
| 68 | * @var XmlElement |
||
| 69 | */ |
||
| 70 | private $_parent; |
||
| 71 | |||
| 72 | /** |
||
| 73 | * @var XmlElement[] |
||
| 74 | */ |
||
| 75 | private $_children = []; |
||
| 76 | |||
| 77 | /** |
||
| 78 | * Initializes element with given name and URI |
||
| 79 | * |
||
| 80 | * @param string $name Element name, including prefix if needed |
||
| 81 | * @param string $uri Namespace URI of element |
||
| 82 | */ |
||
| 83 | 20 | protected function init(string $name, string $uri = null) |
|
| 94 | |||
| 95 | /** |
||
| 96 | * XmlElement constructor |
||
| 97 | * |
||
| 98 | * @param string $name Element name, including prefix if needed |
||
| 99 | * @param string $uri Namespace URI of element |
||
| 100 | */ |
||
| 101 | 16 | public function __construct(string $name, string $uri = null) |
|
| 105 | |||
| 106 | /** |
||
| 107 | * Elements named constructor, same for every subclass. |
||
| 108 | * It's used for factory creation. |
||
| 109 | * |
||
| 110 | * @param string $name Element name, including prefix if needed |
||
| 111 | * @param string $uri Namespace URI of element |
||
| 112 | * |
||
| 113 | * @return XmlElement |
||
| 114 | */ |
||
| 115 | 4 | public static function plain(string $name, string $uri = null) |
|
| 123 | |||
| 124 | /** |
||
| 125 | * @see $innerXml |
||
| 126 | * @return string |
||
| 127 | */ |
||
| 128 | 2 | public function getInnerXml() |
|
| 140 | |||
| 141 | /** |
||
| 142 | * Returns XML representation of element |
||
| 143 | * |
||
| 144 | * @param bool $clean Result will be cleaned if set to true |
||
| 145 | * |
||
| 146 | * @return string |
||
| 147 | */ |
||
| 148 | 3 | public function xml(bool $clean = true): string |
|
| 169 | |||
| 170 | /** |
||
| 171 | * Looks up prefix associated with given URI |
||
| 172 | * |
||
| 173 | * @param string|null $uri |
||
| 174 | * @return string|false |
||
| 175 | */ |
||
| 176 | 10 | public function lookupPrefix(string $uri = null) |
|
| 180 | |||
| 181 | /** |
||
| 182 | * Looks up URI associated with given prefix |
||
| 183 | * |
||
| 184 | * @param string|null $prefix |
||
| 185 | * @return string|false |
||
| 186 | */ |
||
| 187 | 15 | public function lookupUri(string $prefix = null) |
|
| 191 | |||
| 192 | /** |
||
| 193 | * Returns element's namespaces |
||
| 194 | * |
||
| 195 | * @param bool $parent Include namespaces from parent? |
||
| 196 | * @return array |
||
| 197 | */ |
||
| 198 | 17 | public function getNamespaces($parent = true): array |
|
| 210 | |||
| 211 | /** |
||
| 212 | * Sets XML attribute of element |
||
| 213 | * |
||
| 214 | * @param string $attribute Attribute name, optionally with prefix |
||
| 215 | * @param mixed $value Attribute value |
||
| 216 | * @param string|null $uri XML Namespace URI of attribute, prefix will be automatically looked up |
||
| 217 | */ |
||
| 218 | 4 | public function setAttribute(string $attribute, $value, string $uri = null) |
|
| 222 | |||
| 223 | /** |
||
| 224 | * Returns value of specified attribute. |
||
| 225 | * |
||
| 226 | * @param string $attribute Attribute name, optionally with prefix |
||
| 227 | * @param string|null $uri XML Namespace URI of attribute, prefix will be automatically looked up |
||
| 228 | * @return bool|mixed |
||
| 229 | */ |
||
| 230 | 2 | public function getAttribute(string $attribute, string $uri = null) |
|
| 234 | |||
| 235 | /** |
||
| 236 | * Checks if attribute exists |
||
| 237 | * |
||
| 238 | * @param string $attribute Attribute name, optionally with prefix |
||
| 239 | * @param string|null $uri XML Namespace URI of attribute, prefix will be automatically looked up |
||
| 240 | * |
||
| 241 | * @return bool |
||
| 242 | */ |
||
| 243 | 2 | public function hasAttribute(string $attribute, string $uri = null) |
|
| 247 | |||
| 248 | /** |
||
| 249 | * Returns element's parent |
||
| 250 | * @return XmlElement|null |
||
| 251 | */ |
||
| 252 | 1 | public function getParent() |
|
| 256 | |||
| 257 | /** |
||
| 258 | * Sets element's parent |
||
| 259 | * @param XmlElement $parent |
||
| 260 | */ |
||
| 261 | 9 | protected function setParent(XmlElement $parent) |
|
| 273 | |||
| 274 | /** |
||
| 275 | * Appends child to element |
||
| 276 | * |
||
| 277 | * @param XmlElement|string $element |
||
| 278 | * |
||
| 279 | * @return XmlElement|string Same as $element |
||
| 280 | */ |
||
| 281 | 12 | public function append($element) |
|
| 300 | |||
| 301 | /** |
||
| 302 | * Returns namespace URI associated with element |
||
| 303 | * |
||
| 304 | * @return false|string |
||
| 305 | */ |
||
| 306 | 15 | public function getNamespace() |
|
| 310 | |||
| 311 | /** |
||
| 312 | * Adds namespace to element, and associates it with prefix. |
||
| 313 | * |
||
| 314 | * @param string $uri Namespace URI |
||
| 315 | * @param string|bool|null $prefix Prefix which will be used for namespace, false for using element's prefix |
||
| 316 | * and null for no prefix |
||
| 317 | */ |
||
| 318 | 13 | public function setNamespace(string $uri, $prefix = false) |
|
| 326 | |||
| 327 | 7 | public function getName() |
|
| 331 | |||
| 332 | 5 | public function getChildren() |
|
| 336 | |||
| 337 | 15 | public function getPrefix() |
|
| 341 | |||
| 342 | 10 | public function getLocalName() |
|
| 346 | |||
| 347 | 6 | public function getAttributes() |
|
| 351 | |||
| 352 | /** |
||
| 353 | * Returns one element at specified index (for default the first one). |
||
| 354 | * |
||
| 355 | * @param string $name Requested element tag name |
||
| 356 | * @param string $uri Requested element namespace |
||
| 357 | * @param int $index Index of element to retrieve |
||
| 358 | * |
||
| 359 | * @return XmlElement|false Retrieved element |
||
| 360 | */ |
||
| 361 | 1 | public function element(string $name, string $uri = null, int $index = 0) |
|
| 365 | |||
| 366 | /** |
||
| 367 | * Retrieves array of matching elements |
||
| 368 | * |
||
| 369 | * @param string $name Requested element tag name |
||
| 370 | * @param string|null $uri Requested element namespace |
||
| 371 | * |
||
| 372 | * @return XmlElement[] Found Elements |
||
| 373 | */ |
||
| 374 | 2 | public function elements($name, $uri = null) : array |
|
| 383 | |||
| 384 | /** |
||
| 385 | * Filters element with given predicate |
||
| 386 | * |
||
| 387 | * @param callable|string $predicate Predicate or class name |
||
| 388 | * |
||
| 389 | * @return XmlElement[] |
||
| 390 | */ |
||
| 391 | 2 | public function all($predicate) |
|
| 395 | |||
| 396 | /** |
||
| 397 | * Iterates over matching elements |
||
| 398 | * |
||
| 399 | * @param callable|string $predicate Predicate or class name |
||
| 400 | * |
||
| 401 | * @return XmlElement|false |
||
| 402 | */ |
||
| 403 | 1 | public function get($predicate) |
|
| 414 | |||
| 415 | /** |
||
| 416 | * @param string|null $query |
||
| 417 | * @return XPathQuery |
||
| 418 | */ |
||
| 419 | 1 | public function query(string $query = null) |
|
| 423 | |||
| 424 | /** |
||
| 425 | * Helper for retrieving all arguments (including namespaces) |
||
| 426 | * |
||
| 427 | * @return array |
||
| 428 | */ |
||
| 429 | 3 | private function attributes(): array |
|
| 441 | |||
| 442 | /** |
||
| 443 | * Prefixes $name with attribute associated with $uri |
||
| 444 | * |
||
| 445 | * @param string $name Name to prefix |
||
| 446 | * @param string $uri Namespace URI |
||
| 447 | * |
||
| 448 | * @return string |
||
| 449 | */ |
||
| 450 | 4 | protected function _prefix(string $name, string $uri = null): string |
|
| 462 | |||
| 463 | 2 | public function __toString() |
|
| 467 | |||
| 468 | /** |
||
| 469 | * Splits name into local-name and prefix |
||
| 470 | * |
||
| 471 | * @param $name |
||
| 472 | * @return array [$name, $prefix] |
||
| 473 | */ |
||
| 474 | 20 | public static function resolve($name) |
|
| 484 | } |
||
| 485 |
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_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.