Completed
Push — master ( a808f6...04f450 )
by Tom
02:23
created

Sheet::parse()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 19
rs 8.8571
cc 5
eloc 13
nc 10
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->baseDir = $baseDir;
18
		$this->prefix = $prefix;
19
		$this->valueParser = $valueParser;
20
	}
21
22
	public function parse($pos = 0, $rules = [], $indexStart = 0) {
23
		while ($next = strpos($this->tss, '{', $pos)) {
24
			if ($processing = $this->processingInstructions($this->tss, $pos, $next, count($rules)+$indexStart)) {
25
				$pos = $processing['endPos']+1;
26
				$rules = array_merge($rules, $processing['rules']);
27
			}
28
29
			$selector = trim(substr($this->tss, $pos, $next-$pos));
30
			$pos =  strpos($this->tss, '}', $next)+1;
31
			foreach ($this->cssToRules($selector, count($rules)+$indexStart) as  $selector => $rule) {
32
				$rule->properties = $this->getProperties(trim(substr($this->tss, $next+1, $pos-2-$next)));	
33
				$rules = $this->writeRule($rules, $selector, $rule);					
34
			}
35
		}
36
		//there may be processing instructions at the end
37
		if ($processing = $this->processingInstructions($this->tss, $pos, strlen($this->tss), count($rules)+$indexStart)) $rules = array_merge($rules, $processing['rules']);
38
		usort($rules, [$this, 'sortRules']);
39
		return $rules;
40
	}
41
42
	private function CssToRules($selector, $index) {
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
		}		
49
		return $rules;
50
	}
51
52
	private function writeRule($rules, $selector, $newRule) {
53
		if (isset($rules[$selector])) {
54
			$newRule->properties = array_merge($rules[$selector]->properties, $newRule->properties);
55
		}
56
57
		$rules[$selector] = $newRule;
58
		
59
		return $rules;
60
	}
61
62
	private function processingInstructions($tss, $pos, $next, $indexStart) {
63
		$rules = [];
64
		while (($atPos = strpos($tss, '@', $pos)) !== false) {
65
			if ($atPos  <= (int) $next) {
66
				$spacePos = strpos($tss, ' ', $atPos);
67
				$funcName = substr($tss, $atPos+1, $spacePos-$atPos-1);
68
				$pos = strpos($tss, ';', $spacePos);
69
				$args = substr($tss, $spacePos+1, $pos-$spacePos-1);
70
				$rules = array_merge($rules, $this->$funcName($args, $indexStart));
71
			}
72
			else {
73
				break;	
74
			} 
75
		}
76
77
		return empty($rules) ? false : ['endPos' => $pos, 'rules' => $rules];
78
	}
79
80
	private function import($args, $indexStart) {
81
		$sheet = new Sheet(file_get_contents($this->baseDir . trim($args, '\'" ')), $this->baseDir, $this->valueParser, $this->prefix);
82
		return $sheet->parse(0, [], $indexStart);
83
	}
84
85
	private function sortRules($a, $b) {
86
		//If they have the same depth, compare on index
87
		if ($a->depth === $b->depth) return $a->index < $b->index ? -1 : 1;
88
89
		return ($a->depth < $b->depth) ? -1 : 1;
90
	}
91
92
	private function stripComments($str) {
93
		$pos = 0;
94
		while (($pos = strpos($str, '/*', $pos)) !== false) {
95
			$end = strpos($str, '*/', $pos);
96
			$str = substr_replace($str, '', $pos, $end-$pos+2);
97
		}
98
99
		return $str;
100
	}
101
102
	private function getProperties($str) {
103
		$stringExtractor = new StringExtractor($str);
104
		$rules = explode(';', $stringExtractor);
105
		$return = [];
106
107
		foreach ($rules as $rule) {
108
			if (trim($rule) === '') continue;
109
			$parts = explode(':', $rule, 2);
110
111
			$parts[1] = $stringExtractor->rebuild($parts[1]);
112
			$return[trim($parts[0])] = isset($parts[1]) ? trim($parts[1]) : '';
113
		}
114
115
		return $return;
116
	}
117
}
118