Completed
Pull Request — master (#63)
by Tom
02:28
created

DataFunction   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 176
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 29
Bugs 3 Features 2
Metric Value
wmc 43
c 29
b 3
f 2
lcom 1
cbo 3
dl 0
loc 176
rs 8.3157

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A setBaseDir() 0 3 1
A bind() 0 7 4
A isObjectArray() 0 3 3
A iteration() 0 5 1
A key() 0 4 1
A getData() 0 7 4
A data() 0 5 1
A traverse() 0 17 4
A traverseObj() 0 8 3
A ifNull() 0 4 4
A attr() 0 3 1
A templateSubsection() 0 12 2
B template() 0 31 6
A createDummyTemplateDoc() 0 20 4
A loadTemplate() 0 14 3

How to fix   Complexity   

Complex Class

Complex classes like DataFunction 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 DataFunction, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* @description     Transformation Style Sheets - Revolutionising PHP templating    *
3
 * @author          Tom Butler [email protected]                                             *
4
 * @copyright       2015 Tom Butler <[email protected]> | https://r.je/                      *
5
 * @license         http://www.opensource.org/licenses/bsd-license.php  BSD License *
6
 * @version         1.0                                                             */
7
namespace Transphporm\Hook;
8
/* Handles data() and iteration() function calls from the stylesheet */
9
class DataFunction {
10
	private $dataStorage;
11
	private $data;
12
	private $baseDir;
13
14
	public function __construct(\SplObjectStorage $objectStorage, $data, $baseDir, $tss) {
15
		$this->dataStorage = $objectStorage;
16
		$this->data = $data;
17
		$this->baseDir = $baseDir;
18
		$this->tss = $tss;
0 ignored issues
show
Bug introduced by
The property tss does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
19
	}
20
21
	public function setBaseDir($dir) {
22
		$this->baseDir = $dir;
23
	}
24
25
	/** Binds data to an element */
26
	public function bind(\DomNode $element, $data, $type = 'data') {
27
		//This is a bit of a hack to workaround #24, might need a better way of doing this if it causes a problem
28
		if (is_array($data) && $this->isObjectArray($data)) $data = $data[0];
29
		$content = isset($this->dataStorage[$element]) ? $this->dataStorage[$element] : [];
30
		$content[$type] = $data;
31
		$this->dataStorage[$element] = $content;
32
	}
33
34
	private function isObjectArray(array $data) {
35
		return count($data) === 1 && isset($data[0]) && is_object($data[0]);
36
	}
37
38
	public function iteration($val, $element) {
39
		$data = $this->getData($element, 'iteration');
40
		$value = $this->traverse($val, $data, $element);
41
		return $value;
42
	}
43
44
	public function key($val, $element) {
0 ignored issues
show
Unused Code introduced by
The parameter $val is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
45
		$data = $this->getData($element, 'key');
46
		return $data;
47
	}
48
49
	/** Returns the data that has been bound to $element, or, if no data is bound to $element climb the DOM tree to find the data bound to a parent node*/
50
	public function getData(\DomElement $element = null, $type = 'data') {
51
		while ($element) {
52
			if (isset($this->dataStorage[$element]) && isset($this->dataStorage[$element][$type])) return $this->dataStorage[$element][$type];
53
			$element = $element->parentNode;
54
		}
55
		return $this->data;
56
	}
57
58
	public function data($val, \DomElement $element = null) {
59
		$data = $this->getData($element);
60
		$value = $this->traverse($val, $data, $element);
61
		return $value;
62
	}
63
64
	private function traverse($name, $data, $element) {
65
		$name[0] = str_replace(['[', ']'], ['.', ''], $name[0]);
66
		$parts = explode('.', $name[0]);
67
		$obj = $data;
68
		$valueParser = new \Transphporm\Parser\Value($this);
69
70
		foreach ($parts as $part) {
71
			if ($part === '') continue;
72
			$part = $valueParser->parse($part, $element)[0];
73
			$funcResult = $this->traverseObj($part, $obj, $valueParser, $element);
74
75
			if ($funcResult !== false) $obj = $funcResult;
76
			
77
			else $obj = $this->ifNull($obj, $part);
78
		}
79
		return $obj;
80
	}
81
82
	private function traverseObj($part, $obj, $valueParser, $element) {
83
		if (strpos($part, '(') !== false) {
84
			$subObjParser = new \Transphporm\Parser\Value($obj, $valueParser, false);
85
			return $subObjParser->parse($part, $element)[0];
86
		}
87
		else if (method_exists($obj, $part)) return call_user_func([$obj, $part]); 
88
		else return false;
89
	}
90
91
	private function ifNull($obj, $key) {
92
		if (is_array($obj)) return isset($obj[$key]) ? $obj[$key] : null;
93
		else return isset($obj->$key) ? $obj->$key : null;
94
	}
95
96
	public function attr($val, $element) {
97
		return $element->getAttribute(trim($val[0]));
98
	}
99
100
	private function templateSubsection($css, $doc, $element) {
101
		$xpathStr = (new \Transphporm\Parser\CssToXpath($css, new \Transphporm\Parser\Value($this)))->getXpath();
102
		$xpath = new \DomXpath($doc);
103
		$nodes = $xpath->query($xpathStr);
104
		$result = [];
105
106
		foreach ($nodes as $node) {
107
			$result[] = $element->ownerDocument->importNode($node, true);
108
		}
109
110
		return $result;
111
	}
112
113
114
	public function template($val, \DomElement $element, $rules) {
115
		//Check the nesting level... without this it will keep applying TSS to included templates forever in some cases
116
		if (isset($rules['template-recursion']) && $rules['template-recursion'] == 'on') $tss = $this->tss;
117
		else $tss = '';
118
		//Create a document to mimic the structure of the parent template
119
		
120
		$newDocument = $this->createDummyTemplateDoc($element, $this->baseDir . $val[0]);		
121
122
		//Build a new template using the $newDocument
123
		$newTemplate = new \Transphporm\Builder($newDocument->saveXml(), $tss);
124
125
		$data = $this->getData($element);
126
127
	
128
		//Output the template as a DomDocument
129
		$doc = $newTemplate->output($data, true)->body;
130
131
		//Find the corresponding element in the new document that matches the position of the original $element
132
		//and read the contents as the result to import into the parent template
133
		$result = [];
134
		$xpath = new \DomXpath($doc);
135
		$correspondingElement = $xpath->query('//*[@transphpormbaselement]')[0];
136
		if (!$correspondingElement) $correspondingElement = $doc->documentElement;		
137
		foreach ($correspondingElement->childNodes as $child) {
138
			$child = $child->cloneNode(true);
139
			if ($child instanceof \DomElement) $child->setAttribute('transphporm', 'includedtemplate');
140
141
			$result[] = $child;			
142
		}		
143
		return $result;
144
	}
145
146
	private function createDummyTemplateDoc(\DomElement $element, $templateFile) {		
147
		$newDocument = new \DomDocument;
148
		$root = $newDocument->createElement('template');
149
		$newDocument->appendChild($root);
150
151
		//Loop through all parent nodes of $element and import them into the new document.
152
		$el = $element;
153
		$baseElement = null;
154
		do {
155
			$firstChild = $root->firstChild;
156
			$el = $el->cloneNode();
157
			$newNode = $newDocument->importNode($el);
158
			if ($baseElement === null) $baseElement = $newNode;
159
			if ($firstChild) $newNode->appendChild($firstChild);
160
			$root->appendChild($newNode);
161
		}
162
		while (($el = $el->parentNode)  instanceof \DomElement);	
163
		$this->loadTemplate($baseElement, $templateFile);
164
		return $newDocument;
165
	}
166
167
	private function loadTemplate($baseElement, $templateFile) {
168
		$baseElement->setAttribute('transphpormbaselement', 'true');
169
		//Load the template XML
170
		$templateDoc = new \DomDocument();
171
	
172
		$templateDoc->loadXml(file_get_contents($templateFile));
173
174
		if ($templateDoc->documentElement->tagName == 'template') {
175
			foreach ($templateDoc->documentElement->childNodes as $node) {
176
				$node = $baseElement->ownerDocument->importNode($node, true);
177
				$baseElement->appendChild($node);		
178
			}
179
		}
180
	}
181
182
183
184
}
185