Created
November 5, 2016 21:23
-
-
Save firstred/23e05023d67630c90d294761efcaee44 to your computer and use it in GitHub Desktop.
PrestaShop Custom Smarty file with microsecond delay after possible temp file name collision
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* | |
| * 2007-2016 PrestaShop | |
| * | |
| * NOTICE OF LICENSE | |
| * | |
| * This source file is subject to the Open Software License (OSL 3.0) | |
| * that is bundled with this package in the file LICENSE.txt. | |
| * It is also available through the world-wide-web at this URL: | |
| * http://opensource.org/licenses/osl-3.0.php | |
| * If you did not receive a copy of the license and are unable to | |
| * obtain it through the world-wide-web, please send an email | |
| * to [email protected] so we can send you a copy immediately. | |
| * | |
| * DISCLAIMER | |
| * | |
| * Do not edit or add to this file if you wish to upgrade PrestaShop to newer | |
| * versions in the future. If you wish to customize PrestaShop for your | |
| * needs please refer to http://www.prestashop.com for more information. | |
| * | |
| * @author PrestaShop SA <[email protected]> | |
| * @copyright 2007-2016 PrestaShop SA | |
| * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) | |
| * International Registered Trademark & Property of PrestaShop SA | |
| */ | |
| class SmartyCustomCore extends Smarty | |
| { | |
| public function __construct() | |
| { | |
| parent::__construct(); | |
| $this->template_class = 'Smarty_Custom_Template'; | |
| } | |
| /** | |
| * Delete compiled template file (lazy delete if resource_name is not specified) | |
| * | |
| * @param string $resource_name template name | |
| * @param string $compile_id compile id | |
| * @param int $exp_time expiration time | |
| * | |
| * @return int number of template files deleted | |
| */ | |
| public function clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) | |
| { | |
| if ($resource_name == null) { | |
| Db::getInstance()->execute('REPLACE INTO `'._DB_PREFIX_.'smarty_last_flush` (`type`, `last_flush`) VALUES (\'compile\', FROM_UNIXTIME('.time().'))'); | |
| return 0; | |
| } else { | |
| return parent::clearCompiledTemplate($resource_name, $compile_id, $exp_time); | |
| } | |
| } | |
| /** | |
| * Mark all template files to be regenerated | |
| * | |
| * @param int $exp_time expiration time | |
| * @param string $type resource type | |
| * | |
| * @return int number of cache files which needs to be updated | |
| */ | |
| public function clearAllCache($exp_time = null, $type = null) | |
| { | |
| Db::getInstance()->execute('REPLACE INTO `'._DB_PREFIX_.'smarty_last_flush` (`type`, `last_flush`) VALUES (\'template\', FROM_UNIXTIME('.time().'))'); | |
| return $this->delete_from_lazy_cache(null, null, null); | |
| } | |
| /** | |
| * Mark file to be regenerated for a specific template | |
| * | |
| * @param string $template_name template name | |
| * @param string $cache_id cache id | |
| * @param string $compile_id compile id | |
| * @param int $exp_time expiration time | |
| * @param string $type resource type | |
| * | |
| * @return int number of cache files which needs to be updated | |
| */ | |
| public function clearCache($template_name, $cache_id = null, $compile_id = null, $exp_time = null, $type = null) | |
| { | |
| return $this->delete_from_lazy_cache($template_name, $cache_id, $compile_id); | |
| } | |
| /** | |
| * Check the compile cache needs to be invalidated (multi front + local cache compatible) | |
| */ | |
| public function check_compile_cache_invalidation() | |
| { | |
| static $last_flush = null; | |
| if (!file_exists($this->getCompileDir().'last_flush')) { | |
| @touch($this->getCompileDir().'last_flush', time()); | |
| } elseif (defined('_DB_PREFIX_')) { | |
| if ($last_flush === null) { | |
| $sql = 'SELECT UNIX_TIMESTAMP(last_flush) as last_flush FROM `'._DB_PREFIX_.'smarty_last_flush` WHERE type=\'compile\''; | |
| $last_flush = Db::getInstance()->getValue($sql, false); | |
| } | |
| if ((int)$last_flush && @filemtime($this->getCompileDir().'last_flush') < $last_flush) { | |
| @touch($this->getCompileDir().'last_flush', time()); | |
| parent::clearCompiledTemplate(); | |
| } | |
| } | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| public function fetch($template = null, $cache_id = null, $compile_id = null, $parent = null, $display = false, $merge_tpl_vars = true, $no_output_filter = false) | |
| { | |
| $this->check_compile_cache_invalidation(); | |
| $count = 0; | |
| $maxTries = 3; | |
| while (true) { | |
| try { | |
| $tpl = parent::fetch($template, $cache_id, $compile_id, $parent, $display, $merge_tpl_vars, $no_output_filter); | |
| return $tpl; | |
| } catch (SmartyException $e) { | |
| // handle exception | |
| if (++$count === $maxTries) { | |
| throw $e; | |
| } | |
| usleep(1); | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true) | |
| { | |
| $this->check_compile_cache_invalidation(); | |
| if ($this->caching) { | |
| $this->check_template_invalidation($template, $cache_id, $compile_id); | |
| return parent::createTemplate($template, $cache_id, $compile_id, $parent, $do_clone); | |
| } else { | |
| return parent::createTemplate($template, $cache_id, $compile_id, $parent, $do_clone); | |
| } | |
| } | |
| /** | |
| * Handle the lazy template cache invalidation | |
| * | |
| * @param string $template template name | |
| * @param string $cache_id cache id | |
| * @param string $compile_id compile id | |
| */ | |
| public function check_template_invalidation($template, $cache_id, $compile_id) | |
| { | |
| static $last_flush = null; | |
| if (!file_exists($this->getCacheDir().'last_template_flush')) { | |
| @touch($this->getCacheDir().'last_template_flush', time()); | |
| } elseif (defined('_DB_PREFIX_')) { | |
| if ($last_flush === null) { | |
| $sql = 'SELECT UNIX_TIMESTAMP(last_flush) as last_flush FROM `'._DB_PREFIX_.'smarty_last_flush` WHERE type=\'template\''; | |
| $last_flush = Db::getInstance()->getValue($sql, false); | |
| } | |
| if ((int)$last_flush && @filemtime($this->getCacheDir().'last_template_flush') < $last_flush) { | |
| @touch($this->getCacheDir().'last_template_flush', time()); | |
| parent::clearAllCache(); | |
| } else { | |
| if ($cache_id !== null && (is_object($cache_id) || is_array($cache_id))) { | |
| $cache_id = null; | |
| } | |
| if ($this->is_in_lazy_cache($template, $cache_id, $compile_id) === false) { | |
| // insert in cache before the effective cache creation to avoid nasty race condition | |
| $this->insert_in_lazy_cache($template, $cache_id, $compile_id); | |
| parent::clearCache($template, $cache_id, $compile_id); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Store the cache file path | |
| * | |
| * @param string $filepath cache file path | |
| * @param string $template template name | |
| * @param string $cache_id cache id | |
| * @param string $compile_id compile id | |
| */ | |
| public function update_filepath($filepath, $template, $cache_id, $compile_id) | |
| { | |
| $template_md5 = md5($template); | |
| $sql = 'UPDATE `'._DB_PREFIX_.'smarty_lazy_cache` | |
| SET filepath=\''.pSQL($filepath).'\' | |
| WHERE `template_hash`=\''.pSQL($template_md5).'\''; | |
| $sql .= ' AND cache_id="'.pSQL((string)$cache_id).'"'; | |
| if (strlen($compile_id) > 32) { | |
| $compile_id = md5($compile_id); | |
| } | |
| $sql .= ' AND compile_id="'.pSQL((string)$compile_id).'"'; | |
| Db::getInstance()->execute($sql, false); | |
| } | |
| /** | |
| * Check if the current template is stored in the lazy cache | |
| * Entry in the lazy cache = no need to regenerate the template | |
| * | |
| * @param string $template template name | |
| * @param string $cache_id cache id | |
| * @param string $compile_id compile id | |
| * | |
| * @return bool | |
| */ | |
| public function is_in_lazy_cache($template, $cache_id, $compile_id) | |
| { | |
| static $is_in_lazy_cache = array(); | |
| $template_md5 = md5($template); | |
| if (strlen($compile_id) > 32) { | |
| $compile_id = md5($compile_id); | |
| } | |
| $key = md5($template_md5.'-'.$cache_id.'-'.$compile_id); | |
| if (isset($is_in_lazy_cache[$key])) { | |
| return $is_in_lazy_cache[$key]; | |
| } else { | |
| $sql = 'SELECT UNIX_TIMESTAMP(last_update) as last_update, filepath FROM `'._DB_PREFIX_.'smarty_lazy_cache` | |
| WHERE `template_hash`=\''.pSQL($template_md5).'\''; | |
| $sql .= ' AND cache_id="'.pSQL((string)$cache_id).'"'; | |
| $sql .= ' AND compile_id="'.pSQL((string)$compile_id).'"'; | |
| $result = Db::getInstance()->getRow($sql, false); | |
| // If the filepath is not yet set, it means the cache update is in progress in another process. | |
| // In this case do not try to clear the cache again and tell to use the existing cache, if any | |
| if ($result !== false && $result['filepath'] == '') { | |
| // If the cache update is stalled for more than 1min, something should be wrong, | |
| // remove the entry from the lazy cache | |
| if ($result['last_update'] < time() - 60) { | |
| $this->delete_from_lazy_cache($template, $cache_id, $compile_id); | |
| } | |
| $return = true; | |
| } else { | |
| if ($result === false | |
| || @filemtime($this->getCacheDir().$result['filepath']) < $result['last_update']) { | |
| $return = false; | |
| } else { | |
| $return = $result['filepath']; | |
| } | |
| } | |
| $is_in_lazy_cache[$key] = $return; | |
| } | |
| return $return; | |
| } | |
| /** | |
| * Insert the current template in the lazy cache | |
| * | |
| * @param string $template template name | |
| * @param string $cache_id cache id | |
| * @param string $compile_id compile id | |
| * | |
| * @return bool | |
| */ | |
| public function insert_in_lazy_cache($template, $cache_id, $compile_id) | |
| { | |
| $template_md5 = md5($template); | |
| $sql = 'INSERT IGNORE INTO `'._DB_PREFIX_.'smarty_lazy_cache` | |
| (`template_hash`, `cache_id`, `compile_id`, `last_update`) | |
| VALUES (\''.pSQL($template_md5).'\''; | |
| $sql .= ',"'.pSQL((string)$cache_id).'"'; | |
| if (strlen($compile_id) > 32) { | |
| $compile_id = md5($compile_id); | |
| } | |
| $sql .= ',"'.pSQL((string)$compile_id).'"'; | |
| $sql .= ', FROM_UNIXTIME('.time().'))'; | |
| return Db::getInstance()->execute($sql, false); | |
| } | |
| /** | |
| * Delete the current template from the lazy cache or the whole cache if no template name is given | |
| * | |
| * @param string $template template name | |
| * @param string $cache_id cache id | |
| * @param string $compile_id compile id | |
| * | |
| * @return bool | |
| */ | |
| public function delete_from_lazy_cache($template, $cache_id, $compile_id) | |
| { | |
| if (!$template) { | |
| return Db::getInstance()->execute('TRUNCATE TABLE `'._DB_PREFIX_.'smarty_lazy_cache`', false); | |
| } | |
| $template_md5 = md5($template); | |
| $sql = 'DELETE FROM `'._DB_PREFIX_.'smarty_lazy_cache` | |
| WHERE template_hash=\''.pSQL($template_md5).'\''; | |
| if ($cache_id != null) { | |
| $sql .= ' AND cache_id LIKE "'.pSQL((string)$cache_id).'%"'; | |
| } | |
| if ($compile_id != null) { | |
| if (strlen($compile_id) > 32) { | |
| $compile_id = md5($compile_id); | |
| } | |
| $sql .= ' AND compile_id="'.pSQL((string)$compile_id).'"'; | |
| } | |
| Db::getInstance()->execute($sql, false); | |
| return Db::getInstance()->Affected_Rows(); | |
| } | |
| } | |
| class Smarty_Custom_Template extends Smarty_Internal_Template | |
| { | |
| /** @var SmartyCustom|null */ | |
| public $smarty = null; | |
| public function fetch($template = null, $cache_id = null, $compile_id = null, $parent = null, $display = false, $merge_tpl_vars = true, $no_output_filter = false) | |
| { | |
| if ($this->smarty->caching) { | |
| $count = 0; | |
| $maxTries = 3; | |
| while(true) { | |
| try { | |
| $tpl = parent::fetch($template, $cache_id, $compile_id, $parent, $display, $merge_tpl_vars, $no_output_filter); | |
| break; | |
| } catch (SmartyException $e) { | |
| // handle exception | |
| if (++$count === $maxTries) { | |
| throw $e; | |
| } | |
| usleep(1); | |
| } | |
| } | |
| if (property_exists($this, 'cached')) { | |
| $filepath = str_replace($this->smarty->getCacheDir(), '', $this->cached->filepath); | |
| if ($this->smarty->is_in_lazy_cache($this->template_resource, $this->cache_id, $this->compile_id) != $filepath) { | |
| $this->smarty->update_filepath($filepath, $this->template_resource, $this->cache_id, $this->compile_id); | |
| } | |
| } | |
| return $tpl; | |
| } else { | |
| $count = 0; | |
| $maxTries = 3; | |
| while(true) { | |
| try { | |
| $tpl = parent::fetch($template, $cache_id, $compile_id, $parent, $display, $merge_tpl_vars, $no_output_filter); | |
| break; | |
| } catch (SmartyException $e) { | |
| // handle exception | |
| if (++$count === $maxTries) { | |
| throw $e; | |
| } | |
| usleep(1); | |
| } | |
| } | |
| return $tpl; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment