engine = $engine; $this->name = new Name($engine, $name); $this->data($this->engine->getData($name)); } /** * Magic method used to call extension functions. * @param string $name * @param array $arguments * @return mixed */ public function __call($name, $arguments) { return $this->engine->getFunction($name)->call($this, $arguments); } /** * Alias for render() method. * @throws \Throwable * @throws \Exception * @return string */ public function __toString() { return $this->render(); } /** * Assign or get template data. * @param array $data * @return mixed */ public function data(array $data = null) { if (is_null($data)) { return $this->data; } $this->data = array_merge($this->data, $data); } /** * Check if the template exists. * @return boolean */ public function exists() { try { ($this->engine->getResolveTemplatePath())($this->name); return true; } catch (TemplateNotFound $e) { return false; } } /** * Get the template path. * @return string */ public function path() { try { return ($this->engine->getResolveTemplatePath())($this->name); } catch (TemplateNotFound $e) { return $e->paths()[0]; } } /** * Render the template and layout. * @param array $data * @throws \Throwable * @throws \Exception * @return string */ public function render(array $data = array()) { $this->data($data); $path = ($this->engine->getResolveTemplatePath())($this->name); try { $level = ob_get_level(); ob_start(); (function() { extract($this->data); include func_get_arg(0); })($path); $content = ob_get_clean(); if (isset($this->layoutName)) { $layout = $this->engine->make($this->layoutName); $layout->sections = array_merge($this->sections, array('content' => $content)); $content = $layout->render($this->layoutData); } return $content; } catch (Throwable $e) { while (ob_get_level() > $level) { ob_end_clean(); } throw $e; } } /** * Set the template's layout. * @param string $name * @param array $data * @return null */ public function layout($name, array $data = array()) { $this->layoutName = $name; $this->layoutData = $data; } /** * Start a new section block. * @param string $name * @return null */ public function start($name) { if ($name === 'content') { throw new LogicException( 'The section name "content" is reserved.' ); } if ($this->sectionName) { throw new LogicException('You cannot nest sections within other sections.'); } $this->sectionName = $name; ob_start(); } /** * Start a new section block in APPEND mode. * @param string $name * @return null */ public function push($name) { $this->appendSection = true; /* for backward compatibility */ $this->sectionMode = self::SECTION_MODE_APPEND; $this->start($name); } /** * Start a new section block in PREPEND mode. * @param string $name * @return null */ public function unshift($name) { $this->appendSection = false; /* for backward compatibility */ $this->sectionMode = self::SECTION_MODE_PREPEND; $this->start($name); } /** * Stop the current section block. * @return null */ public function stop() { if (is_null($this->sectionName)) { throw new LogicException( 'You must start a section before you can stop it.' ); } if (!isset($this->sections[$this->sectionName])) { $this->sections[$this->sectionName] = ''; } switch ($this->sectionMode) { case self::SECTION_MODE_REWRITE: $this->sections[$this->sectionName] = ob_get_clean(); break; case self::SECTION_MODE_APPEND: $this->sections[$this->sectionName] .= ob_get_clean(); break; case self::SECTION_MODE_PREPEND: $this->sections[$this->sectionName] = ob_get_clean().$this->sections[$this->sectionName]; break; } $this->sectionName = null; $this->sectionMode = self::SECTION_MODE_REWRITE; $this->appendSection = false; /* for backward compatibility */ } /** * Alias of stop(). * @return null */ public function end() { $this->stop(); } /** * Returns the content for a section block. * @param string $name Section name * @param string $default Default section content * @return string|null */ public function section($name, $default = null) { if (!isset($this->sections[$name])) { return $default; } return $this->sections[$name]; } /** * Fetch a rendered template. * @param string $name * @param array $data * @return string */ public function fetch($name, array $data = array()) { return $this->engine->render($name, $data); } /** * Output a rendered template. * @param string $name * @param array $data * @return null */ public function insert($name, array $data = array()) { echo $this->engine->render($name, $data); } /** * Apply multiple functions to variable. * @param mixed $var * @param string $functions * @return mixed */ public function batch($var, $functions) { foreach (explode('|', $functions) as $function) { if ($this->engine->doesFunctionExist($function)) { $var = call_user_func(array($this, $function), $var); } elseif (is_callable($function)) { $var = call_user_func($function, $var); } else { throw new LogicException( 'The batch function could not find the "' . $function . '" function.' ); } } return $var; } /** * Escape string. * @param string $string * @param null|string $functions * @return string */ public function escape($string, $functions = null) { static $flags; if (!isset($flags)) { $flags = ENT_QUOTES | (defined('ENT_SUBSTITUTE') ? ENT_SUBSTITUTE : 0); } if ($functions) { $string = $this->batch($string, $functions); } return htmlspecialchars($string ?? '', $flags, 'UTF-8'); } /** * Alias to escape function. * @param string $string * @param null|string $functions * @return string */ public function e($string, $functions = null) { return $this->escape($string, $functions); } }