Completed
Pull Request — master (#77)
by
unknown
02:19
created

Sheet::parse()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 1
Metric Value
c 5
b 1
f 1
dl 0
loc 18
rs 8.8571
cc 5
eloc 13
nc 12
nop 3
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\Parser;
8
/** Parses a .tss file into individual rules, each rule has a query e,g, `ul li` and a set of rules e.g. `display: none; bind: iteration(id);` */
9
class Sheet {
10
	private $tss;
11
	private $baseDir;
12
	private $prefix;
13
	private $valueParser;
14
15
	public function __construct($tss, $baseDir, Value $valueParser, $prefix = '') {
16
		$this->tss = $this->stripComments($tss, '/*', '*/');
17
		$this->tss = $this->stripComments($this->tss, '//', "\n");
18
		$this->baseDir = $baseDir;
19
		$this->prefix = $prefix;
20
		$this->valueParser = $valueParser;
21
	}
22
23
	public function parse($pos = 0, $rules = [], $indexStart = 0) {
24
		while ($next = strpos($this->tss, '{', $pos)) {
25
			if ($processing = $this->processingInstructions($this->tss, $pos, $next, count($rules)+$indexStart)) {
26
				$pos = $processing['endPos']+1;
27
				$rules = array_merge($rules, $processing['rules']);
28
			}
29
30
			$selector = trim(substr($this->tss, $pos, $next-$pos));
31
			$pos =  strpos($this->tss, '}', $next)+1;
32
			$newRules = $this->cssToRules($selector, count($rules)+$indexStart, $this->getProperties(trim(substr($this->tss, $next+1, $pos-2-$next))));
33
			$rules = $this->writeRule($rules, $newRules);
34
		}
35
		//there may be processing instructions at the end
36
		if ($processing = $this->processingInstructions($this->tss, $pos, strlen($this->tss), count($rules)+$indexStart)) $rules = array_merge($rules, $processing['rules']);
37
		usort($rules, [$this, 'sortRules']);
38
		if (empty($rules)) trigger_error("No TSS rules parsed");
39
		return $rules;
40
	}
41
42
	private function CssToRules($selector, $index, $properties) {
43
		$parts = explode(',', $selector);
44
		$rules = [];
45
		foreach ($parts as $part) {
46
			$xPath = new CssToXpath($part, $this->valueParser, $this->prefix);
47
			$rules[$part] = new \Transphporm\Rule($xPath->getXpath(), $xPath->getPseudo(), $xPath->getDepth(), $index++);
48
			$rules[$part]->properties = $properties;
49
		}		
50
		return $rules;
51
	}
52
53
	private function writeRule($rules, $newRules) {
54
		foreach ($newRules as $selector => $newRule) {
55
			if (isset($rules[$selector])) {
56
				$newRule->properties = array_merge($rules[$selector]->properties, $newRule->properties);
57
			}
58
			$rules[$selector] = $newRule;
59
		}	
60
		
61
		return $rules;
62
	}
63
64
	private function processingInstructions($tss, $pos, $next, $indexStart) {
65
		$rules = [];
66
		while (($atPos = strpos($tss, '@', $pos)) !== false) {
67
			if ($atPos  <= (int) $next) {
68
				$spacePos = strpos($tss, ' ', $atPos);
69
				$funcName = substr($tss, $atPos+1, $spacePos-$atPos-1);
70
				$pos = strpos($tss, ';', $spacePos);
71
				$args = substr($tss, $spacePos+1, $pos-$spacePos-1);
72
				$rules = array_merge($rules, $this->$funcName($args, $indexStart));
73
			}
74
			else {
75
				break;	
76
			} 
77
		}
78
79
		return empty($rules) ? false : ['endPos' => $pos, 'rules' => $rules];
80
	}
81
82
	private function import($args, $indexStart) {
83
		$fileName = $this->valueParser->parse(trim($args, '\'" '));
84
		$sheet = new Sheet(file_get_contents($this->baseDir . $fileName[0]), $this->baseDir, $this->valueParser, $this->prefix);
85
		return $sheet->parse(0, [], $indexStart);
86
	}
87
88
	private function sortRules($a, $b) {
89
		//If they have the same depth, compare on index
90
		if ($a->depth === $b->depth) return $a->index < $b->index ? -1 : 1;
91
92
		return ($a->depth < $b->depth) ? -1 : 1;
93
	}
94
95
	private function stripComments($str, $open, $close) {
96
		$pos = 0;
97
		while (($pos = strpos($str, $open, $pos)) !== false) {
98
			$end = strpos($str, $close, $pos);
99
			if ($end === false) break;
100
			$str = substr_replace($str, '', $pos, $end-$pos+2);
101
		}
102
103
		return $str;
104
	}
105
106
	private function getProperties($str) {
107
		$stringExtractor = new StringExtractor($str);
108
		$rules = explode(';', $stringExtractor);
109
		$return = [];
110
111
		foreach ($rules as $rule) {
112
			if (trim($rule) === '') continue;
113
			$parts = explode(':', $rule, 2);
114
115
			$parts[1] = $stringExtractor->rebuild($parts[1]);
116
			$return[trim($parts[0])] = isset($parts[1]) ? trim($parts[1]) : '';
117
		}
118
119
		return $return;
120
	}
121
}
122