Completed
Push — master ( c42a51...da7c37 )
by Tom
01:57
created

Builder::output()   B

Complexity

Conditions 3
Paths 2

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
rs 8.9713
cc 3
eloc 15
nc 2
nop 2
1
<?php
2
/* @description     Transformation Style Sheets - Revolutionising PHP templating    *
3
 * @author          Tom Butler [email protected]                                             *
4
 * @copyright       2017 Tom Butler <[email protected]> | https://r.je/                      *
5
 * @license         http://www.opensource.org/licenses/bsd-license.php  BSD License *
6
 * @version         1.2                                                             */
7
namespace Transphporm;
8
/** Builds a Transphorm instance from the 3 constituent parts. XML template string, TSS string and data */
9
class Builder {
10
	private $template;
11
	private $tss;
12
	private $cache;
13
	private $time;
14
	private $modules = [];
15
	private $config;
16
	private $filePath;
17
	private $cacheKey;
18
	private $defaultModules = [
19
		'\\Transphporm\\Module\\Basics',
20
		'\\Transphporm\\Module\\Pseudo',
21
		'\\Transphporm\\Module\\Format',
22
		'\\Transphporm\\Module\\Functions'
23
	];
24
25
	public function __construct($template, $tss = '', $modules = null) {
26
		$this->template = $template;
27
		$this->tss = $tss;
28
		$this->cache = new Cache(new \ArrayObject());
29
		$this->filePath = new FilePath();
30
		$modules = is_array($modules) ? $modules : $this->defaultModules;
31
		foreach ($modules as $module) $this->loadModule(new $module);
32
	}
33
34
	//Allow setting the time used by Transphporm for caching. This is for testing purposes
35
	//Would be better if PHP allowed setting the script clock, but this is the simplest way of overriding it
36
	public function setTime($time) {
37
		$this->time = $time;
38
	}
39
40
	public function loadModule(Module $module) {
41
		$this->modules[get_class($module)] = $module;
42
	}
43
44
	public function setLocale($locale) {
45
        $format = new \Transphporm\Module\Format($locale);
46
        $this->modules[get_class($format)] = $format;
47
    }
48
49
	public function addPath($dir) {
50
		$this->filePath->addPath($dir);
51
	}
52
53
	public function output($data = null, $document = false) {
54
		$headers = [];
55
56
		$tssCache = new SheetLoader($this->cache, $this->filePath, $this->tss, $this->time);
57
		$this->cacheKey = $tssCache->getCacheKey($data);
58
		$result = $this->loadTemplate();
59
		//If an update is required, run any rules that need to be run. Otherwise, return the result from cache
60
		//without creating any further objects, loading a DomDocument, etc
61
62
		if (empty($result['renderTime']) || $tssCache->updateRequired($data) === true) {
63
			$template = $this->createAndProcessTemplate($data, $result['cache'], $headers);
64
			$tssCache->processRules($template, $this->config);
65
66
			$result = ['cache' => $template->output($document),
67
			   'renderTime' => time(),
68
			   'headers' => array_merge($result['headers'], $headers),
69
			   'body' => $this->doPostProcessing($template)->output($document)
70
			];
71
72
			$this->cache->write($tssCache->getCacheKey($data) . $this->template, $result);
73
		}
74
		unset($result['cache'], $result['renderTime']);
75
		return (object) $result;
76
	}
77
78
	private function createAndProcessTemplate($data, $body, &$headers) {
79
		$elementData = new \Transphporm\Hook\ElementData(new \SplObjectStorage(), $data);
80
		$functionSet = new FunctionSet($elementData);
81
		//To be a valid XML document it must have a root element, automatically wrap it in <template> to ensure it does
82
		$template = new Template($this->isValidDoc($body) ? str_ireplace('<!doctype', '<!DOCTYPE', $body) : '<template>' . $body . '</template>' );
83
84
		$valueParser = new Parser\Value($functionSet);
85
		$this->config = new Config($functionSet, $valueParser, $elementData, new Hook\Formatter(), new Parser\CssToXpath($functionSet, $template->getPrefix(), md5($this->tss)), $this->filePath, $headers);
86
87
		foreach ($this->modules as $module) $module->load($this->config);
88
		return $template;
89
	}
90
91
	//Add a postprocessing hook. This cleans up anything transphporm has added to the markup which needs to be removed
92
	private function doPostProcessing($template) {
93
		$template->addHook('//*[@transphporm]', new Hook\PostProcess());
94
		return $template;
95
	}
96
97
98
	//Load a template, firstly check if it's a file or a valid string
99
	private function loadTemplate() {
100
        $result = ['cache' => $this->template, 'headers' => []];
101
		if (strpos($this->template, "\n") === false && is_file($this->template)) $result = $this->loadTemplateFromFile($this->template);
102
		return $result;
103
	}
104
105
    private function loadTemplateFromFile($file) {
0 ignored issues
show
Unused Code introduced by
The parameter $file 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...
106
        $xml = $this->cache->load($this->cacheKey . $this->template, filemtime($this->template));
107
        return $xml ? $xml : ['cache' => file_get_contents($this->template) ?: "", 'headers' => []];
108
    }
109
110
	public function setCache(\ArrayAccess $cache) {
111
		$this->cache = new Cache($cache);
112
	}
113
114
	private function isValidDoc($xml) {
115
		return (strpos($xml, '<!') === 0 && strpos($xml, '<!--') !== 0) || strpos($xml, '<?') === 0 || strpos($xml, '<html') === 0;
116
	}
117
118
	public function __destruct() {
119
		//Required hack as DomXPath can only register static functions clear the statically stored instance to avoid memory leaks
120
		if (isset($this->config)) $this->config->getCssToXpath()->cleanup();
121
	}
122
}
123