Completed
Push — master ( ed7d82...0b58b7 )
by Tom
02:40
created

Builder::setTime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
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         0.9                                                             */
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 $registeredProperties = [];
13
	private $formatters = [];
14
	private $locale;
15
	private $baseDir;
16
	private $cache;
17
	private $userCache;
18
	private $time;
19
20
	public function __construct($template, $tss = '') {
21
		$this->template = $template;
22
		$this->tss = $tss;
23
		$this->cache = new Cache(new \ArrayObject());
24
	}
25
26
	public function setTime($time) {
27
		$this->time = $time;
28
	}
29
30
	public function output($data = null, $document = false) {
31
		$locale = $this->getLocale();
32
		$data = new Hook\DataFunction(new \SplObjectStorage(), $data, $locale, $this->baseDir);
33
		$headers = [];
34
		$this->registerProperties($this->getBasicProperties($data, $locale, $headers));
35
36
		$cachedOutput = $this->loadTemplate();
37
		$xml = $cachedOutput['body'];
38
		//To be a valid XML document it must have a root element, automatically wrap it in <template> to ensure it does
39
		$template = new Template($this->isValidDoc($xml) ? $xml : '<template>' . $xml . '</template>' );
40
41
		//Allow $time to be set via arguments to spoof time passage during tests
42
		foreach ($this->getRules($template) as $rule) {
43
			if ($rule->shouldRun($this->time)) $this->executeTssRule($rule, $template, $data);			
44
		}
45
		
46
		$output = $template->output($document);
47
		$result = ['headers' => array_merge($cachedOutput['headers'], $headers), 'body' => $output];
48
		$this->cache->write($this->template, $result);
49
		//Add the postprocessing hook
50
		$template->addHook('//*[@transphporm]', new Hook\PostProcess());
51
		$result['body'] = $template->output($document);
52
		return (object) $result;
53
	}
54
55
	private function executeTssRule($rule, $template, $data) {
56
		$rule->touch();
57
		$hook = new Hook\Rule($rule->properties, new Hook\PseudoMatcher($rule->pseudo, $data), $data);
58
		foreach ($this->registeredProperties as $properties) $hook->registerProperties($properties);
59
		$template->addHook($rule->query, $hook);
60
	}
61
62
	private function loadTemplate() {
63
		if (trim($this->template)[0] !== '<') {			
64
			$xml = $this->cache->load($this->template, filemtime($this->template));
65
			return $xml ? $xml : ['body' => file_get_contents($this->template), 'headers' => []];
66
		}
67
		else return ['body' => $this->template, 'headers' => []];	
68
	}
69
70
	private function getRules($template) {		
71
		if (is_file($this->tss)) {
72
			$this->baseDir = dirname(realpath($this->tss)) . DIRECTORY_SEPARATOR;
73
			$key = $this->tss . $template->getPrefix() . $this->baseDir;
74
			$rules = $this->cache->load($key, filemtime($this->tss));
75
			if (!$rules) return $this->cache->write($key, (new Sheet(file_get_contents($this->tss), $this->baseDir, $template->getPrefix()))->parse());
76
			else return $rules;
77
		}
78
		else return (new Sheet($this->tss, $this->baseDir, $template->getPrefix()))->parse();
79
	}
80
81
	private function getBasicProperties($data, $locale, &$headers) {
82
		$basicProperties = new Hook\BasicProperties($data, $headers);
83
		$basicProperties->registerFormatter(new Formatter\Number($locale));
84
		$basicProperties->registerFormatter(new Formatter\Date($locale));
85
		$basicProperties->registerFormatter(new Formatter\StringFormatter());
86
		foreach ($this->formatters as $formatter) $basicProperties->registerFormatter($formatter);
87
88
		return isset($this->userCache) ? new Hook\Cache($basicProperties, $this->userCache) : $basicProperties;
89
	}
90
91
	public function setCache(\ArrayAccess $cache) {
92
		$this->cache = new Cache($cache);
93
	}
94
95
	private function getLocale() {
96
		if (is_array($this->locale)) return $this->locale;
97
		else if (strlen($this->locale) > 0) return json_decode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'Formatter' . DIRECTORY_SEPARATOR . 'Locale' . DIRECTORY_SEPARATOR . $this->locale . '.json'), true);
98
		else return json_decode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'Formatter' . DIRECTORY_SEPARATOR . 'Locale' . DIRECTORY_SEPARATOR . 'enGB.json'), true);
99
	}
100
101
	public function registerProperties($object) {
102
		$this->registeredProperties[] = $object;
103
	}
104
105
	public function registerFormatter($formatter) {
106
		$this->formatters[] = $formatter;
107
	}
108
109
	public function setLocale($locale) {
110
		$this->locale = $locale;
111
	}
112
113
	private function isValidDoc($xml) {
114
		return strpos($xml, '<!') === 0 || strpos($xml, '<?') === 0;
115
	}
116
}
117