summaryrefslogtreecommitdiff
path: root/tests/UnitTests/__shared
diff options
context:
space:
mode:
Diffstat (limited to 'tests/UnitTests/__shared')
-rw-r--r--tests/UnitTests/__shared/PHPunitplugins/block.dummyblock.php4
-rw-r--r--tests/UnitTests/__shared/PHPunitplugins/compiler.getparamsshort.php16
-rw-r--r--tests/UnitTests/__shared/PHPunitplugins/function.checkconfigvar.php26
-rw-r--r--tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php37
-rw-r--r--tests/UnitTests/__shared/PHPunitplugins/function.getparams.php8
-rw-r--r--tests/UnitTests/__shared/PHPunitplugins/function.getvar.php8
-rw-r--r--tests/UnitTests/__shared/PHPunitplugins/function.pluginassign.php4
-rw-r--r--tests/UnitTests/__shared/cacheresources/cacheresource.apc.php73
-rw-r--r--tests/UnitTests/__shared/cacheresources/cacheresource.memcache.php101
-rw-r--r--tests/UnitTests/__shared/cacheresources/cacheresource.mysql.php190
-rw-r--r--tests/UnitTests/__shared/cacheresources/cacheresource.pdo.php347
-rw-r--r--tests/UnitTests/__shared/cacheresources/cacheresource.pdo_gzip.php42
-rw-r--r--tests/UnitTests/__shared/resources/resource.extendsall.php66
13 files changed, 873 insertions, 49 deletions
diff --git a/tests/UnitTests/__shared/PHPunitplugins/block.dummyblock.php b/tests/UnitTests/__shared/PHPunitplugins/block.dummyblock.php
index 7de4fa09..b20ca8ec 100644
--- a/tests/UnitTests/__shared/PHPunitplugins/block.dummyblock.php
+++ b/tests/UnitTests/__shared/PHPunitplugins/block.dummyblock.php
@@ -2,8 +2,8 @@
/**
* Smarty block plugin dummy for testing plugin functionallity
*
- * @package Smarty
- * @subpackage PHPunitPlugin
+
+
*/
/**
* Smarty {dummyblock} {/dummyblock}
diff --git a/tests/UnitTests/__shared/PHPunitplugins/compiler.getparamsshort.php b/tests/UnitTests/__shared/PHPunitplugins/compiler.getparamsshort.php
index e5907eb1..368b4a91 100644
--- a/tests/UnitTests/__shared/PHPunitplugins/compiler.getparamsshort.php
+++ b/tests/UnitTests/__shared/PHPunitplugins/compiler.getparamsshort.php
@@ -2,10 +2,12 @@
/**
* Smarty plugin params
*
- * @package Smarty
- * @subpackage PHPunitPlugin
+
+
*/
+use Smarty\Compile\Base;
+
/**
* Smarty {getparams}
*
@@ -14,13 +16,13 @@
*
* @return string
*/
-class smarty_compiler_getparamsshort extends Smarty_Internal_CompileBase
+class smarty_compiler_getparamsshort extends Base
{
/**
* Attribute definition: Overwrites base class.
*
* @var array
- * @see Smarty_Internal_CompileBase
+ * @see Base
*/
public $required_attributes = array();
@@ -28,18 +30,18 @@ class smarty_compiler_getparamsshort extends Smarty_Internal_CompileBase
* Attribute definition: Overwrites base class.
*
* @var array
- * @see Smarty_Internal_CompileBase
+ * @see Base
*/
public $optional_attributes = array('_any');
/**
* Attribute definition: Overwrites base class.
*
* @var array
- * @see Smarty_Internal_CompileBase
+ * @see Base
*/
public $shorttag_order = array('s1', 's2', 's3');
- public function compile($args, $compiler)
+ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null)
{
$_attr = $this->getAttributes($compiler, $args);
$output = '<?php echo "array(';
diff --git a/tests/UnitTests/__shared/PHPunitplugins/function.checkconfigvar.php b/tests/UnitTests/__shared/PHPunitplugins/function.checkconfigvar.php
index c026793b..94fcc2c9 100644
--- a/tests/UnitTests/__shared/PHPunitplugins/function.checkconfigvar.php
+++ b/tests/UnitTests/__shared/PHPunitplugins/function.checkconfigvar.php
@@ -2,10 +2,12 @@
/**
* Smarty plugin for testing scopes in config vars
*
- * @package Smarty
- * @subpackage PHPunitPlugin
+
+
*/
+use Smarty\Template;
+
/**
* Smarty {checkconfigvar}
*
@@ -17,29 +19,29 @@
function smarty_function_checkconfigvar($params, $template)
{
$output = '';
- $types = array('template', 'data', 'smarty');
+ $types = array('template', 'data', 'global');
if (isset($params['types'])) {
$types = (array)$params['types'];
}
$var = $params['var'];
$ptr = $template;
while ($ptr) {
- if (in_array('template', $types) && $ptr instanceof Smarty_Internal_Template) {
- $output .= "#{$ptr->source->name}:\${$var} =";
- $output .= isset($ptr->config_vars[$var]) ? preg_replace('/\s/', '', var_export($ptr->config_vars[$var], true)) : 'null';
+ if (in_array('template', $types) && $ptr instanceof Template) {
+ $output .= "#{$ptr->getSource()->name}:\${$var} =";
+ $output .= $ptr->hasConfigVariable($var) ? preg_replace('/\s/', '', var_export($ptr->getConfigVariable($var), true)) : 'null';
$ptr = $ptr->parent;
- } elseif (in_array('data', $types) && $ptr instanceof Smarty_Data) {
+ } elseif (in_array('data', $types) && !($ptr instanceof Template || $ptr instanceof \Smarty\Smarty)) {
$output .= "#data:\${$var} =";
- $output .= isset($ptr->config_vars[$var]) ? preg_replace('/\s/', '', var_export($ptr->config_vars[$var], true)) : 'null';
+ $output .= $ptr->hasConfigVariable($var) ? preg_replace('/\s/', '', var_export($ptr->getConfigVariable($var), true)) : 'null';
$ptr = $ptr->parent;
} else {
$ptr = null;
}
}
- if (in_array('smarty', $types)) {
- $output .= "#Smarty:\${$var} =";
- $output .= isset($template->smarty->config_vars[ $var ]) ?
- preg_replace('/\s/', '', var_export($template->smarty->config_vars[ $var ], true)) : 'null';
+ if (in_array('global', $types)) {
+ $output .= "#global:\${$var} =";
+ $output .= $template->getSmarty()->hasConfigVariable($var) ?
+ preg_replace('/\s/', '', var_export($template->getSmarty()->getConfigVariable($var), true)) : 'null';
}
return $output;
}
diff --git a/tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php b/tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php
index 5d549c8b..62a4e674 100644
--- a/tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php
+++ b/tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php
@@ -2,55 +2,52 @@
/**
* Smarty plugin for testing scopes
*
- * @package Smarty
- * @subpackage PHPunitPlugin
+
+
*/
+use Smarty\Template;
+
/**
* Smarty {checkvar}
*
* @param array $params parameter array
- * @param object $template template object
+ * @param Template $template template object
*
* @return string
*/
-function smarty_function_checkvar($params, $template)
+function smarty_function_checkvar($params, \Smarty\Template $template)
{
$output = '';
- $types = array('template', 'data', 'smarty', 'global');
+ $types = ['template', 'data', 'global'];
if (isset($params['types'])) {
$types = (array)$params['types'];
}
$var = $params['var'];
$ptr = $template;
while ($ptr) {
- if (in_array('template', $types) && $ptr instanceof Smarty_Internal_Template) {
- $output .= "#{$ptr->source->name}:\${$var} =";
- $output .= isset($ptr->tpl_vars[$var]) ? preg_replace('/\s/', '', var_export($ptr->tpl_vars[$var]->value, true)) : '>unassigned<';
+ if (in_array('template', $types) && $ptr instanceof Template) {
+ $output .= "#{$ptr->getSource()->name}:\${$var} =";
+ $output .= $ptr->hasVariable($var) ? preg_replace('/\s/', '', var_export($ptr->getValue($var), true)) : '>unassigned<';
$i = 0;
- while (isset($ptr->_cache[ 'varStack' ][ $i ])) {
- $output .= "#{$ptr->_cache[ 'varStack' ][ $i ]['name']} = ";
- $output .= isset($ptr->_cache[ 'varStack' ][ $i ][ 'tpl' ][$var]) ? preg_replace('/\s/', '', var_export($ptr->_cache[ 'varStack' ][ $i ][ 'tpl' ][$var]->value, true)) : '>unassigned<';
+ while (isset($ptr->_var_stack[ $i ])) {
+ $output .= "#{$ptr->_var_stack[ $i ]['name']} = ";
+ $output .= isset($ptr->_var_stack[ $i ][ 'tpl' ][$var]) ? preg_replace('/\s/', '', var_export($ptr->_var_stack[ $i ][ 'tpl' ][$var]->value, true)) : '>unassigned<';
$i ++;
}
$ptr = $ptr->parent;
- } elseif (in_array('data', $types) && $ptr instanceof Smarty_Data) {
+ } elseif (in_array('data', $types) && !($ptr instanceof Template || $ptr instanceof \Smarty\Smarty)) {
$output .= "#data:\${$var} =";
- $output .= isset($ptr->tpl_vars[$var]) ? preg_replace('/\s/', '', var_export($ptr->tpl_vars[$var]->value, true)) : '>unassigned<';
+ $output .= $ptr->hasVariable($var) ? preg_replace('/\s/', '', var_export($ptr->getValue($var), true)) : '>unassigned<';
$ptr = $ptr->parent;
} else {
$ptr = null;
}
}
- if (in_array('smarty', $types)) {
- $output .= "#Smarty:\${$var} =";
- $output .= isset($template->smarty->tpl_vars[ $var ]) ?
- preg_replace('/\s/', '', var_export($template->smarty->tpl_vars[ $var ]->value, true)) : '>unassigned<';
- }
if (in_array('global', $types)) {
$output .= "#global:\${$var} =";
- $output .= isset(Smarty::$global_tpl_vars[ $var ]) ?
- preg_replace('/\s/', '', var_export(Smarty::$global_tpl_vars[ $var ]->value, true)) : '>unassigned<';
+ $output .= $template->getSmarty()->hasVariable($var) ?
+ preg_replace('/\s/', '', var_export($template->getSmarty()->getValue($var), true)) : '>unassigned<';
}
return $output;
}
diff --git a/tests/UnitTests/__shared/PHPunitplugins/function.getparams.php b/tests/UnitTests/__shared/PHPunitplugins/function.getparams.php
index be1c26ab..41075800 100644
--- a/tests/UnitTests/__shared/PHPunitplugins/function.getparams.php
+++ b/tests/UnitTests/__shared/PHPunitplugins/function.getparams.php
@@ -2,10 +2,12 @@
/**
* Smarty plugin params
*
- * @package Smarty
- * @subpackage PHPunitPlugin
+
+
*/
+use Smarty\Template;
+
/**
* Smarty {gatparams}
*
@@ -14,7 +16,7 @@
*
* @return string
*/
-function smarty_function_getparams($params, Smarty_Internal_Template $template)
+function smarty_function_getparams($params, Template $template)
{
return var_export($params, true);
}
diff --git a/tests/UnitTests/__shared/PHPunitplugins/function.getvar.php b/tests/UnitTests/__shared/PHPunitplugins/function.getvar.php
index 2b78fbbb..c1b0756b 100644
--- a/tests/UnitTests/__shared/PHPunitplugins/function.getvar.php
+++ b/tests/UnitTests/__shared/PHPunitplugins/function.getvar.php
@@ -2,10 +2,12 @@
/**
* Smarty plugin getvar
*
- * @package Smarty
- * @subpackage PHPunitPlugin
+
+
*/
+use Smarty\Template;
+
/**
* Smarty {getvar}
*
@@ -14,7 +16,7 @@
*
* @return string
*/
-function smarty_function_getvar($params, Smarty_Internal_Template $template)
+function smarty_function_getvar($params, Template $template)
{
if (isset($params[ 'assign' ])) {
$template->assign($params[ 'assign' ], $template->getTemplateVars($params[ 'var' ]));
diff --git a/tests/UnitTests/__shared/PHPunitplugins/function.pluginassign.php b/tests/UnitTests/__shared/PHPunitplugins/function.pluginassign.php
index 111d9d05..7786b742 100644
--- a/tests/UnitTests/__shared/PHPunitplugins/function.pluginassign.php
+++ b/tests/UnitTests/__shared/PHPunitplugins/function.pluginassign.php
@@ -2,8 +2,8 @@
/**
* Smarty plugin for assign
*
- * @package Smarty
- * @subpackage PHPunitPlugin
+
+
*/
/**
diff --git a/tests/UnitTests/__shared/cacheresources/cacheresource.apc.php b/tests/UnitTests/__shared/cacheresources/cacheresource.apc.php
new file mode 100644
index 00000000..3f437888
--- /dev/null
+++ b/tests/UnitTests/__shared/cacheresources/cacheresource.apc.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * APC CacheResource
+ * CacheResource Implementation based on the KeyValueStore API to use
+ * memcache as the storage resource for Smarty's output caching.
+ * *
+ *
+
+ * @author Uwe Tews
+ */
+class Smarty_CacheResource_Apc extends \Smarty\Cacheresource\KeyValueStore
+{
+
+ /**
+ * Read values for a set of keys from cache
+ *
+ * @param array $keys list of keys to fetch
+ *
+ * @return array list of values with the given keys used as indexes
+ * @return boolean true on success, false on failure
+ */
+ protected function read(array $keys)
+ {
+ $_res = array();
+ $res = apc_fetch($keys);
+ foreach ($res as $k => $v) {
+ $_res[ $k ] = $v;
+ }
+ return $_res;
+ }
+
+ /**
+ * Save values for a set of keys to cache
+ *
+ * @param array $keys list of values to save
+ * @param int $expire expiration time
+ *
+ * @return boolean true on success, false on failure
+ */
+ protected function write(array $keys, $expire = null)
+ {
+ foreach ($keys as $k => $v) {
+ apc_store($k, $v, $expire);
+ }
+ return true;
+ }
+
+ /**
+ * Remove values from cache
+ *
+ * @param array $keys list of keys to delete
+ *
+ * @return boolean true on success, false on failure
+ */
+ protected function delete(array $keys)
+ {
+ foreach ($keys as $k) {
+ apc_delete($k);
+ }
+ return true;
+ }
+
+ /**
+ * Remove *all* values from cache
+ *
+ * @return boolean true on success, false on failure
+ */
+ protected function purge()
+ {
+ return apc_clear_cache('user');
+ }
+}
diff --git a/tests/UnitTests/__shared/cacheresources/cacheresource.memcache.php b/tests/UnitTests/__shared/cacheresources/cacheresource.memcache.php
new file mode 100644
index 00000000..bc212a82
--- /dev/null
+++ b/tests/UnitTests/__shared/cacheresources/cacheresource.memcache.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * Memcache CacheResource
+ * CacheResource Implementation based on the KeyValueStore API to use
+ * memcache as the storage resource for Smarty's output caching.
+ * Note that memcache has a limitation of 256 characters per cache-key.
+ * To avoid complications all cache-keys are translated to a sha1 hash.
+ *
+
+ * @author Rodney Rehm
+ */
+class Smarty_CacheResource_Memcache extends \Smarty\Cacheresource\KeyValueStore
+{
+ /**
+ * memcache instance
+ *
+ * @var Memcache
+ */
+ private $memcache = null;
+
+ /**
+ * @return Memcache|Memcached
+ */
+ public function getMemcache() {
+ if ($this->memcache === null) {
+ if (class_exists('Memcached')) {
+ $this->memcache = new Memcached();
+ } else {
+ $this->memcache = new Memcache();
+ }
+ $this->memcache->addServer('127.0.0.1', 11211);
+ }
+ return $this->memcache;
+ }
+
+ /**
+ * Read values for a set of keys from cache
+ *
+ * @param array $keys list of keys to fetch
+ *
+ * @return array list of values with the given keys used as indexes
+ * @return boolean true on success, false on failure
+ */
+ protected function read(array $keys)
+ {
+ $res = array();
+ foreach ($keys as $key) {
+ $k = sha1($key);
+ $res[$key] = $this->getMemcache()->get($k);
+ }
+ return $res;
+ }
+
+ /**
+ * Save values for a set of keys to cache
+ *
+ * @param array $keys list of values to save
+ * @param int $expire expiration time
+ *
+ * @return boolean true on success, false on failure
+ */
+ protected function write(array $keys, $expire = null)
+ {
+ foreach ($keys as $k => $v) {
+ $k = sha1($k);
+ if (class_exists('Memcached')) {
+ $this->getMemcache()->set($k, $v, $expire);
+ } else {
+ $this->getMemcache()->set($k, $v, 0, $expire);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Remove values from cache
+ *
+ * @param array $keys list of keys to delete
+ *
+ * @return boolean true on success, false on failure
+ */
+ protected function delete(array $keys)
+ {
+ foreach ($keys as $k) {
+ $k = sha1($k);
+ $this->getMemcache()->delete($k);
+ }
+ return true;
+ }
+
+ /**
+ * Remove *all* values from cache
+ *
+ * @return boolean true on success, false on failure
+ */
+ protected function purge()
+ {
+ return $this->getMemcache()->flush();
+ }
+}
diff --git a/tests/UnitTests/__shared/cacheresources/cacheresource.mysql.php b/tests/UnitTests/__shared/cacheresources/cacheresource.mysql.php
new file mode 100644
index 00000000..9b51c272
--- /dev/null
+++ b/tests/UnitTests/__shared/cacheresources/cacheresource.mysql.php
@@ -0,0 +1,190 @@
+<?php
+
+use Smarty\Exception;
+
+/**
+ * MySQL CacheResource
+ * CacheResource Implementation based on the Custom API to use
+ * MySQL as the storage resource for Smarty's output caching.
+ * Table definition:
+ * <pre>CREATE TABLE IF NOT EXISTS `output_cache` (
+ * `id` CHAR(40) NOT NULL COMMENT 'sha1 hash',
+ * `name` VARCHAR(250) NOT NULL,
+ * `cache_id` VARCHAR(250) NULL DEFAULT NULL,
+ * `compile_id` VARCHAR(250) NULL DEFAULT NULL,
+ * `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ * `content` LONGTEXT NOT NULL,
+ * PRIMARY KEY (`id`),
+ * INDEX(`name`),
+ * INDEX(`cache_id`),
+ * INDEX(`compile_id`),
+ * INDEX(`modified`)
+ * ) ENGINE = InnoDB;</pre>
+ *
+
+ * @author Rodney Rehm
+ */
+class Smarty_CacheResource_Mysql extends \Smarty\Cacheresource\Custom
+{
+
+ /**
+ * @return PDO
+ * @throws Exception
+ */
+ protected function db(): PDO {
+ static $dbConn = null;
+ try {
+ return $dbConn ?? ($dbConn = new PDO("mysql:dbname=test;host=127.0.0.1", "smarty"));
+ } catch (PDOException $e) {
+ throw new Exception('Mysql Resource failed: ' . $e->getMessage());
+ }
+ }
+
+ /**
+ * @return false|PDOStatement
+ * @throws Exception
+ */
+ protected function fetchQuery() {
+ static $query = null;
+ return $query ?? $query = $this->db()->prepare('SELECT modified, content FROM output_cache WHERE id = :id');
+ }
+
+ /**
+ * @return false|PDOStatement
+ * @throws Exception
+ */
+ protected function fetchTimestampQuery() {
+ static $query = null;
+ return $query ?? $query = $this->db()->prepare('SELECT modified FROM output_cache WHERE id = :id');
+ }
+
+ /**
+ * @return false|PDOStatement
+ * @throws Exception
+ */
+ protected function saveQuery() {
+ static $query = null;
+ return $query ?? $query = $this->db()->prepare(
+ 'REPLACE INTO output_cache (id, name, cache_id, compile_id, content)
+ VALUES (:id, :name, :cache_id, :compile_id, :content)'
+ );
+ }
+
+ /**
+ * fetch cached content and its modification time from data source
+ *
+ * @param string $id unique cache content identifier
+ * @param string $name template name
+ * @param string $cache_id cache id
+ * @param string $compile_id compile id
+ * @param string $content cached content
+ * @param integer $mtime cache modification timestamp (epoch)
+ *
+ * @return void
+ * @throws Exception
+ */
+ protected function fetch($id, $name, $cache_id, $compile_id, &$content, &$mtime)
+ {
+ $this->fetchQuery()->execute(array('id' => $id));
+ $row = $this->fetchQuery()->fetch();
+ $this->fetchQuery()->closeCursor();
+ if ($row) {
+ $content = $row[ 'content' ];
+ $mtime = strtotime($row[ 'modified' ]);
+ } else {
+ $content = null;
+ $mtime = null;
+ }
+ }
+
+ /**
+ * Fetch cached content's modification timestamp from data source
+ *
+ * @note implementing this method is optional. Only implement it if modification times can be accessed faster than
+ * loading the complete cached content.
+ *
+ * @param string $id unique cache content identifier
+ * @param string $name template name
+ * @param string $cache_id cache id
+ * @param string $compile_id compile id
+ *
+ * @return integer|boolean timestamp (epoch) the template was modified, or false if not found
+ */
+ protected function fetchTimestamp($id, $name, $cache_id, $compile_id)
+ {
+ $this->fetchTimestampQuery()->execute(array('id' => $id));
+ $mtime = strtotime($this->fetchTimestampQuery()->fetchColumn());
+ $this->fetchTimestampQuery()->closeCursor();
+ return $mtime;
+ }
+
+ /**
+ * Save content to cache
+ *
+ * @param string $id unique cache content identifier
+ * @param string $name template name
+ * @param string $cache_id cache id
+ * @param string $compile_id compile id
+ * @param integer|null $exp_time seconds till expiration time in seconds or null
+ * @param string $content content to cache
+ *
+ * @return boolean success
+ */
+ protected function save($id, $name, $cache_id, $compile_id, $exp_time, $content)
+ {
+ $this->saveQuery()->execute(
+ array('id' => $id,
+ 'name' => $name,
+ 'cache_id' => $cache_id,
+ 'compile_id' => $compile_id,
+ 'content' => $content,)
+ );
+ return !!$this->saveQuery()->rowCount();
+ }
+
+ /**
+ * Delete content from cache
+ *
+ * @param string $name template name
+ * @param string $cache_id cache id
+ * @param string $compile_id compile id
+ * @param integer|null $exp_time seconds till expiration or null
+ *
+ * @return integer number of deleted caches
+ */
+ protected function delete($name, $cache_id, $compile_id, $exp_time)
+ {
+ // delete the whole cache
+ if ($name === null && $cache_id === null && $compile_id === null && $exp_time === null) {
+ // returning the number of deleted caches would require a second query to count them
+ $query = $this->db()->query('TRUNCATE TABLE output_cache');
+ return -1;
+ }
+ // build the filter
+ $where = array();
+ // equal test name
+ if ($name !== null) {
+ $where[] = 'name = ' . $this->db()->quote($name);
+ }
+ // equal test compile_id
+ if ($compile_id !== null) {
+ $where[] = 'compile_id = ' . $this->db()->quote($compile_id);
+ }
+ // range test expiration time
+ if ($exp_time !== null) {
+ $where[] = 'modified < DATE_SUB(NOW(), INTERVAL ' . intval($exp_time) . ' SECOND)';
+ }
+ // equal test cache_id and match sub-groups
+ if ($cache_id !== null) {
+ $where[] =
+ '(cache_id = ' .
+ $this->db()->quote($cache_id) .
+ ' OR cache_id LIKE ' .
+ $this->db()->quote($cache_id . '|%') .
+ ')';
+ }
+ // run delete query
+ $query = $this->db()->query('DELETE FROM output_cache WHERE ' . join(' AND ', $where));
+ return $query->rowCount();
+ }
+}
diff --git a/tests/UnitTests/__shared/cacheresources/cacheresource.pdo.php b/tests/UnitTests/__shared/cacheresources/cacheresource.pdo.php
new file mode 100644
index 00000000..8be18ec9
--- /dev/null
+++ b/tests/UnitTests/__shared/cacheresources/cacheresource.pdo.php
@@ -0,0 +1,347 @@
+<?php
+
+use Smarty\Exception;
+
+/**
+ * PDO Cache Handler
+ * Allows you to store Smarty Cache files into your db.
+ * Example table :
+ * CREATE TABLE `smarty_cache` (
+ * `id` char(40) NOT NULL COMMENT 'sha1 hash',
+ * `name` varchar(250) NOT NULL,
+ * `cache_id` varchar(250) DEFAULT NULL,
+ * `compile_id` varchar(250) DEFAULT NULL,
+ * `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ * `expire` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+ * `content` mediumblob NOT NULL,
+ * PRIMARY KEY (`id`),
+ * KEY `name` (`name`),
+ * KEY `cache_id` (`cache_id`),
+ * KEY `compile_id` (`compile_id`),
+ * KEY `modified` (`modified`),
+ * KEY `expire` (`expire`)
+ * ) ENGINE=InnoDB
+ * Example usage :
+ * $cnx = new PDO("mysql:host=localhost;dbname=mydb", "username", "password");
+ * $smarty->setCachingType('pdo');
+ * $smarty->registerCacheResource('pdo', new Smarty_CacheResource_Pdo($cnx, 'smarty_cache'));
+ *
+ * @author Beno!t POLASZEK - 2014
+ */
+class Smarty_CacheResource_Pdo extends \Smarty\Cacheresource\Custom
+{
+ /**
+ * @var string[]
+ */
+ protected $fetchStatements = array('default' => 'SELECT %2$s
+ FROM %1$s
+ WHERE 1
+ AND id = :id
+ AND cache_id IS NULL
+ AND compile_id IS NULL',
+ 'withCacheId' => 'SELECT %2$s
+ FROM %1$s
+ WHERE 1
+ AND id = :id
+ AND cache_id = :cache_id
+ AND compile_id IS NULL',
+ 'withCompileId' => 'SELECT %2$s
+ FROM %1$s
+ WHERE 1
+ AND id = :id
+ AND compile_id = :compile_id
+ AND cache_id IS NULL',
+ 'withCacheIdAndCompileId' => 'SELECT %2$s
+ FROM %1$s
+ WHERE 1
+ AND id = :id
+ AND cache_id = :cache_id
+ AND compile_id = :compile_id');
+
+ /**
+ * @var string
+ */
+ protected $insertStatement = 'INSERT INTO %s
+
+ SET id = :id,
+ name = :name,
+ cache_id = :cache_id,
+ compile_id = :compile_id,
+ modified = CURRENT_TIMESTAMP,
+ expire = DATE_ADD(CURRENT_TIMESTAMP, INTERVAL :expire SECOND),
+ content = :content
+
+ ON DUPLICATE KEY UPDATE
+ name = :name,
+ cache_id = :cache_id,
+ compile_id = :compile_id,
+ modified = CURRENT_TIMESTAMP,
+ expire = DATE_ADD(CURRENT_TIMESTAMP, INTERVAL :expire SECOND),
+ content = :content';
+
+ /**
+ * @var string
+ */
+ protected $deleteStatement = 'DELETE FROM %1$s WHERE %2$s';
+
+ /**
+ * @var string
+ */
+ protected $truncateStatement = 'TRUNCATE TABLE %s';
+
+ /**
+ * @var string
+ */
+ protected $fetchColumns = 'modified, content';
+
+ /**
+ * @var string
+ */
+ protected $fetchTimestampColumns = 'modified';
+
+ /**
+ * @var \PDO
+ */
+ protected $pdo;
+
+ /**
+ * @var
+ */
+ protected $table;
+
+ /**
+ * @var null
+ */
+ protected $database;
+
+ /**
+ * Constructor
+ *
+ * @param PDO $pdo PDO : active connection
+ * @param string $table : table (or view) name
+ * @param string $database : optional - if table is located in another db
+ *
+ * @throws \Smarty\Exception
+ */
+ public function __construct(PDO $pdo, $table, $database = null)
+ {
+ if (is_null($table)) {
+ throw new Exception("Table name for caching can't be null");
+ }
+ $this->pdo = $pdo;
+ $this->table = $table;
+ $this->database = $database;
+ $this->fillStatementsWithTableName();
+ }
+
+ /**
+ * Fills the table name into the statements.
+ *
+ * @return $this Current Instance
+ * @access protected
+ */
+ protected function fillStatementsWithTableName()
+ {
+ foreach ($this->fetchStatements as &$statement) {
+ $statement = sprintf($statement, $this->getTableName(), '%s');
+ }
+ $this->insertStatement = sprintf($this->insertStatement, $this->getTableName());
+ $this->deleteStatement = sprintf($this->deleteStatement, $this->getTableName(), '%s');
+ $this->truncateStatement = sprintf($this->truncateStatement, $this->getTableName());
+ return $this;
+ }
+
+ /**
+ * Gets the fetch statement, depending on what you specify
+ *
+ * @param string $columns : the column(s) name(s) you want to retrieve from the database
+ * @param string $id unique cache content identifier
+ * @param string|null $cache_id cache id
+ * @param string|null $compile_id compile id
+ *
+ * @access protected
+ * @return \PDOStatement
+ */
+ protected function getFetchStatement($columns, $id, $cache_id = null, $compile_id = null)
+ {
+ $args = array();
+ if (!is_null($cache_id) && !is_null($compile_id)) {
+ $query = $this->fetchStatements[ 'withCacheIdAndCompileId' ] and
+ $args = array('id' => $id, 'cache_id' => $cache_id, 'compile_id' => $compile_id);
+ } elseif (is_null($cache_id) && !is_null($compile_id)) {
+ $query = $this->fetchStatements[ 'withCompileId' ] and
+ $args = array('id' => $id, 'compile_id' => $compile_id);
+ } elseif (!is_null($cache_id) && is_null($compile_id)) {
+ $query = $this->fetchStatements[ 'withCacheId' ] and $args = array('id' => $id, 'cache_id' => $cache_id);
+ } else {
+ $query = $this->fetchStatements[ 'default' ] and $args = array('id' => $id);
+ }
+ $query = sprintf($query, $columns);
+ $stmt = $this->pdo->prepare($query);
+ foreach ($args as $key => $value) {
+ $stmt->bindValue($key, $value);
+ }
+ return $stmt;
+ }
+
+ /**
+ * fetch cached content and its modification time from data source
+ *
+ * @param string $id unique cache content identifier
+ * @param string $name template name
+ * @param string|null $cache_id cache id
+ * @param string|null $compile_id compile id
+ * @param string $content cached content
+ * @param integer $mtime cache modification timestamp (epoch)
+ *
+ * @return void
+ * @access protected
+ */
+ protected function fetch($id, $name, $cache_id, $compile_id, &$content, &$mtime)
+ {
+ $stmt = $this->getFetchStatement($this->fetchColumns, $id, $cache_id, $compile_id);
+ $stmt->execute();
+ $row = $stmt->fetch();
+ $stmt->closeCursor();
+ if ($row) {
+ $content = $this->outputContent($row[ 'content' ]);
+ $mtime = strtotime($row[ 'modified' ]);
+ } else {
+ $content = null;
+ $mtime = null;
+ }
+ }
+
+ /**
+ * Fetch cached content's modification timestamp from data source
+ * {@internal implementing this method is optional.
+ * Only implement it if modification times can be accessed faster than loading the complete cached content.}}
+ *
+ * @param string $id unique cache content identifier
+ * @param string $name template name
+ * @param string|null $cache_id cache id
+ * @param string|null $compile_id compile id
+ *
+ * @return integer|boolean timestamp (epoch) the template was modified, or false if not found
+ * @access protected
+ */
+ // protected function fetchTimestamp($id, $name, $cache_id = null, $compile_id = null) {
+ // $stmt = $this->getFetchStatement($this->fetchTimestampColumns, $id, $cache_id, $compile_id);
+ // $stmt -> execute();
+ // $mtime = strtotime($stmt->fetchColumn());
+ // $stmt -> closeCursor();
+ // return $mtime;
+ // }
+ /**
+ * Save content to cache
+ *
+ * @param string $id unique cache content identifier
+ * @param string $name template name
+ * @param string|null $cache_id cache id
+ * @param string|null $compile_id compile id
+ * @param integer|null $exp_time seconds till expiration time in seconds or null
+ * @param string $content content to cache
+ *
+ * @return boolean success
+ * @access protected
+ */
+ protected function save($id, $name, $cache_id, $compile_id, $exp_time, $content)
+ {
+ $stmt = $this->pdo->prepare($this->insertStatement);
+ $stmt->bindValue('id', $id);
+ $stmt->bindValue('name', $name);
+ $stmt->bindValue('cache_id', $cache_id, (is_null($cache_id)) ? PDO::PARAM_NULL : PDO::PARAM_STR);
+ $stmt->bindValue('compile_id', $compile_id, (is_null($compile_id)) ? PDO::PARAM_NULL : PDO::PARAM_STR);
+ $stmt->bindValue('expire', (int)$exp_time, PDO::PARAM_INT);
+ $stmt->bindValue('content', $this->inputContent($content));
+ $stmt->execute();
+ return !!$stmt->rowCount();
+ }
+
+ /**
+ * Encodes the content before saving to database
+ *
+ * @param string $content
+ *
+ * @return string $content
+ * @access protected
+ */
+ protected function inputContent($content)
+ {
+ return $content;
+ }
+
+ /**
+ * Decodes the content before saving to database
+ *
+ * @param string $content
+ *
+ * @return string $content
+ * @access protected
+ */
+ protected function outputContent($content)
+ {
+ return $content;
+ }
+
+ /**
+ * Delete content from cache
+ *
+ * @param string|null $name template name
+ * @param string|null $cache_id cache id
+ * @param string|null $compile_id compile id
+ * @param integer|null|-1 $exp_time seconds till expiration or null
+ *
+ * @return integer number of deleted caches
+ * @access protected
+ */
+ protected function delete($name = null, $cache_id = null, $compile_id = null, $exp_time = null)
+ {
+ // delete the whole cache
+ if ($name === null && $cache_id === null && $compile_id === null && $exp_time === null) {
+ // returning the number of deleted caches would require a second query to count them
+ $this->pdo->query($this->truncateStatement);
+ return -1;
+ }
+ // build the filter
+ $where = array();
+ // equal test name
+ if ($name !== null) {
+ $where[] = 'name = ' . $this->pdo->quote($name);
+ }
+ // equal test cache_id and match sub-groups
+ if ($cache_id !== null) {
+ $where[] =
+ '(cache_id = ' .
+ $this->pdo->quote($cache_id) .
+ ' OR cache_id LIKE ' .
+ $this->pdo->quote($cache_id . '|%') .
+ ')';
+ }
+ // equal test compile_id
+ if ($compile_id !== null) {
+ $where[] = 'compile_id = ' . $this->pdo->quote($compile_id);
+ }
+ // for clearing expired caches
+ if ($exp_time === \Smarty\Smarty::CLEAR_EXPIRED) {
+ $where[] = 'expire < CURRENT_TIMESTAMP';
+ } // range test expiration time
+ elseif ($exp_time !== null) {
+ $where[] = 'modified < DATE_SUB(NOW(), INTERVAL ' . intval($exp_time) . ' SECOND)';
+ }
+ // run delete query
+ $query = $this->pdo->query(sprintf($this->deleteStatement, join(' AND ', $where)));
+ return $query->rowCount();
+ }
+
+ /**
+ * Gets the formatted table name
+ *
+ * @return string
+ * @access protected
+ */
+ protected function getTableName()
+ {
+ return (is_null($this->database)) ? "`{$this->table}`" : "`{$this->database}`.`{$this->table}`";
+ }
+}
diff --git a/tests/UnitTests/__shared/cacheresources/cacheresource.pdo_gzip.php b/tests/UnitTests/__shared/cacheresources/cacheresource.pdo_gzip.php
new file mode 100644
index 00000000..89818abb
--- /dev/null
+++ b/tests/UnitTests/__shared/cacheresources/cacheresource.pdo_gzip.php
@@ -0,0 +1,42 @@
+<?php
+
+require_once __DIR__ . '/cacheresource.pdo.php';
+
+/**
+ * PDO Cache Handler with GZIP support
+ * Example usage :
+ * $cnx = new PDO("mysql:host=localhost;dbname=mydb", "username", "password");
+ * $smarty->setCachingType('pdo_gzip');
+ * $smarty->registerCacheResource('pdo_gzip', new Smarty_CacheResource_Pdo_Gzip($cnx, 'smarty_cache'));
+ *
+ * @require Smarty_CacheResource_Pdo class
+ * @author Beno!t POLASZEK - 2014
+ */
+class Smarty_CacheResource_Pdo_Gzip extends Smarty_CacheResource_Pdo
+{
+ /**
+ * Encodes the content before saving to database
+ *
+ * @param string $content
+ *
+ * @return string $content
+ * @access protected
+ */
+ protected function inputContent($content)
+ {
+ return gzdeflate($content);
+ }
+
+ /**
+ * Decodes the content before saving to database
+ *
+ * @param string $content
+ *
+ * @return string $content
+ * @access protected
+ */
+ protected function outputContent($content)
+ {
+ return gzinflate($content);
+ }
+}
diff --git a/tests/UnitTests/__shared/resources/resource.extendsall.php b/tests/UnitTests/__shared/resources/resource.extendsall.php
new file mode 100644
index 00000000..9ffddc05
--- /dev/null
+++ b/tests/UnitTests/__shared/resources/resource.extendsall.php
@@ -0,0 +1,66 @@
+<?php
+
+use Smarty\Exception;
+use Smarty\Template;
+use Smarty\Template\Source;
+
+/**
+ * Extends All Resource
+ * Resource Implementation modifying the extends-Resource to walk
+ * through the template_dirs and inherit all templates of the same name
+ *
+
+ * @author Rodney Rehm
+ */
+class My_Resource_Extendsall extends \Smarty\Resource\ExtendsPlugin
+{
+ /**
+ * populate Source Object with meta data from Resource
+ *
+ * @param Source $source source object
+ * @param Template $_template template object
+ *
+ * @return void
+ */
+ public function populate(Source $source, Template $_template = null)
+ {
+ $uid = '';
+ $sources = array();
+ $timestamp = 0;
+ foreach ($source->getSmarty()->getTemplateDir() as $key => $directory) {
+ try {
+ $s = \Smarty\Template\Source::load(null, $source->getSmarty(),
+ 'file:' . '[' . $key . ']' . $source->name);
+ if (!$s->exists) {
+ continue;
+ }
+ $sources[ $s->uid ] = $s;
+ $uid .= $s->uid;
+ $timestamp = $s->timestamp > $timestamp ? $s->timestamp : $timestamp;
+ } catch (Exception $e) {
+ }
+ }
+ if (!$sources) {
+ $source->exists = false;
+ return;
+ }
+ $sources = array_reverse($sources, true);
+ reset($sources);
+ $s = current($sources);
+ $source->components = $sources;
+ $source->uid = sha1($uid . $source->getSmarty()->_joined_template_dir);
+ $source->exists = true;
+ $source->timestamp = $timestamp;
+ }
+
+ /**
+ * Disable timestamp checks for extendsall resource.
+ * The individual source components will be checked.
+ *
+ * @return bool false
+ */
+ public function checkTimestamps()
+ {
+ return false;
+ }
+}