| 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\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 $xPath; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | 	private $valueParser; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | 	private $sheetLoader; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | 	private $file; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  | 	private $rules; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  | 	public function __construct($tss, CssToXpath $xPath, Value $valueParser, \Transphporm\FilePath $filePath, \Transphporm\SheetLoader\SheetLoader $sheetLoader, $file = null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  | 		$this->xPath = $xPath; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  | 		$this->valueParser = $valueParser; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  | 		$this->filePath = $filePath; | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  | 		$this->sheetLoader = $sheetLoader; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  | 		$this->file = $file; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  | 		$this->tss = (new Tokenizer($tss))->getTokens(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  | 	public function parse($indexStart = 0) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  | 		if (!empty($this->rules)) return $this->rules['rules']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  | 		$rules = $this->parseTokens($indexStart); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  | 		$this->checkError($rules); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  | 		return $rules; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  | 	private function parseTokens($indexStart) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  | 		$this->rules = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  | 		foreach (new TokenFilterIterator($this->tss, [Tokenizer::WHITESPACE]) as $token) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  | 			if ($processing = $this->processingInstructions($token, count($this->rules)+$indexStart)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  | 				$this->rules = array_merge($this->rules, $processing); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  | 			else if (!in_array($token['type'], [Tokenizer::NEW_LINE, Tokenizer::AT_SIGN])) $this->addRules($token, $indexStart++); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  | 		return $this->rules; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  | 	private function addRules($token, $indexStart) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  | 		$selector = $this->tss->from($token['type'], true)->to(Tokenizer::OPEN_BRACE); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  | 		$this->tss->skip(count($selector)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  | 		if (count($selector) === 0) return; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  | 		$newRules = $this->cssToRules($selector, count($this->rules)+$indexStart, $this->getProperties($this->tss->current()['value']), $token['line']); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  | 		$this->rules = $this->writeRule($this->rules, $newRules); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  | 	private function checkError($rules) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  | 		if (empty($rules) && count($this->tss) > 0) throw new \Exception('No TSS rules parsed'); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  | 	private function CssToRules($selector, $index, $properties, $line) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  | 		$parts = $selector->trim()->splitOnToken(Tokenizer::ARG); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  | 		$rules = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  | 		foreach ($parts as $part) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  | 			$serialized = serialize($part->removeLine()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  | 			$rules[$serialized] = new \Transphporm\Rule($this->xPath->getXpath($part), $this->xPath->getPseudo($part), $this->xPath->getDepth($part), $index, $this->file, $line); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  | 			$rules[$serialized]->properties = $properties; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  | 		return $rules; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  | 	} | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 68 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 69 |  |  | 	private function writeRule($rules, $newRules) { | 
            
                                                                        
                            
            
                                    
            
            
                | 70 |  |  | 		foreach ($newRules as $selector => $newRule) { | 
            
                                                                        
                            
            
                                    
            
            
                | 71 |  |  | 			if (isset($rules[$selector])) { | 
            
                                                                        
                            
            
                                    
            
            
                | 72 |  |  | 				$newRule->properties = array_merge($rules[$selector]->properties, $newRule->properties); | 
            
                                                                        
                            
            
                                    
            
            
                | 73 |  |  | 				$newRule->index = $rules[$selector]->index; | 
            
                                                                        
                            
            
                                    
            
            
                | 74 |  |  | 			} | 
            
                                                                        
                            
            
                                    
            
            
                | 75 |  |  | 			$rules[$selector] = $newRule; | 
            
                                                                        
                            
            
                                    
            
            
                | 76 |  |  | 		} | 
            
                                                                        
                            
            
                                    
            
            
                | 77 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 78 |  |  | 		return $rules; | 
            
                                                                        
                            
            
                                    
            
            
                | 79 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  | 	private function processingInstructions($token, $indexStart) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  | 		if ($token['type'] !== Tokenizer::AT_SIGN) return false; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  | 		$tokens = $this->tss->from(Tokenizer::AT_SIGN, false)->to(Tokenizer::SEMI_COLON, false); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  | 		$funcName = $tokens->from(Tokenizer::NAME, true)->read(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  | 		$funcToks = $tokens->from(Tokenizer::NAME); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  | 		$args = $this->valueParser->parseTokens($funcToks); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  | 		$rules = $this->$funcName($args, $indexStart, $funcToks); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  | 		$this->tss->skip(count($tokens)+2); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  | 		return $rules; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  | 	private function import($args, $indexStart, $tokens) { | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  | 		$fileName = $this->filePath->getFilePath($args[0]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  | 		$this->sheetLoader->addImport($fileName); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  | 		$tssFile = new \Transphporm\SheetLoader\TSSString(file_get_contents($fileName), $this->filePath); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  | 		return $tssFile->getRules($this->xPath, $this->valueParser, $this->sheetLoader, $indexStart); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  | 	private function cacheKey($args, $indexStart, $tokens) { | 
                            
                    |  |  |  | 
                                                                                        
                                                                                            
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  | 		$this->sheetLoader->setCacheKey($tokens); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  | 	private function getProperties($tokens) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |         $rules = $tokens->splitOnToken(Tokenizer::SEMI_COLON); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  |         $return = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 |  |  |         foreach ($rules as $rule) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |             $name = $rule->from(Tokenizer::NAME, true)->to(Tokenizer::COLON)->read(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |             $return[$name] = $rule->from(Tokenizer::COLON)->trim(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |         return $return; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |     } | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 116 |  |  | } | 
            
                                                        
            
                                    
            
            
                | 117 |  |  |  | 
            
                        
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: