From c3aa9994c8777e9554cd62d0f41afccc5de94c50 Mon Sep 17 00:00:00 2001 From: Uwe Tews Date: Thu, 1 Jan 2015 23:34:29 +0100 Subject: [PATCH] rework source resource handling - move class Smarty_Template_Source into its own file - impelement all source processing into the classes it better belongs to --- libs/Autoloader.php | 36 +- .../smarty_internal_resource_eval.php | 6 +- .../smarty_internal_resource_extends.php | 2 +- .../smarty_internal_resource_file.php | 141 +++- .../smarty_internal_resource_php.php | 21 +- .../smarty_internal_resource_registered.php | 2 +- .../smarty_internal_resource_stream.php | 4 +- .../smarty_internal_resource_string.php | 6 +- libs/sysplugins/smarty_resource.php | 701 +----------------- libs/sysplugins/smarty_resource_custom.php | 2 +- .../sysplugins/smarty_resource_recompiled.php | 7 + .../sysplugins/smarty_resource_uncompiled.php | 28 + libs/sysplugins/smarty_template_source.php | 276 +++++++ 13 files changed, 533 insertions(+), 699 deletions(-) create mode 100644 libs/sysplugins/smarty_template_source.php diff --git a/libs/Autoloader.php b/libs/Autoloader.php index d0215d9f..0ae0907b 100644 --- a/libs/Autoloader.php +++ b/libs/Autoloader.php @@ -10,12 +10,10 @@ * * @package Smarty * @author Uwe Tews - * * Usage: * include '...path/Autoloader.php'; * Smarty_Autoloader::register(); * $smarty = new Smarty(); - * * Note: This autoloader is not needed if you use Composer. * Composer will automatically add the classes of the Smarty package to it common autoloader. */ @@ -49,18 +47,20 @@ class Smarty_Autoloader ); private static $syspluginsClasses = array( - 'smarty_config_source' => true, - 'smarty_security' => true, - 'smarty_cacheresource' => true, - 'smarty_compiledresource' => true, - 'smarty_cacheresource_custom' => true, - 'smarty_cacheresource_keyvaluestore' => true, - 'smarty_resource' => true, - 'smarty_resource_custom' => true, - 'smarty_resource_uncompiled' => true, - 'smarty_resource_recompiled' => true, - 'smartyexception' => true, - 'smartycompilerexception' => true, + 'smarty_config_source' => true, + 'smarty_security' => true, + 'smarty_cacheresource' => true, + 'smarty_compiledresource' => true, + 'smarty_cacheresource_custom' => true, + 'smarty_cacheresource_keyvaluestore' => true, + 'smarty_resource' => true, + 'smarty_resource_custom' => true, + 'smarty_resource_uncompiled' => true, + 'smarty_resource_recompiled' => true, + 'smarty_template_source' => true, + 'smarty_template_compiled' => true, + 'smartyexception' => true, + 'smartycompilerexception' => true, ); /** @@ -132,9 +132,9 @@ class Smarty_Autoloader if (isset(self::$rootClasses[$class])) { $file = self::$SMARTY_DIR . self::$rootClasses[$class]; if (!self::$fileCheck || is_file($file)) { - require $file; - return; - } + require $file; + return; + } } self::$unknown[$class] = true; return; @@ -144,7 +144,7 @@ class Smarty_Autoloader require $file; return; } - self::$unknown[$class] = true; + self::$unknown[$class] = true; return; } } diff --git a/libs/sysplugins/smarty_internal_resource_eval.php b/libs/sysplugins/smarty_internal_resource_eval.php index 1c64b7cf..aca71035 100644 --- a/libs/sysplugins/smarty_internal_resource_eval.php +++ b/libs/sysplugins/smarty_internal_resource_eval.php @@ -73,11 +73,11 @@ class Smarty_Internal_Resource_Eval extends Smarty_Resource_Recompiled * * @param Smarty $smarty Smarty instance * @param string $resource_name resource_name to make unique - * @param boolean $is_config flag for config resource + * @param boolean $isConfig flag for config resource * * @return string unique resource name */ - protected function buildUniqueResourceName(Smarty $smarty, $resource_name, $is_config = false) + public function buildUniqueResourceName(Smarty $smarty, $resource_name, $isConfig = false) { return get_class($this) . '#' . $this->decode($resource_name); } @@ -89,7 +89,7 @@ class Smarty_Internal_Resource_Eval extends Smarty_Resource_Recompiled * * @return string resource's basename */ - protected function getBasename(Smarty_Template_Source $source) + public function getBasename(Smarty_Template_Source $source) { return ''; } diff --git a/libs/sysplugins/smarty_internal_resource_extends.php b/libs/sysplugins/smarty_internal_resource_extends.php index c78dba51..16974ef2 100644 --- a/libs/sysplugins/smarty_internal_resource_extends.php +++ b/libs/sysplugins/smarty_internal_resource_extends.php @@ -34,7 +34,7 @@ class Smarty_Internal_Resource_Extends extends Smarty_Resource */ public function populate(Smarty_Template_Source $source, Smarty_Internal_Template $_template = null) { - $uid = ''; + $uid = sha1(getcwd()); $sources = array(); $components = explode('|', $source->name); $exists = true; diff --git a/libs/sysplugins/smarty_internal_resource_file.php b/libs/sysplugins/smarty_internal_resource_file.php index 0abdc449..9faaf683 100644 --- a/libs/sysplugins/smarty_internal_resource_file.php +++ b/libs/sysplugins/smarty_internal_resource_file.php @@ -17,6 +17,145 @@ */ class Smarty_Internal_Resource_File extends Smarty_Resource { + + /** + * build template filepath by traversing the template_dir array + * + * @param Smarty_Template_Source $source source object + * @param Smarty_Internal_Template $_template template object + * + * @return string fully qualified filepath + * @throws SmartyException + */ + protected function buildFilepath(Smarty_Template_Source $source, Smarty_Internal_Template $_template = null) + { + $file = str_replace(array('\\', '/./'), '/', $source->name); + if ($source->isConfig) { + $_directories = $source->smarty->getConfigDir(); + } else { + $_directories = $source->smarty->getTemplateDir(); + } + preg_match('#^((?P[\/]|[a-zA-Z]:[\/])|(\[(?P[^\]]+)\])|((?P\.[\/])?(?P(\.\.[\/])*))|(?P[\/]))?(?P.+)$#', $file, $fileMatch); + // go relative to a given template? + if ($_template && $_template->parent instanceof Smarty_Internal_Template && (!empty($fileMatch['rel1']) || !empty($fileMatch['rel2']))) { + if ($_template->parent->source->type != 'file' && $_template->parent->source->type != 'extends' && !$_template->parent->allow_relative_path) { + throw new SmartyException("Template '{$file}' cannot be relative to template of resource type '{$_template->parent->source->type}'"); + } + $path = dirname($_template->parent->source->filepath); + if (!preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $path)) { + // the path gained from the parent template is relative to the current working directory + // as expansions (like include_path) have already been done + $path = getcwd() . '/' . $path; + } + // normalize path + $path = str_replace(array('\\', './'), array('/', ''), $path); + // simple relative + if (!empty($fileMatch['rel1'])) { + $file = $path . '/' . $fileMatch['file']; + } else { + for ($i = 1; $i <= substr_count($fileMatch['rel2'], '../'); $i ++) { + $path = substr($path, 0, strrpos($path, '/')); + } + $file = $path . '/' . $fileMatch['file']; + } + // files relative to a template only get one shot + return $this->fileExists($source, $file) ? $file : false; + } + + $_filepath = null; + // template_dir index? + if (!empty($fileMatch['index'])) { + $index = $fileMatch['index']; + $_directory = null; + // try string indexes + if (isset($_directories[$index])) { + $_directory = $_directories[$index]; + } elseif (is_numeric($index)) { + // try numeric index + $index = (int) $index; + if (isset($_directories[$index])) { + $_directory = $_directories[$index]; + } else { + // try at location index + $keys = array_keys($_directories); + $_directory = $_directories[$keys[$index]]; + } + } + if ($_directory) { + $_filepath = $_directory . $fileMatch['file']; + if ($this->fileExists($source, $_filepath)) { + return $_filepath; + } + } + } + + // relative file name? + if (empty($fileMatch['absolute'])) { + foreach ($_directories as $_directory) { + if (!empty($fileMatch['rel2'])) { + for ($i = 1; $i <= substr_count($fileMatch['rel2'], '../') + 1; $i ++) { + $_directory = substr($_directory, 0, strrpos($_directory, '/')); + } + $_filepath = $_directory . '/' . $fileMatch['file']; + } else { + $_filepath = $_directory . $fileMatch['file']; + } + if ($this->fileExists($source, $_filepath)) { + return $_filepath; + } + if ($source->smarty->use_include_path && !preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $_directory)) { + // try PHP include_path + if (function_exists('stream_resolve_include_path')) { + $_filepath = stream_resolve_include_path($_filepath); + } else { + $_filepath = Smarty_Internal_Get_Include_Path::getIncludePath($_filepath); + } + + if ($_filepath !== false) { + if ($this->fileExists($source, $_filepath)) { + return $_filepath; + } + } + } + } + } else { + // try absolute filepath + if ($this->fileExists($source, $file)) { + return $file; + } + } + // Could be relative to cwd + if (empty($fileMatch['rel1'])) { + $file = getcwd() . '/' . $fileMatch['file']; + } else { + $path = getcwd(); + for ($i = 1; $i <= substr_count($fileMatch['rel2'], '../'); $i ++) { + $path = substr($path, 0, strrpos($path, '/')); + } + $file = $path . '/' . $fileMatch['file']; + } + if ($this->fileExists($source, $file)) { + return $file; + } + + // give up + return false; + } + + /** + * test is file exists and save timestamp + * + * @param Smarty_Template_Source $source source object + * @param string $file file name + * + * @return bool true if file exists + */ + protected function fileExists(Smarty_Template_Source $source, $file) + { + $source->timestamp = is_file($file) ? @filemtime($file) : false; + return $source->exists = !!$source->timestamp; + } + /** * populate Source Object with meta data from Resource * @@ -32,7 +171,7 @@ class Smarty_Internal_Resource_File extends Smarty_Resource $source->smarty->security_policy->isTrustedResourceDir($source->filepath); } - $source->uid = sha1(realpath($source->filepath)); + $source->uid = sha1(getcwd() . $source->filepath); if ($source->smarty->compile_check && !isset($source->timestamp)) { $source->timestamp = @filemtime($source->filepath); $source->exists = !!$source->timestamp; diff --git a/libs/sysplugins/smarty_internal_resource_php.php b/libs/sysplugins/smarty_internal_resource_php.php index 97ec9f5f..593ced75 100644 --- a/libs/sysplugins/smarty_internal_resource_php.php +++ b/libs/sysplugins/smarty_internal_resource_php.php @@ -9,8 +9,14 @@ * @author Uwe Tews * @author Rodney Rehm */ -class Smarty_Internal_Resource_Php extends Smarty_Resource_Uncompiled +class Smarty_Internal_Resource_PHP extends Smarty_Internal_Resource_File { + /** + * Flag that it's an uncompiled resource + * + * @var bool + */ + public $uncompiled = true; /** * container for short_open_tag directive's value before executing PHP templates * @@ -116,4 +122,17 @@ class Smarty_Internal_Resource_Php extends Smarty_Resource_Uncompiled include($source->filepath); ini_set('short_open_tag', $this->short_open_tag); } + + /** + * populate compiled object with compiled filepath + * + * @param Smarty_Template_Compiled $compiled compiled object + * @param Smarty_Internal_Template $_template template object (is ignored) + */ + public function populateCompiledFilepath(Smarty_Template_Compiled $compiled, Smarty_Internal_Template $_template) + { + $compiled->filepath = false; + $compiled->timestamp = false; + $compiled->exists = false; + } } diff --git a/libs/sysplugins/smarty_internal_resource_registered.php b/libs/sysplugins/smarty_internal_resource_registered.php index f12d843e..f7175dd9 100644 --- a/libs/sysplugins/smarty_internal_resource_registered.php +++ b/libs/sysplugins/smarty_internal_resource_registered.php @@ -91,7 +91,7 @@ class Smarty_Internal_Resource_Registered extends Smarty_Resource * * @return string resource's basename */ - protected function getBasename(Smarty_Template_Source $source) + public function getBasename(Smarty_Template_Source $source) { return basename($source->name); } diff --git a/libs/sysplugins/smarty_internal_resource_stream.php b/libs/sysplugins/smarty_internal_resource_stream.php index f7d1aac3..8a902268 100644 --- a/libs/sysplugins/smarty_internal_resource_stream.php +++ b/libs/sysplugins/smarty_internal_resource_stream.php @@ -70,11 +70,11 @@ class Smarty_Internal_Resource_Stream extends Smarty_Resource_Recompiled * * @param Smarty $smarty Smarty instance * @param string $resource_name resource_name to make unique - * @param boolean $is_config flag for config resource + * @param boolean $isConfig flag for config resource * * @return string unique resource name */ - protected function buildUniqueResourceName(Smarty $smarty, $resource_name, $is_config = false) + public function buildUniqueResourceName(Smarty $smarty, $resource_name, $isConfig = false) { return get_class($this) . '#' . $resource_name; } diff --git a/libs/sysplugins/smarty_internal_resource_string.php b/libs/sysplugins/smarty_internal_resource_string.php index bfa06997..75e99e6f 100644 --- a/libs/sysplugins/smarty_internal_resource_string.php +++ b/libs/sysplugins/smarty_internal_resource_string.php @@ -73,11 +73,11 @@ class Smarty_Internal_Resource_String extends Smarty_Resource * * @param Smarty $smarty Smarty instance * @param string $resource_name resource_name to make unique - * @param boolean $is_config flag for config resource + * @param boolean $isConfig flag for config resource * * @return string unique resource name */ - protected function buildUniqueResourceName(Smarty $smarty, $resource_name, $is_config = false) + public function buildUniqueResourceName(Smarty $smarty, $resource_name, $isConfig = false) { return get_class($this) . '#' . $this->decode($resource_name); } @@ -90,7 +90,7 @@ class Smarty_Internal_Resource_String extends Smarty_Resource * * @return string resource's basename */ - protected function getBasename(Smarty_Template_Source $source) + public function getBasename(Smarty_Template_Source $source) { return ''; } diff --git a/libs/sysplugins/smarty_resource.php b/libs/sysplugins/smarty_resource.php index b8b78ca6..93c95ca4 100644 --- a/libs/sysplugins/smarty_resource.php +++ b/libs/sysplugins/smarty_resource.php @@ -16,6 +16,25 @@ */ abstract class Smarty_Resource { + /** + * Source is bypassing compiler + * + * @var boolean + */ + public $uncompiled = false; + + /** + * Source must be recompiled on every occasion + * + * @var boolean + */ + public $recompiled = false; + /** + * resource handler object + * + * @var Smarty_Resource + */ + public $handler = null; /** * cache for Smarty_Template_Source instances * @@ -28,12 +47,6 @@ abstract class Smarty_Resource * @var array */ public static $compileds = array(); - /** - * cache for Smarty_Resource instances - * - * @var array - */ - public static $resources = array(); /** * resource types provided by the core * @@ -103,270 +116,19 @@ abstract class Smarty_Resource * * @param Smarty $smarty Smarty instance * @param string $resource_name resource_name to make unique - * @param boolean $is_config flag for config resource + * @param boolean $isConfig flag for config resource * * @return string unique resource name */ - protected function buildUniqueResourceName(Smarty $smarty, $resource_name, $is_config = false) + public function buildUniqueResourceName(Smarty $smarty, $resource_name, $isConfig = false) { - if ($is_config) { + if ($isConfig) { return get_class($this) . '#' . $smarty->joined_config_dir . '#' . $resource_name; } else { return get_class($this) . '#' . $smarty->joined_template_dir . '#' . $resource_name; } } - /** - * populate Compiled Object with compiled filepath - * - * @param Smarty_Template_Compiled $compiled compiled object - * @param Smarty_Internal_Template $_template template object - */ - public function populateCompiledFilepath(Smarty_Template_Compiled $compiled, Smarty_Internal_Template $_template) - { - $_compile_id = isset($_template->compile_id) ? preg_replace('![^\w\|]+!', '_', $_template->compile_id) : null; - $_filepath = $compiled->source->uid; - // if use_sub_dirs, break file into directories - if ($_template->smarty->use_sub_dirs) { - $_filepath = substr($_filepath, 0, 2) . DS - . substr($_filepath, 2, 2) . DS - . substr($_filepath, 4, 2) . DS - . $_filepath; - } - $_compile_dir_sep = $_template->smarty->use_sub_dirs ? DS : '^'; - if (isset($_compile_id)) { - $_filepath = $_compile_id . $_compile_dir_sep . $_filepath; - } - // caching token - if ($_template->caching) { - $_cache = '.cache'; - } else { - $_cache = ''; - } - $_compile_dir = $_template->smarty->getCompileDir(); - // set basename if not specified - $_basename = $this->getBasename($compiled->source); - if ($_basename === null) { - $_basename = basename(preg_replace('![^\w\/]+!', '_', $compiled->source->name)); - } - // separate (optional) basename by dot - if ($_basename) { - $_basename = '.' . $_basename; - } - - $compiled->filepath = $_compile_dir . $_filepath . '.' . $compiled->source->type . $_basename . $_cache . '.php'; - } - - /** - * Normalize Paths "foo/../bar" to "bar" - * - * @param string $_path path to normalize - * @param boolean $ds respect windows directory separator - * - * @return string normalized path - */ - protected function normalizePath($_path, $ds = true) - { - if ($ds) { - // don't we all just love windows? - $_path = str_replace('\\', '/', $_path); - } - - $offset = 0; - - // resolve simples - $_path = preg_replace('#/\./(\./)*#', '/', $_path); - // resolve parents - while (true) { - $_parent = strpos($_path, '/../', $offset); - if (!$_parent) { - break; - } elseif ($_path[$_parent - 1] === '.') { - $offset = $_parent + 3; - continue; - } - - $_pos = strrpos($_path, '/', $_parent - strlen($_path) - 1); - if ($_pos === false) { - // don't we all just love windows? - $_pos = $_parent; - } - - $_path = substr_replace($_path, '', $_pos, $_parent + 3 - $_pos); - } - - if ($ds && DS != '/') { - // don't we all just love windows? - $_path = str_replace('/', '\\', $_path); - } - - return $_path; - } - - /** - * build template filepath by traversing the template_dir array - * - * @param Smarty_Template_Source $source source object - * @param Smarty_Internal_Template $_template template object - * - * @return string fully qualified filepath - * @throws SmartyException if default template handler is registered but not callable - */ - protected function buildFilepath(Smarty_Template_Source $source, Smarty_Internal_Template $_template = null) - { - $file = $source->name; - if ($source instanceof Smarty_Config_Source) { - $_directories = $source->smarty->getConfigDir(); - $_default_handler = $source->smarty->default_config_handler_func; - } else { - $_directories = $source->smarty->getTemplateDir(); - $_default_handler = $source->smarty->default_template_handler_func; - } - - // go relative to a given template? - $_file_is_dotted = $file[0] == '.' && ($file[1] == '.' || $file[1] == '/' || $file[1] == "\\"); - if ($_template && $_template->parent instanceof Smarty_Internal_Template && $_file_is_dotted) { - if ($_template->parent->source->type != 'file' && $_template->parent->source->type != 'extends' && !$_template->parent->allow_relative_path) { - throw new SmartyException("Template '{$file}' cannot be relative to template of resource type '{$_template->parent->source->type}'"); - } - $file = dirname($_template->parent->source->filepath) . DS . $file; - $_file_exact_match = true; - if (!preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $file)) { - // the path gained from the parent template is relative to the current working directory - // as expansions (like include_path) have already been done - $file = getcwd() . DS . $file; - } - } - - // resolve relative path - if (!preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $file)) { - // don't we all just love windows? - $_path = DS . trim($file, '/'); - $_was_relative = true; - } else { - // don't we all just love windows? - $_path = str_replace('\\', '/', $file); - } - $_path = $this->normalizePath($_path, false); - if (DS != '/') { - // don't we all just love windows? - $_path = str_replace('/', '\\', $_path); - } - // revert to relative - if (isset($_was_relative)) { - $_path = substr($_path, 1); - } - - // this is only required for directories - $file = rtrim($_path, '/\\'); - - // files relative to a template only get one shot - if (isset($_file_exact_match)) { - return $this->fileExists($source, $file) ? $file : false; - } - - // template_dir index? - if (preg_match('#^\[(?P[^\]]+)\](?P.+)$#', $file, $match)) { - $_directory = null; - // try string indexes - if (isset($_directories[$match['key']])) { - $_directory = $_directories[$match['key']]; - } elseif (is_numeric($match['key'])) { - // try numeric index - $match['key'] = (int) $match['key']; - if (isset($_directories[$match['key']])) { - $_directory = $_directories[$match['key']]; - } else { - // try at location index - $keys = array_keys($_directories); - $_directory = $_directories[$keys[$match['key']]]; - } - } - - if ($_directory) { - $_file = substr($file, strpos($file, ']') + 1); - $_filepath = $_directory . $_file; - if ($this->fileExists($source, $_filepath)) { - return $_filepath; - } - } - } - - $_stream_resolve_include_path = function_exists('stream_resolve_include_path'); - - // relative file name? - if (!preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $file)) { - foreach ($_directories as $_directory) { - $_filepath = $_directory . $file; - if ($this->fileExists($source, $_filepath)) { - return $this->normalizePath($_filepath); - } - if ($source->smarty->use_include_path && !preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $_directory)) { - // try PHP include_path - if ($_stream_resolve_include_path) { - $_filepath = stream_resolve_include_path($_filepath); - } else { - $_filepath = Smarty_Internal_Get_Include_Path::getIncludePath($_filepath); - } - - if ($_filepath !== false) { - if ($this->fileExists($source, $_filepath)) { - return $this->normalizePath($_filepath); - } - } - } - } - } - - // try absolute filepath - if ($this->fileExists($source, $file)) { - return $file; - } - - // no tpl file found - if ($_default_handler) { - if (!is_callable($_default_handler)) { - if ($source instanceof Smarty_Config_Source) { - throw new SmartyException("Default config handler not callable"); - } else { - throw new SmartyException("Default template handler not callable"); - } - } - $_return = call_user_func_array($_default_handler, - array($source->type, $source->name, &$_content, &$_timestamp, $source->smarty)); - if (is_string($_return)) { - $source->timestamp = @filemtime($_return); - $source->exists = !!$source->timestamp; - - return $_return; - } elseif ($_return === true) { - $source->content = $_content; - $source->timestamp = $_timestamp; - $source->exists = true; - - return $_filepath; - } - } - - // give up - return false; - } - - /** - * test is file exists and save timestamp - * - * @param Smarty_Template_Source $source source object - * @param string $file file name - * - * @return bool true if file exists - */ - protected function fileExists(Smarty_Template_Source $source, $file) - { - $source->timestamp = is_file($file) ? @filemtime($file) : false; - - return $source->exists = !!$source->timestamp; - } - /** * Determine basename for compiled filename * @@ -374,7 +136,7 @@ abstract class Smarty_Resource * * @return string resource's basename */ - protected function getBasename(Smarty_Template_Source $source) + public function getBasename(Smarty_Template_Source $source) { return null; } @@ -399,15 +161,8 @@ abstract class Smarty_Resource if (isset($smarty->registered_resources[$type])) { if ($smarty->registered_resources[$type] instanceof Smarty_Resource) { $smarty->_resource_handlers[$type] = $smarty->registered_resources[$type]; - // note registered to smarty is not kept unique! - return $smarty->_resource_handlers[$type]; - } - - if (!isset(self::$resources['registered'])) { - self::$resources['registered'] = new Smarty_Internal_Resource_Registered(); - } - if (!isset($smarty->_resource_handlers[$type])) { - $smarty->_resource_handlers[$type] = self::$resources['registered']; + } else { + $smarty->_resource_handlers[$type] = new Smarty_Internal_Resource_Registered(); } return $smarty->_resource_handlers[$type]; @@ -415,25 +170,15 @@ abstract class Smarty_Resource // try sysplugins dir if (isset(self::$sysplugins[$type])) { - if (!isset(self::$resources[$type])) { - $_resource_class = 'Smarty_Internal_Resource_' . ucfirst($type); - self::$resources[$type] = new $_resource_class(); - } - - return $smarty->_resource_handlers[$type] = self::$resources[$type]; + $_resource_class = 'Smarty_Internal_Resource_' . ucfirst($type); + return $smarty->_resource_handlers[$type] = new $_resource_class(); } // try plugins dir $_resource_class = 'Smarty_Resource_' . ucfirst($type); if ($smarty->loadPlugin($_resource_class)) { - if (isset(self::$resources[$type])) { - return $smarty->_resource_handlers[$type] = self::$resources[$type]; - } - if (class_exists($_resource_class, false)) { - self::$resources[$type] = new $_resource_class(); - - return $smarty->_resource_handlers[$type] = self::$resources[$type]; + return $smarty->_resource_handlers[$type] = new $_resource_class(); } else { $smarty->registerResource($type, array( "smarty_resource_{$type}_source", @@ -441,7 +186,6 @@ abstract class Smarty_Resource "smarty_resource_{$type}_secure", "smarty_resource_{$type}_trusted" )); - // give it another try, now that the resource is registered properly return self::load($smarty, $type); } @@ -454,11 +198,7 @@ abstract class Smarty_Resource if (is_object($smarty->security_policy)) { $smarty->security_policy->isTrustedStream($type); } - if (!isset(self::$resources['stream'])) { - self::$resources['stream'] = new Smarty_Internal_Resource_Stream(); - } - - return $smarty->_resource_handlers[$type] = self::$resources['stream']; + return $smarty->_resource_handlers[$type] = new Smarty_Internal_Resource_Stream();; } // TODO: try default_(template|config)_handler @@ -478,7 +218,7 @@ abstract class Smarty_Resource * * @return void */ - protected static function parseResourceName($resource_name, $default_resource, &$name, &$type) + public static function parseResourceName($resource_name, $default_resource, &$name, &$type) { $parts = explode(':', $resource_name, 2); if (!isset($parts[1]) || !isset($parts[0][1])) { @@ -515,7 +255,7 @@ abstract class Smarty_Resource // TODO: optimize for Smarty's internal resource types $resource = Smarty_Resource::load($template->smarty, $type); // go relative to a given template? - $_file_is_dotted = $name[0] == '.' && ($name[1] == '.' || $name[1] == '/' || $name[1] == "\\"); + $_file_is_dotted = $name[0] == '.' && ($name[1] == '.' || $name[1] == '/'); if ($template instanceof Smarty_Internal_Template && $_file_is_dotted && ($template->source->type == 'file' || $template->parent->source->type == 'extends')) { $name = dirname($template->source->filepath) . DS . $name; } @@ -524,6 +264,7 @@ abstract class Smarty_Resource /** * initialize Source Object for given resource + * wrapper for backward compatibility to versions < 3.1.22 * Either [$_template] or [$smarty, $template_resource] must be specified * * @param Smarty_Internal_Template $_template template object @@ -534,383 +275,7 @@ abstract class Smarty_Resource */ public static function source(Smarty_Internal_Template $_template = null, Smarty $smarty = null, $template_resource = null) { - if ($_template) { - $smarty = $_template->smarty; - $template_resource = $_template->template_resource; - } - - // parse resource_name, load resource handler, identify unique resource name - self::parseResourceName($template_resource, $smarty->default_resource_type, $name, $type); - $resource = Smarty_Resource::load($smarty, $type); - // go relative to a given template? - $_file_is_dotted = isset($name[0]) && $name[0] == '.' && ($name[1] == '.' || $name[1] == '/' || $name[1] == "\\"); - if ($_file_is_dotted && isset($_template) && $_template->parent instanceof Smarty_Internal_Template && ($_template->parent->source->type == 'file' || $_template->parent->source->type == 'extends')) { - $name2 = dirname($_template->parent->source->filepath) . DS . $name; - } else { - $name2 = $name; - } - $unique_resource_name = $resource->buildUniqueResourceName($smarty, $name2); - - // check runtime cache - $_cache_key = 'template|' . $unique_resource_name; - if ($smarty->compile_id) { - $_cache_key .= '|' . $smarty->compile_id; - } - if (isset(self::$sources[$_cache_key])) { - return self::$sources[$_cache_key]; - } - - // create source - $source = new Smarty_Template_Source($resource, $smarty, $template_resource, $type, $name, $unique_resource_name); - $resource->populate($source, $_template); - - // runtime cache - self::$sources[$_cache_key] = $source; - - return $source; - } - - /** - * initialize Config Source Object for given resource - * - * @param Smarty_Internal_Config $_config config object - * - * @throws SmartyException - * @return Smarty_Config_Source Source Object - */ - public static function config(Smarty_Internal_Config $_config) - { - static $_incompatible_resources = array('eval' => true, 'string' => true, 'extends' => true, 'php' => true); - $config_resource = $_config->config_resource; - $smarty = $_config->smarty; - - // parse resource_name - self::parseResourceName($config_resource, $smarty->default_config_type, $name, $type); - - // make sure configs are not loaded via anything smarty can't handle - if (isset($_incompatible_resources[$type])) { - throw new SmartyException ("Unable to use resource '{$type}' for config"); - } - - // load resource handler, identify unique resource name - $resource = Smarty_Resource::load($smarty, $type); - $unique_resource_name = $resource->buildUniqueResourceName($smarty, $name, true); - - // check runtime cache - $_cache_key = 'config|' . $unique_resource_name; - if (isset(self::$sources[$_cache_key])) { - return self::$sources[$_cache_key]; - } - - // create source - $source = new Smarty_Config_Source($resource, $smarty, $config_resource, $type, $name, $unique_resource_name); - $resource->populate($source, null); - - // runtime cache - self::$sources[$_cache_key] = $source; - - return $source; + return Smarty_Template_Source::load($_template, $smarty, $template_resource); } } -/** - * Smarty Resource Data Object - * Meta Data Container for Template Files - * - * @package Smarty - * @subpackage TemplateResources - * @author Rodney Rehm - * @property integer $timestamp Source Timestamp - * @property boolean $exists Source Existence - * @property boolean $template Extended Template reference - * @property string $content Source Content - */ -class Smarty_Template_Source -{ - /** - * Name of the Class to compile this resource's contents with - * - * @var string - */ - public $compiler_class = null; - - /** - * Name of the Class to tokenize this resource's contents with - * - * @var string - */ - public $template_lexer_class = null; - - /** - * Name of the Class to parse this resource's contents with - * - * @var string - */ - public $template_parser_class = null; - - /** - * Unique Template ID - * - * @var string - */ - public $uid = null; - - /** - * Template Resource (Smarty_Internal_Template::$template_resource) - * - * @var string - */ - public $resource = null; - - /** - * Resource Type - * - * @var string - */ - public $type = null; - - /** - * Resource Name - * - * @var string - */ - public $name = null; - - /** - * Unique Resource Name - * - * @var string - */ - public $unique_resource = null; - - /** - * Source Filepath - * - * @var string - */ - public $filepath = null; - - /** - * Source is bypassing compiler - * - * @var boolean - */ - public $uncompiled = null; - - /** - * Source must be recompiled on every occasion - * - * @var boolean - */ - public $recompiled = null; - - /** - * The Components an extended template is made of - * - * @var array - */ - public $components = null; - - /** - * Resource Handler - * - * @var Smarty_Resource - */ - public $handler = null; - - /** - * Smarty instance - * - * @var Smarty - */ - public $smarty = null; - - /** - * create Source Object container - * - * @param Smarty_Resource $handler Resource Handler this source object communicates with - * @param Smarty $smarty Smarty instance this source object belongs to - * @param string $resource full template_resource - * @param string $type type of resource - * @param string $name resource name - * @param string $unique_resource unique resource name - */ - public function __construct(Smarty_Resource $handler, Smarty $smarty, $resource, $type, $name, $unique_resource) - { - $this->handler = $handler; // Note: prone to circular references - - $this->compiler_class = $handler->compiler_class; - $this->template_lexer_class = $handler->template_lexer_class; - $this->template_parser_class = $handler->template_parser_class; - $this->uncompiled = $this->handler instanceof Smarty_Resource_Uncompiled; - $this->recompiled = $this->handler instanceof Smarty_Resource_Recompiled; - - $this->smarty = $smarty; - $this->resource = $resource; - $this->type = $type; - $this->name = $name; - $this->unique_resource = $unique_resource; - } - - /** - * get a Compiled Object of this source - * - * @param Smarty_Internal_Template|Smarty_Internal_Config $_template template object - * - * @return Smarty_Template_Compiled compiled object - */ - public function getCompiled($_template) - { - // check runtime cache - $_cache_key = $this->unique_resource . '#'; - if ($_template->caching) { - $_cache_key .= 'caching#'; - } - $_cache_key .= $_template->compile_id; - if (isset(Smarty_Resource::$compileds[$_cache_key])) { - return Smarty_Resource::$compileds[$_cache_key]; - } - - $compiled = new Smarty_Template_Compiled($this); - $this->handler->populateCompiledFilepath($compiled, $_template); - $compiled->timestamp = @filemtime($compiled->filepath); - $compiled->exists = !!$compiled->timestamp; - - // runtime cache - Smarty_Resource::$compileds[$_cache_key] = $compiled; - - return $compiled; - } - - /** - * render the uncompiled source - * - * @param Smarty_Internal_Template $_template template object - */ - public function renderUncompiled(Smarty_Internal_Template $_template) - { - return $this->handler->renderUncompiled($this, $_template); - } - - /** - * <> Generic Setter. - * - * @param string $property_name valid: timestamp, exists, content, template - * @param mixed $value new value (is not checked) - * - * @throws SmartyException if $property_name is not valid - */ - public function __set($property_name, $value) - { - switch ($property_name) { - // regular attributes - case 'timestamp': - case 'exists': - case 'content': - // required for extends: only - case 'template': - $this->$property_name = $value; - break; - - default: - throw new SmartyException("invalid source property '$property_name'."); - } - } - - /** - * <> Generic getter. - * - * @param string $property_name valid: timestamp, exists, content - * - * @return mixed - * @throws SmartyException if $property_name is not valid - */ - public function __get($property_name) - { - switch ($property_name) { - case 'timestamp': - case 'exists': - $this->handler->populateTimestamp($this); - - return $this->$property_name; - - case 'content': - return $this->content = $this->handler->getContent($this); - - default: - throw new SmartyException("source property '$property_name' does not exist."); - } - } -} - -/** - * Smarty Resource Data Object - * Meta Data Container for Template Files - * - * @package Smarty - * @subpackage TemplateResources - * @author Rodney Rehm - * @property string $content compiled content - */ -class Smarty_Template_Compiled -{ - /** - * Compiled Filepath - * - * @var string - */ - public $filepath = null; - - /** - * Compiled Timestamp - * - * @var integer - */ - public $timestamp = null; - - /** - * Compiled Existence - * - * @var boolean - */ - public $exists = false; - - /** - * Compiled Content Loaded - * - * @var boolean - */ - public $loaded = false; - - /** - * Template was compiled - * - * @var boolean - */ - public $isCompiled = false; - - /** - * Source Object - * - * @var Smarty_Template_Source - */ - public $source = null; - - /** - * Metadata properties - * populated by Smarty_Internal_Template::decodeProperties() - * - * @var array - */ - public $_properties = null; - - /** - * create Compiled Object container - * - * @param Smarty_Template_Source $source source object this compiled object belongs to - */ - public function __construct(Smarty_Template_Source $source) - { - $this->source = $source; - } -} diff --git a/libs/sysplugins/smarty_resource_custom.php b/libs/sysplugins/smarty_resource_custom.php index 7c11bd8c..619f2d6f 100644 --- a/libs/sysplugins/smarty_resource_custom.php +++ b/libs/sysplugins/smarty_resource_custom.php @@ -88,7 +88,7 @@ abstract class Smarty_Resource_Custom extends Smarty_Resource * * @return string resource's basename */ - protected function getBasename(Smarty_Template_Source $source) + public function getBasename(Smarty_Template_Source $source) { return basename($source->name); } diff --git a/libs/sysplugins/smarty_resource_recompiled.php b/libs/sysplugins/smarty_resource_recompiled.php index 66b36e17..c823387b 100644 --- a/libs/sysplugins/smarty_resource_recompiled.php +++ b/libs/sysplugins/smarty_resource_recompiled.php @@ -16,6 +16,13 @@ */ abstract class Smarty_Resource_Recompiled extends Smarty_Resource { + /** + * Flag that it's an recompiled resource + * + * @var bool + */ + public $recompiled = true; + /** * populate Compiled Object with compiled filepath * diff --git a/libs/sysplugins/smarty_resource_uncompiled.php b/libs/sysplugins/smarty_resource_uncompiled.php index 4ee5d790..26a9a57c 100644 --- a/libs/sysplugins/smarty_resource_uncompiled.php +++ b/libs/sysplugins/smarty_resource_uncompiled.php @@ -16,6 +16,13 @@ */ abstract class Smarty_Resource_Uncompiled extends Smarty_Resource { + /** + * Flag that it's an uncompiled resource + * + * @var bool + */ + public $uncompiled = true; + /** * Render and output the template (without using the compiler) * @@ -38,4 +45,25 @@ abstract class Smarty_Resource_Uncompiled extends Smarty_Resource $compiled->timestamp = false; $compiled->exists = false; } + + /** + * render compiled template code + * + * @param Smarty_Internal_Template $_template + * + * @return string + * @throws Exception + */ + public function render($_template) + { + try { + ob_start(); + $this->renderUncompiled($_template->source, $_template); + return ob_get_clean(); + } + catch (Exception $e) { + ob_get_clean(); + throw $e; + } + } } diff --git a/libs/sysplugins/smarty_template_source.php b/libs/sysplugins/smarty_template_source.php new file mode 100644 index 00000000..6802c0c5 --- /dev/null +++ b/libs/sysplugins/smarty_template_source.php @@ -0,0 +1,276 @@ +handler = $handler; // Note: prone to circular references + + $this->recompiled = $handler->recompiled; + $this->uncompiled = $handler->uncompiled; + $this->compiler_class = $handler->compiler_class; + $this->template_lexer_class = $handler->template_lexer_class; + $this->template_parser_class = $handler->template_parser_class; + + $this->smarty = $smarty; + $this->resource = $resource; + $this->type = $type; + $this->name = $name; + } + + /** + * initialize Source Object for given resource + * Either [$_template] or [$smarty, $template_resource] must be specified + * + * @param Smarty_Internal_Template $_template template object + * @param Smarty $smarty smarty object + * @param string $template_resource resource identifier + * + * @return Smarty_Template_Source Source Object + * @throws SmartyException + */ + public static function load(Smarty_Internal_Template $_template = null, Smarty $smarty = null, $template_resource = null) + { + if ($_template) { + $smarty = $_template->smarty; + $template_resource = $_template->template_resource; + } + if (empty($template_resource)) { + throw new SmartyException('Missing template name'); + } + // parse resource_name, load resource handler, identify unique resource name + Smarty_Resource::parseResourceName($template_resource, $smarty->default_resource_type, $name, $type); + $resource = Smarty_Resource::load($smarty, $type); + // if resource is not recompiling and resource name is not dotted we can check the source cache + if ($smarty->resource_caching && !$resource->recompiled && !(isset($name[1]) && $name[0] == '.' && ($name[1] == '.' || $name[1] == '/'))) { + $unique_resource = $resource->buildUniqueResourceName($smarty, $name); + if (isset($smarty->source_objects[$unique_resource])) { + return $smarty->source_objects[$unique_resource]; + } + } else { + $unique_resource = null; + } + // create new source object + $source = new Smarty_Template_Source($resource, $smarty, $template_resource, $type, $name); + $resource->populate($source, $_template); + if ((!isset($source->exists) || !$source->exists) && isset($_template->smarty->default_template_handler_func)) { + Smarty_Internal_Extension_DefaultTemplateHandler::_getDefault($_template, $source, $resObj); + } + // on recompiling resources we are done + if ($smarty->resource_caching && !$resource->recompiled) { + // may by we have already $unique_resource + $is_relative = false; + if (!isset($unique_resource)) { + $is_relative = isset($name[1]) && $name[0] == '.' && ($name[1] == '.' || $name[1] == '/') && + ($type == 'file' || (isset($_template->parent->source) && $_template->parent->source->type == 'extends')); + $unique_resource = $resource->buildUniqueResourceName($smarty, $is_relative ? $source->filepath . $name : $name); + } + $source->unique_resource = $unique_resource; + // save in runtime cache if not relative + if (!$is_relative) { + $smarty->source_objects[$unique_resource] = $source; + } + } + return $source; + } + + /** + * render the uncompiled source + * + * @param Smarty_Internal_Template $_template template object + */ + public function renderUncompiled(Smarty_Internal_Template $_template) + { + try { + ob_start(); + $this->renderUncompiled($_template->source, $_template); + return ob_get_clean(); + } + catch (Exception $e) { + ob_get_clean(); + throw $e; + } + } + + /** + * <> Generic Setter. + * + * @param string $property_name valid: timestamp, exists, content, template + * @param mixed $value new value (is not checked) + * + * @throws SmartyException if $property_name is not valid + */ + public function __set($property_name, $value) + { + switch ($property_name) { + // regular attributes + case 'timestamp': + case 'exists': + case 'content': + // required for extends: only + case 'template': + $this->$property_name = $value; + break; + + default: + throw new SmartyException("source property '$property_name' does not exist."); + } + } + + /** + * <> Generic getter. + * + * @param string $property_name valid: timestamp, exists, content + * + * @return mixed + * @throws SmartyException if $property_name is not valid + */ + public function __get($property_name) + { + switch ($property_name) { + case 'timestamp': + case 'exists': + $this->handler->populateTimestamp($this); + + return $this->$property_name; + + case 'content': + return $this->content = $this->handler->getContent($this); + + default: + throw new SmartyException("source property '$property_name' does not exist."); + } + } +}