diff --git a/change_log.txt b/change_log.txt index b29df9aa..bc666a12 100644 --- a/change_log.txt +++ b/change_log.txt @@ -2,6 +2,7 @@ 13.10.2011 - add caching for config files in Smarty_Resource - bugfix disable of caching after isCached() call did not work (Forum Topic 20131) +- add concept unique_resource to combat potentially ambiguous template_resource values when custom resource handlers are used (Forum Topic 20128) 11.10.2011 - add runtime checks for not matching {capture}/{/capture} calls (Forum Topic 20120) diff --git a/libs/Smarty.class.php b/libs/Smarty.class.php index ff5f155f..41071ab9 100644 --- a/libs/Smarty.class.php +++ b/libs/Smarty.class.php @@ -1161,7 +1161,8 @@ class Smarty extends Smarty_Internal_TemplateBase { $cache_id = $cache_id === null ? $this->cache_id : $cache_id; $compile_id = $compile_id === null ? $this->compile_id : $compile_id; // already in template cache? - $_templateId = sha1($this->smarty->joined_template_dir.$template . $cache_id . $compile_id); + $unique_template_name = Smarty_Resource::getUniqueTemplateName($this, $template); + $_templateId = sha1($unique_template_name . $cache_id . $compile_id); if ($do_clone) { if (isset($this->template_objects[$_templateId])) { // return cached template object diff --git a/libs/sysplugins/smarty_cacheresource_keyvaluestore.php b/libs/sysplugins/smarty_cacheresource_keyvaluestore.php index 7d9cfcc0..01071d5e 100644 --- a/libs/sysplugins/smarty_cacheresource_keyvaluestore.php +++ b/libs/sysplugins/smarty_cacheresource_keyvaluestore.php @@ -175,9 +175,14 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource { $tpl = new $smarty->template_class($resource_name, $smarty); if ($tpl->source->exists) { $uid = $tpl->source->uid; - } + } + + // TODO: uwe.tews check if $tpl->compile_id vs. $compile_id is correct + // from what I can see $tpl->compile_id would always be null?! + // remove from template cache - unset($smarty->template_objects[sha1($smarty->joined_template_dir . $tpl->template_resource . $tpl->cache_id . $tpl->compile_id)]); + $_templateId = sha1($tpl->source->unique_resource . $tpl->cache_id . $tpl->compile_id); + unset($smarty->template_objects[$_templateId]); } return $uid; } diff --git a/libs/sysplugins/smarty_config_source.php b/libs/sysplugins/smarty_config_source.php index fb6d9817..043ff13e 100644 --- a/libs/sysplugins/smarty_config_source.php +++ b/libs/sysplugins/smarty_config_source.php @@ -24,13 +24,14 @@ class Smarty_Config_Source extends Smarty_Template_Source { /** * create Config 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 config_resource - * @param string $type type of resource - * @param string $name resource name + * @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 config_resource + * @param string $type type of resource + * @param string $name resource name + * @param string $unique_resource unqiue resource name */ - public function __construct(Smarty_Resource $handler, Smarty $smarty, $resource, $type, $name) + public function __construct(Smarty_Resource $handler, Smarty $smarty, $resource, $type, $name, $unique_resource) { $this->handler = $handler; // Note: prone to circular references @@ -43,22 +44,9 @@ class Smarty_Config_Source extends Smarty_Template_Source { $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 $_template template objet - * @return Smarty_Template_Compiled compiled object - * - public function getCompiled(Smarty_Internal_Template $_template) - { - $compiled = new Smarty_Template_Compiled($this); - $this->handler->populateCompiledFilepath($compiled, $_template); - return $compiled; - } - */ - /** * <> Generic setter. * diff --git a/libs/sysplugins/smarty_internal_cacheresource_file.php b/libs/sysplugins/smarty_internal_cacheresource_file.php index 9c4d0326..aaeb4dd9 100644 --- a/libs/sysplugins/smarty_internal_cacheresource_file.php +++ b/libs/sysplugins/smarty_internal_cacheresource_file.php @@ -151,13 +151,18 @@ class Smarty_Internal_CacheResource_File extends Smarty_CacheResource { $smarty->caching = true; $tpl = new $smarty->template_class($resource_name, $smarty); $smarty->caching = $_save_stat; + + // TODO: uwe.tews check if $tpl->compile_id vs. $compile_id is correct + // from what I can see $tpl->compile_id would always be null?! + + // remove from template cache + $tpl->source; // have the template registered before unset() + $_templateId = sha1($tpl->source->unique_resource . $tpl->cache_id . $tpl->compile_id); + unset($smarty->template_objects[$_templateId]); + if ($tpl->source->exists) { $_resourcename_parts = basename(str_replace('^', '/', $tpl->cached->filepath)); - // remove from template cache - unset($smarty->template_objects[sha1($smarty->joined_template_dir.$tpl->template_resource . $tpl->cache_id . $tpl->compile_id)]); } else { - // remove from template cache - unset($smarty->template_objects[sha1($smarty->joined_template_dir.$tpl->template_resource . $tpl->cache_id . $tpl->compile_id)]); return 0; } } diff --git a/libs/sysplugins/smarty_internal_resource_eval.php b/libs/sysplugins/smarty_internal_resource_eval.php index 8b30f03d..cf2ec3e1 100644 --- a/libs/sysplugins/smarty_internal_resource_eval.php +++ b/libs/sysplugins/smarty_internal_resource_eval.php @@ -37,23 +37,45 @@ class Smarty_Internal_Resource_Eval extends Smarty_Resource_Recompiled { /** * Load template's source from $resource_name into current template object * - * {@internal if source begins with "base64:" or "urlencode:", the source is decoded accordingly}} - * + * @uses decode() to decode base64 and urlencoded template_resources * @param Smarty_Template_Source $source source object * @return string template source */ public function getContent(Smarty_Template_Source $source) + { + return $this->decode($source->name); + } + + /** + * decode base64 and urlencode + * + * @param string $string template_resource to decode + * @return string decoded template_resource + */ + protected function decode($string) { // decode if specified - if (($pos = strpos($source->name, ':')) !== false) { - if (!strncmp($source->name, 'base64', 6)) { - return base64_decode(substr($source->name, 7)); - } elseif (!strncmp($source->name, 'urlencode', 9)) { - return urldecode(substr($source->name, 10)); + if (($pos = strpos($string, ':')) !== false) { + if (!strncmp($string, 'base64', 6)) { + return base64_decode(substr($string, 7)); + } elseif (!strncmp($string, 'urlencode', 9)) { + return urldecode(substr($string, 10)); } } - - return $source->name; + + return $string; + } + + /** + * modify resource_name according to resource handlers specifications + * + * @param Smarty $smarty Smarty instance + * @param string $resource_name resource_name to make unique + * @return string unique resource name + */ + protected function buildUniqueResourceName(Smarty $smarty, $resource_name) + { + return get_class($this) . '#' .$this->decode($resource_name); } /** diff --git a/libs/sysplugins/smarty_internal_resource_stream.php b/libs/sysplugins/smarty_internal_resource_stream.php index cba8adb8..85698c23 100644 --- a/libs/sysplugins/smarty_internal_resource_stream.php +++ b/libs/sysplugins/smarty_internal_resource_stream.php @@ -59,7 +59,18 @@ class Smarty_Internal_Resource_Stream extends Smarty_Resource_Recompiled { return false; } } - + + /** + * modify resource_name according to resource handlers specifications + * + * @param Smarty $smarty Smarty instance + * @param string $resource_name resource_name to make unique + * @return string unique resource name + */ + protected function buildUniqueResourceName(Smarty $smarty, $resource_name) + { + return get_class($this) . '#' . $resource_name; + } } ?> \ No newline at end of file diff --git a/libs/sysplugins/smarty_internal_resource_string.php b/libs/sysplugins/smarty_internal_resource_string.php index 45b720d6..9571337b 100644 --- a/libs/sysplugins/smarty_internal_resource_string.php +++ b/libs/sysplugins/smarty_internal_resource_string.php @@ -37,24 +37,45 @@ class Smarty_Internal_Resource_String extends Smarty_Resource { /** * Load template's source from $resource_name into current template object * - * {@internal if source begins with "base64:" or "urlencode:", the source is decoded accordingly}} - * + * @uses decode() to decode base64 and urlencoded template_resources * @param Smarty_Template_Source $source source object * @return string template source - * @throws SmartyException if source cannot be loaded */ public function getContent(Smarty_Template_Source $source) + { + return $this->decode($source->name); + } + + /** + * decode base64 and urlencode + * + * @param string $string template_resource to decode + * @return string decoded template_resource + */ + protected function decode($string) { // decode if specified - if (($pos = strpos($source->name, ':')) !== false) { - if (!strncmp($source->name, 'base64', 6)) { - return base64_decode(substr($source->name, 7)); - } elseif (!strncmp($source->name, 'urlencode', 9)) { - return urldecode(substr($source->name, 10)); + if (($pos = strpos($string, ':')) !== false) { + if (!strncmp($string, 'base64', 6)) { + return base64_decode(substr($string, 7)); + } elseif (!strncmp($string, 'urlencode', 9)) { + return urldecode(substr($string, 10)); } } - - return $source->name; + + return $string; + } + + /** + * modify resource_name according to resource handlers specifications + * + * @param Smarty $smarty Smarty instance + * @param string $resource_name resource_name to make unique + * @return string unique resource name + */ + protected function buildUniqueResourceName(Smarty $smarty, $resource_name) + { + return get_class($this) . '#' .$this->decode($resource_name); } /** diff --git a/libs/sysplugins/smarty_internal_template.php b/libs/sysplugins/smarty_internal_template.php index 8bb4c4b0..4246bcd8 100644 --- a/libs/sysplugins/smarty_internal_template.php +++ b/libs/sysplugins/smarty_internal_template.php @@ -245,7 +245,8 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { public function getSubTemplate($template, $cache_id, $compile_id, $caching, $cache_lifetime, $data, $parent_scope) { // already in template cache? - $_templateId = sha1($this->smarty->joined_template_dir.$template . $cache_id . $compile_id); + $unique_template_name = Smarty_Resource::getUniqueTemplateName($this->smarty, $template); + $_templateId = sha1($unique_template_name . $cache_id . $compile_id); if (isset($this->smarty->template_objects[$_templateId])) { // clone cached template object because of possible recursive call $tpl = clone $this->smarty->template_objects[$_templateId]; @@ -606,7 +607,8 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { // cache template object under a unique ID // do not cache eval resources if ($this->source->type != 'eval') { - $this->smarty->template_objects[sha1($this->smarty->joined_template_dir.$this->template_resource . $this->cache_id . $this->compile_id)] = $this; + $_templateId = sha1($this->source->unique_resource . $this->cache_id . $this->compile_id); + $this->smarty->template_objects[$_templateId] = $this; } return $this->source; diff --git a/libs/sysplugins/smarty_internal_utility.php b/libs/sysplugins/smarty_internal_utility.php index 03b4851d..bc666003 100644 --- a/libs/sysplugins/smarty_internal_utility.php +++ b/libs/sysplugins/smarty_internal_utility.php @@ -189,16 +189,22 @@ class Smarty_Internal_Utility { $smarty->caching = false; $tpl = new $smarty->template_class($resource_name, $smarty); $smarty->caching = $_save_stat; + + // TODO: uwe.tews check if $tpl->compile_id vs. $compile_id is correct + // from what I can see $tpl->compile_id would always be null?! + + // remove from template cache + $tpl->source; // have the template registered before unset() + $_templateId = sha1($tpl->source->unique_resource . $tpl->cache_id . $tpl->compile_id); + unset($smarty->template_objects[$_templateId]); + if ($tpl->source->exists) { $_resource_part_1 = basename(str_replace('^', '/', $tpl->compiled->filepath)); $_resource_part_1_length = strlen($_resource_part_1); - // remove from template cache - unset($smarty->template_objects[sha1($smarty->joined_template_dir.$tpl->template_resource . $tpl->cache_id . $tpl->compile_id)]); } else { - // remove from template cache - unset($smarty->template_objects[sha1($smarty->joined_template_dir.$tpl->template_resource . $tpl->cache_id . $tpl->compile_id)]); return 0; } + $_resource_part_2 = str_replace('.php','.cache.php',$_resource_part_1); $_resource_part_2_length = strlen($_resource_part_2); } else { diff --git a/libs/sysplugins/smarty_resource.php b/libs/sysplugins/smarty_resource.php index 047076b3..eae3463f 100644 --- a/libs/sysplugins/smarty_resource.php +++ b/libs/sysplugins/smarty_resource.php @@ -90,7 +90,20 @@ abstract class Smarty_Resource { { // intentionally left blank } - + + + /** + * modify resource_name according to resource handlers specifications + * + * @param Smarty $smarty Smarty instance + * @param string $resource_name resource_name to make unique + * @return string unique resource name + */ + protected function buildUniqueResourceName(Smarty $smarty, $resource_name) + { + return get_class($this) . '#' . $smarty->joined_template_dir . '#' . $resource_name; + } + /** * populate Compiled Object with compiled filepath * @@ -380,7 +393,55 @@ abstract class Smarty_Resource { // give up throw new SmartyException('Unkown resource type \'' . $resource_type . '\''); } - + + /** + * extract resource_type and resource_name from template_resource and config_resource + * + * @note "C:/foo.tpl" was forced to file resource up till Smarty 3.1.3 (including). + * @param string $resource_name template_resource or config_resource to parse + * @param string $default_resource the default resource_type defined in $smarty + * @param string &$name the parsed resource name + * @param string &$type the parsed resource type + * @return void + */ + protected static function parseResourceName($resource_name, $default_resource, &$name, &$type) + { + $parts = explode(':', $resource_name, 2); + if (!isset($parts[1]) || !isset($parts[0][1])) { + // no resource given, use default + // or single character before the colon is not a resource type, but part of the filepath + $type = $default_resource; + $name = $resource_name; + } else { + $type = $parts[0]; + $name = $parts[1]; + } + } + + + /** + * modify resource_name according to resource handlers specifications + * + * @param Smarty $smarty Smarty instance + * @param string $resource_name resource_name to make unique + * @return string unique resource name + */ + + /** + * modify template_resource according to resource handlers specifications + * + * @param string $smarty Smarty instance + * @param string $template_resource template_resource to extracate resource handler and name of + * @return string unique resource name + */ + public static function getUniqueTemplateName($smarty, $template_resource) + { + self::parseResourceName($template_resource, $smarty->default_resource_type, $name, $type); + // TODO: optimize for Smarty's internal resource types + $resource = Smarty_Resource::load($smarty, $type); + return $resource->buildUniqueResourceName($smarty, $name); + } + /** * initialize Source Object for given resource * @@ -397,42 +458,24 @@ abstract class Smarty_Resource { $smarty = $_template->smarty; $template_resource = $_template->template_resource; } - - if (($pos = strpos($template_resource, ':')) === false) { - // no resource given, use default - $resource_type = $smarty->default_resource_type; - $resource_name = $template_resource; - } else { - // get type and name from path - $resource_type = substr($template_resource, 0, $pos); - $resource_name = substr($template_resource, $pos +1); - if (strlen($resource_type) == 1) { - // 1 char is not resource type, but part of filepath - $resource_type = 'file'; - $resource_name = $template_resource; - } - } - - $resource = Smarty_Resource::load($smarty, $resource_type); - - // TODO: modify $template_resource here + // 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); + $unique_resource_name = $resource->buildUniqueResourceName($smarty, $name); + // check runtime cache - $_cache_key_dir = $smarty->joined_template_dir; - $_cache_key = 'template|' . $template_resource; - if (!isset(self::$sources[$_cache_key_dir])) { - self::$sources[$_cache_key_dir] = array(); - } - if (isset(self::$sources[$_cache_key_dir][$_cache_key])) { - return self::$sources[$_cache_key_dir][$_cache_key]; + $_cache_key = 'template|' . $unique_resource_name; + if (isset(self::$sources[$_cache_key])) { + return self::$sources[$_cache_key]; } // create source - $source = new Smarty_Template_Source($resource, $smarty, $template_resource, $resource_type, $resource_name); + $source = new Smarty_Template_Source($resource, $smarty, $template_resource, $type, $name, $unique_resource_name); $resource->populate($source, $_template); // runtime cache - self::$sources[$_cache_key_dir][$_cache_key] = $source; + self::$sources[$_cache_key] = $source; return $source; } @@ -447,46 +490,31 @@ abstract class Smarty_Resource { static $_incompatible_resources = array('eval' => true, 'string' => true, 'extends' => true, 'php' => true); $config_resource = $_config->config_resource; $smarty = $_config->smarty; - - if (($pos = strpos($config_resource, ':')) === false) { - // no resource given, use default - $resource_type = $smarty->default_config_type; - $resource_name = $config_resource; - } else { - // get type and name from path - $resource_type = substr($config_resource, 0, $pos); - $resource_name = substr($config_resource, $pos +1); - if (strlen($resource_type) == 1) { - // 1 char is not resource type, but part of filepath - $resource_type = 'file'; - $resource_name = $config_resource; - } - } - if (isset($_incompatible_resources[$resource_type])) { - throw new SmartyException ("Unable to use resource '{$resource_type}' for config"); + // 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"); } - $resource = Smarty_Resource::load($smarty, $resource_type); - - // TODO: modify $config_resource here + // load resource handler, identify unique resource name + $resource = Smarty_Resource::load($smarty, $type); + $unique_resource_name = $resource->buildUniqueResourceName($smarty, $name); // check runtime cache - $_cache_key_dir = $smarty->joined_config_dir; - $_cache_key = 'config|' . $config_resource; - if (!isset(self::$sources[$_cache_key_dir])) { - self::$sources[$_cache_key_dir] = array(); - } - if (isset(self::$sources[$_cache_key_dir][$_cache_key])) { - return self::$sources[$_cache_key_dir][$_cache_key]; + $_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, $resource_type, $resource_name); + $source = new Smarty_Config_Source($resource, $smarty, $config_resource, $type, $name, $unique_resource_name); $resource->populate($source, null); // runtime cache - self::$sources[$_cache_key_dir][$_cache_key] = $source; + self::$sources[$_cache_key] = $source; return $source; } @@ -549,6 +577,12 @@ class Smarty_Template_Source { * @var string */ public $name = null; + + /** + * Unique Resource Name + * @var string + */ + public $unique_resource = null; /** * Source Filepath @@ -589,13 +623,14 @@ class Smarty_Template_Source { /** * 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 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 unqiue resource name */ - public function __construct(Smarty_Resource $handler, Smarty $smarty, $resource, $type, $name) + public function __construct(Smarty_Resource $handler, Smarty $smarty, $resource, $type, $name, $unique_resource) { $this->handler = $handler; // Note: prone to circular references @@ -609,6 +644,7 @@ class Smarty_Template_Source { $this->resource = $resource; $this->type = $type; $this->name = $name; + $this->unique_resource = $unique_resource; } /**