2013-10-02 18:14:53 +02:00
|
|
|
<?php
|
|
|
|
namespace Rails\ActionView;
|
|
|
|
|
|
|
|
use Rails;
|
|
|
|
use Rails\ActionView\Template\Exception;
|
|
|
|
|
|
|
|
class Template extends Base
|
|
|
|
{
|
|
|
|
protected $_filename;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Layout filename.
|
|
|
|
*/
|
|
|
|
protected $_layout;
|
|
|
|
|
|
|
|
protected $_layout_name;
|
|
|
|
|
|
|
|
private $_params;
|
|
|
|
|
|
|
|
private $_template_token;
|
|
|
|
|
|
|
|
private $_initial_ob_level;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used by Inline responder.
|
|
|
|
*/
|
|
|
|
private $_inline_code;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* To render for first time, render() must be
|
|
|
|
* called, and this will be set to true.
|
|
|
|
* Next times render() is called, it will need
|
|
|
|
* parameters in order to work.
|
|
|
|
*/
|
|
|
|
private $_init_rendered = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Template file.
|
|
|
|
*/
|
2013-10-05 02:10:33 +02:00
|
|
|
private $_templateFile;
|
2013-10-02 18:14:53 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Full template path.
|
|
|
|
*/
|
2013-10-05 02:10:33 +02:00
|
|
|
private $_templateFilename;
|
2013-10-02 18:14:53 +02:00
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
private $_type;
|
2013-10-02 18:14:53 +02:00
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
private $_contents;
|
2013-10-02 18:14:53 +02:00
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
private $inline_code;
|
2013-10-02 18:14:53 +02:00
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
private $_lambda;
|
2013-10-02 18:14:53 +02:00
|
|
|
|
2013-10-14 04:45:38 +02:00
|
|
|
// /**
|
|
|
|
// * Template Extension to search for.
|
|
|
|
// * Eg: xml => file_name.xml.php
|
|
|
|
// * If null, no extra extension is appended.
|
|
|
|
// */
|
|
|
|
// private $tplExtension;
|
|
|
|
|
2013-10-02 18:14:53 +02:00
|
|
|
/**
|
|
|
|
* $render_params could be:
|
|
|
|
* - A string, that will be taken as "file".
|
|
|
|
* - An array specifying the type of parameter:
|
|
|
|
* - file: a file that will be required. Can be a relative path (to views folder) or absolute path.
|
|
|
|
* - contents: string that will be included to the layout, if any.
|
|
|
|
* - inline: inline code that will be evaluated.
|
|
|
|
* - lambda: pass an anonyomous function that will be bound to the template and ran.
|
|
|
|
*/
|
|
|
|
public function __construct($render_params, array $params = [], $locals = array())
|
|
|
|
{
|
|
|
|
if (!is_array($render_params)) {
|
|
|
|
$render_params = ['file' => $render_params];
|
|
|
|
}
|
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_type = key($render_params);
|
2013-10-02 18:14:53 +02:00
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
switch ($this->_type) {
|
2013-10-02 18:14:53 +02:00
|
|
|
case 'file':
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_templateFile = array_shift($render_params);
|
2013-10-02 18:14:53 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allows to receive the contents for this template, i.e.
|
|
|
|
* no processing is needed. It will just be added to the layout, if any.
|
|
|
|
*/
|
|
|
|
case 'contents':
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_contents = array_shift($render_params);
|
2013-10-02 18:14:53 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contents will be evalued.
|
|
|
|
*/
|
|
|
|
case 'inline':
|
|
|
|
$this->inline_code = array_shift($render_params);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'lambda':
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_lambda = array_shift($render_params);
|
2013-10-02 18:14:53 +02:00
|
|
|
break;
|
|
|
|
}
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_params = $params;
|
2013-10-02 18:14:53 +02:00
|
|
|
|
|
|
|
if (isset($params['locals'])) {
|
|
|
|
$locals = $params['locals'];
|
|
|
|
unset($params['locals']);
|
|
|
|
}
|
|
|
|
|
|
|
|
$locals && $this->setLocals($locals);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function renderContent()
|
|
|
|
{
|
|
|
|
Rails\ActionView\ViewHelpers::load();
|
|
|
|
$this->_set_initial_ob_level();
|
|
|
|
|
|
|
|
if (!$this->_init_rendered) {
|
|
|
|
ob_start();
|
|
|
|
$this->_init_rendered = true;
|
|
|
|
$this->_init_render();
|
|
|
|
$this->_buffer = ob_get_clean();
|
|
|
|
|
|
|
|
if (!$this->_validate_ob_level()) {
|
|
|
|
$status = ob_get_status();
|
|
|
|
throw new Exception\OutputLeakedException(
|
|
|
|
sprintf('Buffer level: %s; File: %s<br />Topmost buffer\'s contents: <br />%s',
|
|
|
|
$status['level'], substr($this->_filename, strlen(Rails::root()) + 1), htmlentities(ob_get_clean()))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function render(array $params)
|
|
|
|
{
|
|
|
|
$type = key($params);
|
|
|
|
switch ($type) {
|
|
|
|
case 'template':
|
|
|
|
$file = array_shift($params);
|
|
|
|
$template = new self(['file' => $file]);
|
|
|
|
$template->renderContent();
|
|
|
|
return $template->content();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function content($name = null)
|
|
|
|
{
|
|
|
|
if (!func_num_args())
|
|
|
|
return $this->_buffer;
|
|
|
|
else
|
|
|
|
return parent::content($name);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function contents()
|
|
|
|
{
|
|
|
|
return $this->content();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finally prints all the view.
|
|
|
|
*/
|
|
|
|
public function get_buffer_and_clean()
|
|
|
|
{
|
|
|
|
$buffer = $this->_buffer;
|
|
|
|
$this->_buffer = null;
|
|
|
|
return $buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function t($name, array $params = [])
|
|
|
|
{
|
|
|
|
if (is_array($name)) {
|
|
|
|
$params = $name;
|
|
|
|
$name = current($params);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strpos($name, '.') === 0) {
|
2013-10-05 02:10:33 +02:00
|
|
|
if (is_int(strpos($this->_templateFilename, Rails::config()->paths->views->toString()))) {
|
2013-10-02 18:14:53 +02:00
|
|
|
$parts = array();
|
|
|
|
$path = substr(
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_templateFilename, strlen(Rails::config()->paths->views) + 1,
|
|
|
|
strlen(pathinfo($this->_templateFilename, PATHINFO_BASENAME)) * -1
|
2013-10-02 18:14:53 +02:00
|
|
|
)
|
2013-10-05 02:10:33 +02:00
|
|
|
. pathinfo($this->_templateFilename, PATHINFO_FILENAME);
|
2013-10-02 18:14:53 +02:00
|
|
|
|
|
|
|
foreach (explode('/', $path) as $part) {
|
|
|
|
$parts[] = ltrim($part, '_');
|
|
|
|
}
|
|
|
|
$name = implode('.', $parts) . $name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return parent::t($name, $params);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function _init_render()
|
|
|
|
{
|
2013-10-05 02:10:33 +02:00
|
|
|
$layout_wrap = !empty($this->_params['layout']);
|
2013-10-02 18:14:53 +02:00
|
|
|
|
|
|
|
if ($layout_wrap) {
|
2013-10-05 02:10:33 +02:00
|
|
|
$layout_file = $this->resolve_layout_file($this->_params['layout']);
|
2013-10-02 18:14:53 +02:00
|
|
|
|
|
|
|
if (!is_file($layout_file))
|
|
|
|
throw new Exception\LayoutMissingException(
|
|
|
|
sprintf("Missing layout '%s' [ file => %s ]",
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_params['layout'], $layout_file)
|
2013-10-02 18:14:53 +02:00
|
|
|
);
|
|
|
|
ob_start();
|
|
|
|
}
|
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
switch ($this->_type) {
|
2013-10-02 18:14:53 +02:00
|
|
|
case 'file':
|
|
|
|
$this->build_template_filename();
|
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
if (!is_file($this->_templateFilename)) {
|
2013-10-02 18:14:53 +02:00
|
|
|
throw new Exception\TemplateMissingException(
|
2013-10-05 02:10:33 +02:00
|
|
|
sprintf("Missing template file %s", $this->_templateFilename)
|
2013-10-02 18:14:53 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-10-05 02:10:33 +02:00
|
|
|
require $this->_templateFilename;
|
2013-10-02 18:14:53 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'contents':
|
2013-10-05 02:10:33 +02:00
|
|
|
echo $this->_contents;
|
2013-10-02 18:14:53 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'inline':
|
|
|
|
eval('?>' . $this->inline_code . '<?php ');
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'lambda':
|
2013-10-05 02:10:33 +02:00
|
|
|
$lambda = $this->_lambda;
|
|
|
|
$this->_lambda = null;
|
2013-10-02 18:14:53 +02:00
|
|
|
$lambda = $lambda->bindTo($this);
|
|
|
|
$lambda();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($layout_wrap) {
|
|
|
|
$this->_buffer = ob_get_clean();
|
|
|
|
$layout = new Layout($layout_file, ['contents' => $this->_buffer], $this->locals);
|
|
|
|
$layout->renderContent();
|
|
|
|
echo $layout->content();
|
|
|
|
// require $layout_file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function _set_initial_ob_level()
|
|
|
|
{
|
|
|
|
$status = ob_get_status();
|
|
|
|
$this->_initial_ob_level = $this->_get_ob_level();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function _validate_ob_level()
|
|
|
|
{
|
|
|
|
$status = ob_get_status();
|
|
|
|
return $this->_initial_ob_level == $this->_get_ob_level();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function resolve_layout_file($layout)
|
|
|
|
{
|
|
|
|
if (strpos($layout, '/') === 0 || preg_match('/^[A-Za-z]:(?:\\\|\/)/', $layout))
|
|
|
|
return $layout;
|
|
|
|
else {
|
|
|
|
if (is_int(strpos($layout, '.')))
|
|
|
|
return Rails::config()->paths->layouts . '/' . $layout;
|
|
|
|
else
|
|
|
|
return Rails::config()->paths->layouts . '/' . $layout . '.php';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function build_template_filename()
|
|
|
|
{
|
2013-10-05 02:10:33 +02:00
|
|
|
$template = $this->_templateFile;
|
2013-10-02 18:14:53 +02:00
|
|
|
if (strpos($template, '/') === 0 || preg_match('/^[A-Za-z]:(?:\\\|\/)/', $template))
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_templateFilename = $template;
|
2013-10-02 18:14:53 +02:00
|
|
|
else {
|
|
|
|
if (is_int(strpos($template, '.'))) {
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_templateFilename = Rails::config()->paths->views . '/' . $template;
|
2013-10-02 18:14:53 +02:00
|
|
|
} else {
|
2013-10-05 02:10:33 +02:00
|
|
|
$this->_templateFilename = Rails::config()->paths->views . '/' . $template . '.php';
|
2013-10-02 18:14:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function _get_ob_level()
|
|
|
|
{
|
|
|
|
$status = ob_get_status();
|
|
|
|
return !empty($status['level']) ? $status['level'] : 0;
|
|
|
|
}
|
|
|
|
}
|