/** * Compiles template to PHP code. * @return string */ public function compile($name) { foreach ($this->onCompile ?: [] as $cb) { call_user_func(Helpers::checkCallback($cb), $this); } $this->onCompile = []; $source = $this->getLoader()->getContent($name); try { $tokens = $this->getParser()->setContentType($this->contentType)->parse($source); $code = $this->getCompiler()->setContentType($this->contentType)->compile($tokens, $this->getTemplateClass($name)); } catch (\Exception $e) { if (!$e instanceof CompileException) { $e = new CompileException("Thrown exception '{$e->getMessage()}'", NULL, $e); } $line = isset($tokens) ? $this->getCompiler()->getLine() : $this->getParser()->getLine(); throw $e->setSource($source, $line, $name); } if (!preg_match('#\\n|\\?#', $name)) { $code = "<?php\n// source: {$name}\n?>" . $code; } $code = Helpers::optimizePhp($code); return $code; }
/** * @return string */ private function loadTemplate($name) { $code = $this->compile($name); try { if (@eval('?>' . $code) === FALSE) { // @ is escalated to exception $error = error_get_last(); $e = new CompileException('Error in template: ' . $error['message']); throw $e->setSource($code, $error['line'], $name . ' (compiled)'); } } catch (\ParseError $e) { $e = new CompileException('Error in template: ' . $e->getMessage(), 0, $e); throw $e->setSource($code, $e->getLine(), $name . ' (compiled)'); } return $code; }
/** * Vyhození chyby * * @param string * @param int * @return void */ protected function throwError($message, $line) { $exception = new CompileException($message); $exception->setLine($line); throw $exception; }
/** * @return void */ private function loadTemplate($name) { if (!$this->tempDirectory) { $code = $this->compile($name); if (@eval('?>' . $code) === FALSE) { // @ is escalated to exception $error = error_get_last(); $e = new CompileException('Error in template: ' . $error['message']); throw $e->setSource($code, $error['line'], "{$name} (compiled)"); } return; } $file = $this->getCacheFile($name); if (!$this->isExpired($file, $name) && @(include $file) !== FALSE) { // @ - file may not exist return; } if (!is_dir($this->tempDirectory)) { @mkdir($this->tempDirectory); // @ - directory may already exist } $handle = fopen("{$file}.lock", 'c+'); if (!$handle || !flock($handle, LOCK_EX)) { throw new \RuntimeException("Unable to acquire exclusive lock '{$file}.lock'."); } if (!is_file($file) || $this->isExpired($file, $name)) { $code = $this->compile($name); if (file_put_contents("{$file}.tmp", $code) !== strlen($code) || !rename("{$file}.tmp", $file)) { @unlink("{$file}.tmp"); // @ - file may not exist throw new \RuntimeException("Unable to create '{$file}'."); } } if ((include $file) === FALSE) { throw new \RuntimeException("Unable to load '{$file}'."); } flock($handle, LOCK_UN); }
/** * Redukce stromu * * @param Block * @param array * @param array * @return void */ protected function reduceBlock(Block $block, array $selectors = array(), array $variables = array()) { //inicializace nové vrstvy proměnných if (!$block instanceof Main) { $this->variables[] = array(); foreach ($variables as $variable) { try { $this->saveVariable($variable[0], $this->reduceValue($variable[1]), $block instanceof Mixin); } catch (CompileException $e) { throw $e->setLine(end($variable)); } } } if ($block instanceof NestedRule) { $group = array_shift($selectors); $selectors = $this->combineSelectors($this->replaceVariables($block->prefixes), $selectors); $selectors = $this->combineSelectors($selectors, $this->replaceVariables($block->selectors), $group); array_unshift($selectors, count($block->selectors)); } $reduced = $this->getReduced($block, $selectors); foreach ($block->properties as $property) { if (is_array($property)) { try { if ($property[0] == Compiler::$prefixes['variable']) { if (count($property) == 5) { //zápis do pole $this->saveToMap($property[1], $this->valueToIndex($property[4]), $this->reduceValue($property[2])); } else { $this->saveVariable($property[1], $this->reduceValue($property[2])); } } elseif ($property[0] == Compiler::$prefixes['mixin']) { $this->callMixin($property[1], $this->reduceValue($property[2]), $selectors); } elseif ($property[0] == Compiler::$prefixes['none'] || $property[0] == Compiler::$prefixes['important'] || $property[0] == Compiler::$prefixes['raw']) { if ($reduced instanceof Main || $reduced instanceof Media || $reduced instanceof Rule && $this->emptySelectors($selectors)) { throw new CompileException("Vlastnost nemůže být v globálním bloku ani v @media bloku"); } $reduced->properties[] = array($property[0], $property[1], $this->reduceValue($property[2])); } elseif ($property[0] == Compiler::$prefixes['special'] && $property[1] == 'include') { if ($this->inMedia || $reduced instanceof Rule && !$this->emptySelectors($selectors)) { throw new CompileException("Include může být jen v globálním bloku"); } $this->callInclude($property); } elseif ($property[0] == Compiler::$prefixes['special'] && $property[1] == 'import') { if ($this->inMedia || $reduced instanceof Rule && !$this->emptySelectors($selectors)) { throw new CompileException("Import může být jen v globálním bloku"); } $path = $this->reduceValue($property[2]); if ($path[0] !== 'string') { throw new CompileException("Název vkládaného souboru musí být řetězec"); } $media = $this->convertMedia($this->reduceValue($property[3])); $context =& $this->getReducedContext(); $context[] = array('import', $path, $media); } elseif ($property[0] == Compiler::$prefixes['special'] && $property[1] == 'charset') { if (!$reduced instanceof Main) { throw new CompileException("Charset může být jen v globálním bloku"); } $value = $this->reduceValue($property[2]); if ($value[0] !== 'string') { throw new CompileException("Název kódování musí být řetězec"); } $context =& $this->getReducedContext(); $context[] = array('charset', $value); } else { throw new \Exception("Neimplementováno"); } } catch (CompileException $e) { throw $e->setLine(end($property)); } } elseif ($property instanceof NestedRule) { if ($reduced instanceof AtRule && !$reduced instanceof Media) { throw new CompileException("S výjimkou @media at-rules nesmí obsahovat pravidlo"); } $this->callBlock($property, $selectors); } elseif ($property instanceof Mixin) { if ($reduced instanceof AtRule) { throw new CompileException("At-rules nesmí obsahovat mixin"); } if (array_key_exists($property->name, $this->mixins)) { $e = new CompileException("Mixin '{$property->name}' již existuje"); throw $e->setLine($property->line); } $property->file = $this->getFile(); $this->mixins[$property->name] = $property; } elseif ($property instanceof Media) { try { $property->media = $this->convertMedia($this->reduceValue($property->media)); } catch (CompileException $e) { throw $e->setLine($property->line); } $this->reduceBlock($property); } elseif ($property instanceof FontFace) { $this->reduceBlock($property); } else { throw new \Exception("Neimplementováno"); } } if ($block instanceof Media) { $this->inMedia = FALSE; } //zrušení nejvyšší vrstvy proměnných if (!$block instanceof Main) { array_pop($this->variables); } }