Completed
Push — master ( f5a4f7...862e70 )
by Richard
02:25
created

Sheet::stripComments()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 10
loc 10
rs 9.4285
cc 3
eloc 7
nc 3
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 $cache;
11
	private $tss;
12
	private $rules;
13
	private $file;
14
	private $valueParser;
15
	private $xPath;
16
	private $filePath;
17
	private $import = [];
18
19
	public function __construct($tss, CssToXpath $xPath, Value $valueParser, \Transphporm\TSSCache $cache, \Transphporm\FilePath $filePath) {
20
		$this->cache = $cache;
21
		$this->xPath = $xPath;
22
		$this->valueParser = $valueParser;
23
		$this->filePath = $filePath;
24
		if (is_file($tss)) {
25
			$this->file = $tss;
26
			$this->rules = $this->cache->load($tss);
27
			$this->filePath->addPath(dirname(realpath($tss)));
28
			if (empty($this->rules)) $tss = file_get_contents($tss);
29
			else return;
30
		}
31
		$this->tss = (new SheetTokenizer($tss))->getTokens();
32
	}
33
34
	public function parse($indexStart = 0) {
35
		if (!empty($this->rules)) return $this->rules['rules'];
36
		$rules = $this->parseTokens($indexStart);
37
		usort($rules, [$this, 'sortRules']);
38
		$this->checkError($rules);
39
		return $this->cache->write($this->file, $rules, $this->import);
40
	}
41
42
	private function parseTokens($indexStart) {
43
		$this->rules = [];
44
		foreach (new TokenFilterIterator($this->tss, [Tokenizer::WHITESPACE]) as $token) {
45
			if ($processing = $this->processingInstructions($token, count($this->rules)+$indexStart)) {
46
				$this->rules = array_merge($this->rules, $processing);
47
			}
48
			else if ($token['type'] !== Tokenizer::NEW_LINE) $this->addRules($token, $indexStart);
49
		}
50
		return $this->rules;
51
	}
52
53
	private function addRules($token, $indexStart) {
54
		$selector = $this->tss->from($token['type'], true)->to(Tokenizer::OPEN_BRACE);
55
		$this->tss->skip(count($selector));
56
		if (count($selector) === 0) return;
57
58
		$newRules = $this->cssToRules($selector, count($this->rules)+$indexStart, $this->getProperties($this->tss->current()['value']), $token['line']);
59
		$this->rules = $this->writeRule($this->rules, $newRules);
60
	}
61
62
	private function checkError($rules) {
63
		if (empty($rules) && count($this->tss) > 0) throw new \Exception('No TSS rules parsed');
64
	}
65
66
	private function CssToRules($selector, $index, $properties, $line) {
67
		$parts = $selector->trim()->splitOnToken(Tokenizer::ARG);
68
		$rules = [];
69
		foreach ($parts as $part) {
70
			$serialized = serialize($part->removeLine());
71
			$rules[$serialized] = new \Transphporm\Rule($this->xPath->getXpath($part), $this->xPath->getPseudo($part), $this->xPath->getDepth($part), $index++, $this->file, $line);
72
			$rules[$serialized]->properties = $properties;
73
		}
74
		return $rules;
75
	}
76
77
	private function writeRule($rules, $newRules) {
78
		foreach ($newRules as $selector => $newRule) {
79
80
			if (isset($rules[$selector])) {
81
				$newRule->properties = array_merge($rules[$selector]->properties, $newRule->properties);
82
			}
83
			$rules[$selector] = $newRule;
84
		}
85
86
		return $rules;
87
	}
88
89
	private function processingInstructions($token, $indexStart) {
90
		if ($token['type'] !== Tokenizer::AT_SIGN) return false;
91
		$tokens = $this->tss->from(Tokenizer::AT_SIGN, false)->to(Tokenizer::SEMI_COLON, false);
92
		$funcName = $tokens->from(Tokenizer::NAME, true)->read();
93
		$args = $this->valueParser->parseTokens($tokens->from(Tokenizer::NAME));
94
		$rules = $this->$funcName($args, $indexStart);
95
96
		$this->tss->skip(count($tokens)+2);
97
98
		return $rules;
99
	}
100
101
	private function import($args, $indexStart) {
102
		$fileName = $this->filePath->getFilePath($args[0]);
103
		$this->import[] = $fileName;
104
		$sheet = new Sheet($fileName, $this->xPath, $this->valueParser, $this->cache, $this->filePath);
105
		return $sheet->parse($indexStart);
106
	}
107
108
	private function sortRules($a, $b) {
109
		//If they have the same depth, compare on index
110
		if ($a->depth === $b->depth) return $a->index < $b->index ? -1 : 1;
111
112
		return ($a->depth < $b->depth) ? -1 : 1;
113
	}
114
115
	private function getProperties($tokens) {
116
        $rules = $tokens->splitOnToken(Tokenizer::SEMI_COLON);
117
118
        $return = [];
119
        foreach ($rules as $rule) {
120
            $name = $rule->from(Tokenizer::NAME, true)->to(Tokenizer::COLON)->read();
121
            $return[$name] = $rule->from(Tokenizer::COLON)->trim();
122
        }
123
124
        return $return;
125
    }
126
}
127