Completed
Push — master ( 02c8a1...58faf5 )
by Tom
02:14
created

src/Builder.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
	private function getSheetLoader() {
54
		$tssRules = is_file($this->tss) ? new SheetLoader\TSSFile($this->tss, $this->filePath, $this->cache, $this->time) : new SheetLoader\TSSString($this->tss, $this->filePath);
55
		return new SheetLoader\SheetLoader($this->cache, $this->filePath, $tssRules, $this->time);
56
	}
57
58
	public function output($data = null, $document = false) {
59
		$headers = [];
60
61
		$tssCache = $this->getSheetLoader();
62
		$this->cacheKey = $tssCache->getCacheKey($data);
63
		$result = $this->loadTemplate();
64
		//If an update is required, run any rules that need to be run. Otherwise, return the result from cache
65
		//without creating any further objects, loading a DomDocument, etc
66
		if (empty($result['renderTime']) || $tssCache->updateRequired($data) === true) {
67
			$template = $this->createAndProcessTemplate($data, $result['cache'], $headers);
68
			$tssCache->processRules($template, $this->config);
69
70
			$result = ['cache' => $template->output($document),
71
			   'renderTime' => time(),
72
			   'headers' => array_merge($result['headers'], $headers),
73
			   'body' => $this->doPostProcessing($template)->output($document)
74
			];
75
			$this->cache->write($tssCache->getCacheKey($data) . $this->template, $result);
76
		}
77
		unset($result['cache'], $result['renderTime']);
78
		return (object) $result;
79
	}
80
81
	private function createAndProcessTemplate($data, $body, &$headers) {
82
		$elementData = new \Transphporm\Hook\ElementData(new \SplObjectStorage(), $data);
83
		$functionSet = new FunctionSet($elementData);
84
		//To be a valid XML document it must have a root element, automatically wrap it in <template> to ensure it does
85
		$template = new Template($this->isValidDoc($body) ? str_ireplace('<!doctype', '<!DOCTYPE', $body) : '<template>' . $body . '</template>' );
86
87
		$valueParser = new Parser\Value($functionSet);
88
		$this->config = new Config($functionSet, $valueParser, $elementData, new Hook\Formatter(), new Parser\CssToXpath($functionSet, $template->getPrefix(), md5($this->tss)), $this->filePath, $headers);
89
90
		foreach ($this->modules as $module) $module->load($this->config);
91
		return $template;
92
	}
93
94
	//Add a postprocessing hook. This cleans up anything transphporm has added to the markup which needs to be removed
95
	private function doPostProcessing($template) {
96
		$template->addHook('//*[@transphporm]', new Hook\PostProcess());
97
		return $template;
98
	}
99
100
101
	//Load a template, firstly check if it's a file or a valid string
102
	private function loadTemplate() {
103
        $result = ['cache' => $this->template, 'headers' => []];
104
		if (strpos($this->template, "\n") === false && is_file($this->template)) $result = $this->loadTemplateFromFile($this->template);
105
		return $result;
106
	}
107
108
    private function loadTemplateFromFile($file) {
0 ignored issues
show
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...
109
        $xml = $this->cache->load($this->cacheKey . $this->template, filemtime($this->template));
110
        return $xml ? $xml : ['cache' => file_get_contents($this->template) ?: "", 'headers' => []];
111
    }
112
113
	public function setCache(\ArrayAccess $cache) {
114
		$this->cache = new Cache($cache);
115
	}
116
117
	private function isValidDoc($xml) {
118
		return (strpos($xml, '<!') === 0 && strpos($xml, '<!--') !== 0) || strpos($xml, '<?') === 0 || strpos($xml, '<html') === 0;
119
	}
120
121
	public function __destruct() {
122
		//Required hack as DomXPath can only register static functions clear the statically stored instance to avoid memory leaks
123
		if (isset($this->config)) $this->config->getCssToXpath()->cleanup();
124
	}
125
}
126