mirror of
https://github.com/smarty-php/smarty.git
synced 2026-08-04 04:24:18 +02:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 287e12c3be | |||
| 2a87c65994 | |||
| cdee97d3f1 | |||
| 06d6a5efd9 | |||
| 0be92bc8a6 | |||
| 61db287b8f | |||
| 768acd96fa | |||
| baa83541d9 | |||
| 6f054ecc2f | |||
| 4fec27ccc2 | |||
| fea0d02d99 |
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [5.2.0] - 2024-05-28
|
||||
- Fixed a code injection vulnerability in extends-tag. This addresses CVE-2024-35226.
|
||||
- Added `$smarty->setCacheModifiedCheck()` setter for cache_modified_check
|
||||
- Added a PSR-4 loading script to allow Smarty to be used without Composer [#1017](https://github.com/smarty-php/smarty/pull/1017)
|
||||
|
||||
|
||||
## [5.1.0] - 2024-04-22
|
||||
- Prevent deprecation notices during compilation in PHP8.3 [#996](https://github.com/smarty-php/smarty/issues/996)
|
||||
- Fix that getTemplateVars would return an array of objects instead of the assigned variables values [#994](https://github.com/smarty-php/smarty/issues/994)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
- Added `$smarty->prependTemplateDir()` method [#1022](https://github.com/smarty-php/smarty/issues/1022)
|
||||
@@ -0,0 +1 @@
|
||||
- Fix Smarty::assign() not returning $this when called with an array as first parameter [#972](https://github.com/smarty-php/smarty/pull/972)
|
||||
@@ -0,0 +1 @@
|
||||
- Fixed missing Smarty getErrorUnassigned/setErrorUnassigned methods [#979](https://github.com/smarty-php/smarty/issues/979)
|
||||
+13
-10
@@ -12,24 +12,27 @@ Use `getTemplateDir()` to retrieve the configured paths.
|
||||
<?php
|
||||
|
||||
// set a single directory where the config files are stored
|
||||
$smarty->setTemplateDir('./config');
|
||||
$smarty->setTemplateDir('./templates');
|
||||
|
||||
// set multiple directories where config files are stored
|
||||
$smarty->setTemplateDir(['./config', './config_2', './config_3']);
|
||||
// set multiple directories where templates are stored
|
||||
$smarty->setTemplateDir(['./templates', './templates_2', './templates_3']);
|
||||
|
||||
// add directory where config files are stored to the current list of dirs
|
||||
$smarty->addTemplateDir('./config_1');
|
||||
// add directory where templates files are stored to the current list of dirs
|
||||
$smarty->addTemplateDir('./templates_1');
|
||||
|
||||
// add multiple directories to the current list of dirs
|
||||
$smarty->addTemplateDir([
|
||||
'./config_2',
|
||||
'./config_3',
|
||||
'./templates_2',
|
||||
'./templates_3',
|
||||
]);
|
||||
|
||||
// chaining of method calls
|
||||
$smarty->setTemplateDir('./config')
|
||||
->addTemplateDir('./config_1')
|
||||
->addTemplateDir('./config_2');
|
||||
$smarty->setTemplateDir('./templates')
|
||||
->addTemplateDir('./templates_1')
|
||||
->addTemplateDir('./templates_2');
|
||||
|
||||
// insert a template dir before exising template dirs
|
||||
$smarty->prependTemplateDir('./more_important_templates')
|
||||
|
||||
// get all directories where config files are stored
|
||||
$template_dirs = $smarty->getTemplateDir();
|
||||
|
||||
@@ -25,9 +25,17 @@ Here's how you create an instance of Smarty in your PHP scripts:
|
||||
```php
|
||||
<?php
|
||||
|
||||
// Instantiated via composer
|
||||
require 'vendor/autoload.php';
|
||||
use Smarty\Smarty;
|
||||
$smarty = new Smarty();
|
||||
|
||||
// or ...
|
||||
|
||||
// Instantiated directly
|
||||
require("/path/to/smarty/libs/Smarty.class.php");
|
||||
use Smarty\Smarty;
|
||||
$smarty = new Smarty();
|
||||
```
|
||||
|
||||
Now that the library files are in place, it's time to set up the Smarty
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// This is a stub PSR-4 loading script that gets all the pieces of //
|
||||
// Smarty 5.x loaded without requiring the use of composer. It's //
|
||||
// not really a 'class' file, but the name is used so we're //
|
||||
// backwards compatible with previous versions of Smarty. //
|
||||
// //
|
||||
// Example: //
|
||||
// require_once("/path/to/smarty/libs/Smarty.class.php"); //
|
||||
// //
|
||||
// $smarty = new Smarty\Smarty; //
|
||||
// $smarty->testInstall(); //
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
define('__SMARTY_DIR', __DIR__ . '/../src/');
|
||||
|
||||
// Global function declarations
|
||||
require_once(__SMARTY_DIR . "/functions.php");
|
||||
|
||||
spl_autoload_register(function ($class) {
|
||||
// Class prefix
|
||||
$prefix = 'Smarty\\';
|
||||
|
||||
// Does the class use the namespace prefix?
|
||||
$len = strlen($prefix);
|
||||
if (strncmp($prefix, $class, $len) !== 0) {
|
||||
// If not, move to the next registered autoloader
|
||||
return;
|
||||
}
|
||||
|
||||
// Hack off the prefix part
|
||||
$relative_class = substr($class, $len);
|
||||
|
||||
// Build a path to the include file
|
||||
$file = __SMARTY_DIR . str_replace('\\', '/', $relative_class) . '.php';
|
||||
|
||||
// If the file exists, require it
|
||||
if (file_exists($file)) {
|
||||
require_once($file);
|
||||
}
|
||||
});
|
||||
+1
-1
@@ -15,7 +15,7 @@ php utilities/update-smarty-version-number.php $1
|
||||
git add changelog CHANGELOG.md src/Smarty.php
|
||||
git commit -m "version bump"
|
||||
|
||||
git checkout master
|
||||
git checkout support/5
|
||||
git pull
|
||||
git merge --no-ff "release/$1"
|
||||
git branch -d "release/$1"
|
||||
|
||||
@@ -32,7 +32,7 @@ class ExtendsTag extends Inheritance {
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $optional_attributes = ['extends_resource'];
|
||||
protected $optional_attributes = [];
|
||||
|
||||
/**
|
||||
* Attribute definition: Overwrites base class.
|
||||
@@ -64,29 +64,7 @@ class ExtendsTag extends Inheritance {
|
||||
}
|
||||
// add code to initialize inheritance
|
||||
$this->registerInit($compiler, true);
|
||||
$file = trim($_attr['file'], '\'"');
|
||||
if (strlen($file) > 8 && substr($file, 0, 8) === 'extends:') {
|
||||
// generate code for each template
|
||||
$files = array_reverse(explode('|', substr($file, 8)));
|
||||
$i = 0;
|
||||
foreach ($files as $file) {
|
||||
if ($file[0] === '"') {
|
||||
$file = trim($file, '".');
|
||||
} else {
|
||||
$file = "'{$file}'";
|
||||
}
|
||||
$i++;
|
||||
if ($i === count($files) && isset($_attr['extends_resource'])) {
|
||||
$this->compileEndChild($compiler);
|
||||
}
|
||||
$this->compileInclude($compiler, $file);
|
||||
}
|
||||
if (!isset($_attr['extends_resource'])) {
|
||||
$this->compileEndChild($compiler);
|
||||
}
|
||||
} else {
|
||||
$this->compileEndChild($compiler, $_attr['file']);
|
||||
}
|
||||
$this->compileEndChild($compiler, $_attr['file']);
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -106,42 +84,4 @@ class ExtendsTag extends Inheritance {
|
||||
(isset($template) ? ", {$template}, \$_smarty_current_dir" : '') . ");\n?>"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add code for including subtemplate to end of template
|
||||
*
|
||||
* @param \Smarty\Compiler\Template $compiler
|
||||
* @param string $template subtemplate name
|
||||
*
|
||||
* @throws \Smarty\CompilerException
|
||||
* @throws \Smarty\Exception
|
||||
*/
|
||||
private function compileInclude(\Smarty\Compiler\Template $compiler, $template) {
|
||||
$compiler->getParser()->template_postfix[] = new \Smarty\ParseTree\Tag(
|
||||
$compiler->getParser(),
|
||||
$compiler->compileTag(
|
||||
'include',
|
||||
[
|
||||
$template,
|
||||
['scope' => 'parent'],
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create source code for {extends} from source components array
|
||||
*
|
||||
* @param \Smarty\Template $template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function extendsSourceArrayCode(\Smarty\Template $template) {
|
||||
$resources = [];
|
||||
foreach ($template->getSource()->components as $source) {
|
||||
$resources[] = $source->resource;
|
||||
}
|
||||
return $template->getLeftDelimiter() . 'extends file=\'extends:' . join('|', $resources) .
|
||||
'\' extends_resource=true' . $template->getRightDelimiter();
|
||||
}
|
||||
}
|
||||
|
||||
+27
-11
@@ -403,21 +403,37 @@ class Template extends BaseCompiler {
|
||||
}
|
||||
// get template source
|
||||
if (!empty($this->template->getSource()->components)) {
|
||||
// we have array of inheritance templates by extends: resource
|
||||
// generate corresponding source code sequence
|
||||
$_content =
|
||||
ExtendsTag::extendsSourceArrayCode($this->template);
|
||||
|
||||
$_compiled_code = '<?php $_smarty_tpl->getInheritance()->init($_smarty_tpl, true); ?>';
|
||||
|
||||
$i = 0;
|
||||
$reversed_components = array_reverse($this->template->getSource()->components);
|
||||
foreach ($reversed_components as $source) {
|
||||
$i++;
|
||||
if ($i === count($reversed_components)) {
|
||||
$_compiled_code .= '<?php $_smarty_tpl->getInheritance()->endChild($_smarty_tpl); ?>';
|
||||
}
|
||||
$_compiled_code .= $this->compileTag(
|
||||
'include',
|
||||
[
|
||||
var_export($source->resource, true),
|
||||
['scope' => 'parent'],
|
||||
]
|
||||
);
|
||||
}
|
||||
$_compiled_code = $this->smarty->runPostFilters($_compiled_code, $this->template);
|
||||
} else {
|
||||
// get template source
|
||||
$_content = $this->template->getSource()->getContent();
|
||||
$_compiled_code = $this->smarty->runPostFilters(
|
||||
$this->doCompile(
|
||||
$this->smarty->runPreFilters($_content, $this->template),
|
||||
true
|
||||
),
|
||||
$this->template
|
||||
);
|
||||
}
|
||||
$_compiled_code = $this->smarty->runPostFilters(
|
||||
$this->doCompile(
|
||||
$this->smarty->runPreFilters($_content, $this->template),
|
||||
true
|
||||
),
|
||||
$this->template
|
||||
);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
if ($this->smarty->debugging) {
|
||||
$this->smarty->getDebug()->end_compile($this->template);
|
||||
|
||||
+41
-2
@@ -54,7 +54,7 @@ class Smarty extends \Smarty\TemplateBase {
|
||||
/**
|
||||
* smarty version
|
||||
*/
|
||||
const SMARTY_VERSION = '5.1.0';
|
||||
const SMARTY_VERSION = '5.2.0';
|
||||
|
||||
/**
|
||||
* define caching modes
|
||||
@@ -684,6 +684,21 @@ class Smarty extends \Smarty\TemplateBase {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a template directory before any existing directoires
|
||||
*
|
||||
* @param string $new_template_dir directory of template sources
|
||||
* @param bool $is_config true for config_dir
|
||||
*
|
||||
* @return static current Smarty instance for chaining
|
||||
*/
|
||||
public function prependTemplateDir($new_template_dir, $is_config = false) {
|
||||
$current_template_dirs = $is_config ? $this->config_dir : $this->template_dir;
|
||||
array_unshift($current_template_dirs, $new_template_dir);
|
||||
$this->setTemplateDir($current_template_dirs, $is_config);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add config directory(s)
|
||||
*
|
||||
@@ -2211,5 +2226,29 @@ class Smarty extends \Smarty\TemplateBase {
|
||||
return $template;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Whether Smarty displays an error when using an unassigned variable
|
||||
*/
|
||||
public function getErrorUnassigned(): bool
|
||||
{
|
||||
return (bool) $this->error_unassigned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if Smarty should display an error on using an unassigned variable
|
||||
*/
|
||||
public function setErrorUnassigned(bool $error_unassigned): void
|
||||
{
|
||||
$this->error_unassigned = $error_unassigned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if Smarty should check If-Modified-Since headers to determine cache validity.
|
||||
* @param bool $cache_modified_check
|
||||
* @return void
|
||||
*/
|
||||
public function setCacheModifiedCheck($cache_modified_check): void {
|
||||
$this->cache_modified_check = (bool) $cache_modified_check;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -91,4 +91,13 @@ class FileResourceIndexedTest extends PHPUnit_Smarty
|
||||
|
||||
$this->assertNotEquals($tpl->getCached()->filepath, $tpl2->getCached()->filepath);
|
||||
}
|
||||
|
||||
public function testPrependTemplatePath()
|
||||
{
|
||||
$this->smarty->setTemplateDir(__DIR__ . '/templates');
|
||||
$this->smarty->prependTemplateDir(__DIR__ . '/templates_4');
|
||||
$tpl = $this->smarty->createTemplate('dirname.tpl');
|
||||
$this->assertEquals('templates_4', $this->smarty->fetch($tpl));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1193,8 +1193,38 @@ class CompileBlockExtendsTest extends PHPUnit_Smarty
|
||||
);
|
||||
}
|
||||
|
||||
public function testBlockWithAssign() {
|
||||
$this->assertEquals('Captured content is: Content with lots of html here', $this->smarty->fetch('038_child.tpl'));
|
||||
}
|
||||
public function testBlockWithAssign() {
|
||||
$this->assertEquals('Captured content is: Content with lots of html here', $this->smarty->fetch('038_child.tpl'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test escaping of file parameter
|
||||
*/
|
||||
public function testEscaping()
|
||||
{
|
||||
$this->expectException(\Smarty\Exception::class);
|
||||
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||
$this->assertEquals('hello world', $this->smarty->fetch('escaping.tpl'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test escaping of file parameter 2
|
||||
*/
|
||||
public function testEscaping2()
|
||||
{
|
||||
$this->expectException(\Smarty\Exception::class);
|
||||
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||
$this->assertEquals('hello world', $this->smarty->fetch('escaping2.tpl'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test escaping of file parameter 3
|
||||
*/
|
||||
public function testEscaping3()
|
||||
{
|
||||
$this->expectException(\Smarty\Exception::class);
|
||||
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||
$this->assertEquals('hello world', $this->smarty->fetch('escaping3.tpl'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{extends "extends:helloworld.tpl', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}
|
||||
@@ -0,0 +1 @@
|
||||
{extends 'extends:"helloworld.tpl\', var_dump(shell_exec(\'ls\')), 1, 2, 3);}}?>'}
|
||||
@@ -0,0 +1 @@
|
||||
{extends file='extends:"helloworld.tpl'|cat:"', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}
|
||||
@@ -82,6 +82,18 @@ class CompileIncludeTest extends PHPUnit_Smarty
|
||||
$this->assertEquals('I1I2I3', $content, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* test template name escaping
|
||||
*/
|
||||
public function testIncludeFilenameEscaping()
|
||||
{
|
||||
$this->expectException(\Smarty\Exception::class);
|
||||
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||
$tpl = $this->smarty->createTemplate('test_include_security.tpl');
|
||||
$content = $this->smarty->fetch($tpl);
|
||||
$this->assertEquals("hello world", $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* test standard output
|
||||
*
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{include file="helloworld.tpl', var_dump(shell_exec('ls')), 1, 2, 3);}}?>"}
|
||||
@@ -32,4 +32,11 @@ class ExtendsIssue419Test extends PHPUnit_Smarty
|
||||
$this->assertEquals('child', $this->smarty->fetch('extends:001_parent.tpl|001_child.tpl'));
|
||||
}
|
||||
|
||||
public function testextendsSecurity()
|
||||
{
|
||||
$this->expectException(\Smarty\Exception::class);
|
||||
$this->expectExceptionMessageMatches('/Unable to load.*/');
|
||||
$this->assertEquals('child', $this->smarty->fetch('string:{include "001_parent.tpl\', var_dump(shell_exec(\'ls\')), 1, 2, 3);}}?>"}'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user