Compare commits

..
Author SHA1 Message Date
Simon Wisselink b08a1d8332 add changelog 2026-04-13 21:35:41 +02:00
Simon Wisselink a511a011cb changed an incorrect doc and formatted some code. 2026-04-13 15:13:28 +02:00
Simon Wisselink 7cab1c0c24 remove useless resetting of static properties in tearDownAfterClass 2026-04-13 14:15:34 +02:00
b7dac0306f Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 14:08:08 +02:00
Simon Wisselink e73aaf19b5 fixed the unit tests 2026-04-13 13:58:09 +02:00
Simon Wisselink 06c49acee4 cleanup of unused template files, non-shared files stored in __shared folder, no longer required calls to add template folders et cetera 2026-04-13 13:17:20 +02:00
Simon Wisselink c22d109d17 Clean up each test class's unique temp dir in tearDownAfterClass()
Add a private static removeDir() helper and call it from
tearDownAfterClass() to recursively delete the per-class unique temp
directory after each test class finishes. Cleanup failures are silently
ignored (@ suppression) so they never cause test failures.

Set KEEP_SMARTY_TEST_ARTIFACTS=1 in the environment to skip cleanup and
keep the artifacts on disk for debugging.
2026-04-13 11:10:26 +02:00
Simon Wisselink deb0b22976 Remove individualFolders dead code and spurious assertTrue from cleanDirs()
- Remove the never-active individualFolders code path from setUpSmarty()
  (the constant was always true, making the branch unreachable)
- Remove define('individualFolders') from Config.php and the constructor
- Remove $this->assertTrue(true) from cleanDirs(): it existed solely to
  make testInit() count as a passing test; now that cleanDirs() is called
  from setUpSmarty() and from test methods directly, the assertion was
  spuriously inflating assertion counts
- Add tests/**/templates_c/, cache/, templates_tmp/ to .gitignore to
  prevent stale test output from appearing as untracked files
2026-04-13 00:40:28 +02:00
Simon Wisselink cc3e9c2a20 Isolate each test class in a unique temp directory
getTempDir() now appends a per-class uniqid token to the temp path, so
concurrent or sequential test runs never share compiled/cached output.
The token is generated lazily on first use and reset in
tearDownAfterClass(), giving every test class a fresh isolated directory.

As a result, the Bootstrap.php pre-run cleanup of smarty-tests/ is no
longer needed for correctness (stale paths are unreachable) and was
harmful to concurrent runs, so it has been removed.
2026-04-12 23:53:51 +02:00
Simon Wisselink 760f4834b3 Redirect test temp dirs to system temp directory. Fixes #1178
Move all test-generated output (compiled templates, cache files, and
temporary template sources) from per-test-directory folders inside the
working tree to a parallel structure under sys_get_temp_dir()/smarty-tests/.

This removes 215 boilerplate .gitignore files from the repo and ensures
running the test suite leaves zero uncommitted files in the working tree.

All 2296 tests continue to pass with identical behavior.
2026-04-11 00:05:42 +02:00
18 changed files with 90 additions and 104 deletions
-5
View File
@@ -1,13 +1,9 @@
# AGENTS.md
This file is the single source of truth for AI coding assistants working in this repo (including Claude Code, claude.ai/code).
## Project
Smarty v5 — PHP template engine. Single Composer package (`smarty/smarty`), namespace `Smarty\`, source in `src/`, autoloaded via PSR-4. Supports PHP 7.28.5.
Do not use PHP syntax newer than 7.2 in `src/` unless it is guarded for older runtimes.
## Commands
```bash
@@ -63,7 +59,6 @@ After editing a `.plex` or `.y` file, run `make -B` to regenerate. The generator
- All tests extend `PHPUnit_Smarty` (defined in `tests/PHPUnit_Smarty.php`), which provides `setUpSmarty($dir)`.
- Test suite root: `tests/UnitTests/`. Typical test `setUp()` calls `$this->setUpSmarty(__DIR__)`.
- Each test directory may have its own `templates/`, `configs/` subdirectories. Compiled output goes to `templates_c/` and `cache/` (auto-created by the test harness).
- Running the suite scatters generated `templates_c/`, `cache/`, and `templates_tmp/` directories (and empty runtime `templates/`/`configs/` dirs) throughout `tests/` and the repo root. These are not tracked — treat them as noise in `git status`, never commit them, and clean them with `git clean -fd` (exclude tool dirs like `.serena`).
- Three test files are excluded in `phpunit.xml`: Memcache, APC, and HttpModifiedSince tests (require external services).
- Tests needing MySQL/PDO are gated by constants in `tests/Config.php` (disabled by default).
-6
View File
@@ -6,12 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [5.8.1] - 2026-06-23
- Re-activated unit tests for user literals, which were previously disabled due to a bug in refactoring to v5.
- fixed a bug where child template's block content leaked into subsequent rendering of the parent template [#1189](https://github.com/smarty-php/smarty/issues/1189)
- Moved all unit test-generated output from inside the working tree to tmp files [#1178](https://github.com/smarty-php/smarty/issues/1178)
## [5.8.0] - 2026-02-15
- Added support for Backed Enums for php versions >= 8.1 [#1171](https://github.com/smarty-php/smarty/pull/1171)
- Added support for new 'matches' operator doing regex matching [#1169](https://github.com/smarty-php/smarty/pull/1169)
+5 -4
View File
@@ -11,10 +11,10 @@
## include inline
- Re-introduce merge_compiled_includes and the {include inline} attribute?
## Output buffering (major)
- Fix ob_ output buffering commands being scattered around the codebase: Smarty's output model is fundamentally "echo everything, wrap in a buffer to capture". An alternative that would be where rendering returns a string rather than echoing — but that touches the entire compiled template format (the unifunc functions all echo) and is a large change.
## Output buffering
- Fix ob_ output buffering commands being scattered around the codebase
## Review public static vars (major)
## Review public static vars
- such as _CHARSET and _IS_WINDOWS
## Block / inheritance
@@ -24,8 +24,9 @@
## Plugin system
- fix template security checks in one place in compiler
## Beatify output (major)
## Beatify output
- compiled templates could be proper classes, possibly using [nette/php-generator](https://packagist.org/packages/nette/php-generator)
## Unrelated / other
- review (and avoid) use of 'clone' keyword
- what is 'user literal support', why are unit tests skipped?
+1
View File
@@ -0,0 +1 @@
- Moved all unit test-generated output from inside the working tree to tmp files [#1178](https://github.com/smarty-php/smarty/issues/1178)
+1
View File
@@ -20,6 +20,7 @@
<testsuite name="foo">
<directory>./tests/UnitTests/</directory>
<exclude>./tests/UnitTests/CacheResourceTests/Memcache/CacheResourceCustomMemcacheTest.php</exclude>
<exclude>./tests/UnitTests/CacheResourceTests/Apc/CacheResourceCustomApcTest.php</exclude>
<exclude>./tests/UnitTests/CacheModify/ModifiedSince/HttpModifiedSinceTest.php</exclude>
</testsuite>
</testsuites>
+1 -1
View File
@@ -143,7 +143,7 @@ class File extends Base
*
* @param Template $_template template object
*
* @return string|false content
* @return string content
*/
public function retrieveCachedContent(Template $_template)
{
+2 -6
View File
@@ -69,12 +69,8 @@ class InheritanceRuntime {
* @param array $blockNames outer level block name
*/
public function init(Template $tpl, $initChild, $blockNames = []) {
// if called while executing parent template it must be a sub-template with new inheritance root.
// A new root is started either by a child template ($initChild) or by a sub-template included
// outside of any block rendering (empty source stack); the latter must not inherit the leftover
// block overrides of a previously completed inheritance tree (see issue #1189).
if (($initChild || empty($this->sourceStack)) && $this->state === 3
&& (strpos($tpl->template_resource, 'extendsall') === false)) {
// if called while executing parent template it must be a sub-template with new inheritance root
if ($initChild && $this->state === 3 && (strpos($tpl->template_resource, 'extendsall') === false)) {
$tpl->setInheritance(clone $tpl->getSmarty()->getRuntime('Inheritance'));
$tpl->getInheritance()->init($tpl, $initChild, $blockNames);
return;
+1 -1
View File
@@ -54,7 +54,7 @@ class Smarty extends \Smarty\TemplateBase {
/**
* smarty version
*/
const SMARTY_VERSION = '5.8.1';
const SMARTY_VERSION = '5.8.0';
/**
* define caching modes
@@ -17,7 +17,11 @@ class UserliteralTest extends PHPUnit_Smarty
{
public function setUp(): void
{
$this->setUpSmarty(__DIR__);
if (!property_exists('Smarty', 'literals')) {
$this->markTestSkipped('user literal support');
} else {
$this->setUpSmarty(__DIR__);
}
}
@@ -65,6 +65,15 @@ class EvalResourceTest extends PHPUnit_Smarty
$this->assertEquals('', $this->smarty->fetch($tpl));
}
/**
* test usesCompiler
*/
public function testUsesCompiler()
{
$tpl = $this->smarty->createTemplate('eval:hello world');
$this->markTestIncomplete();
}
/**
* test isEvaluated
*/
@@ -86,6 +86,12 @@ class FileResourceTest extends PHPUnit_Smarty
$this->assertEquals('hello world', $tpl->getSource()->getContent());
}
public function testUsesCompiler()
{
$tpl = $this->smarty->createTemplate('helloworld.tpl');
$this->markTestIncomplete();
}
public function testIsEvaluated()
{
$tpl = $this->smarty->createTemplate('helloworld.tpl');
@@ -60,6 +60,15 @@ class StreamResourceTest extends PHPUnit_Smarty
$this->assertEquals('hello world {$foo}', $tpl->getSource()->getContent());
}
/**
* test usesCompiler
*/
public function testUsesCompiler()
{
$tpl = $this->smarty->createTemplate('global:mytest');
$this->markTestIncomplete();
}
/**
* test isEvaluated
*/
@@ -73,6 +73,15 @@ class StringResourceTest extends PHPUnit_Smarty
$this->assertEquals('hello world{$foo}', $tpl->getSource()->getContent());
}
/**
* test usesCompiler
*/
public function testUsesCompiler()
{
$tpl = $this->smarty->createTemplate('string:hello world');
$this->markTestIncomplete();
}
/**
* test isEvaluated
*/
@@ -1,52 +1,50 @@
<?php
// first class callables where introduced in PHP 8.1
if (PHP_VERSION_ID >= 80100) {
/**
* class for register modifier with (first class) callables tests
*
* @runTestsInSeparateProcess
* @preserveGlobalState disabled
* @backupStaticAttributes enabled
*/
class RegisterModifierFirstClassCallablesTest extends PHPUnit_Smarty
{
public function setUp(): void
/**
* class for register modifier with (first class) callables tests
*
* @runTestsInSeparateProcess
* @preserveGlobalState disabled
* @backupStaticAttributes enabled
*/
class RegisterModifierFirstClassCallablesTest extends PHPUnit_Smarty
{
// First-class callable syntax (Closure::fromCallable shorthand) requires PHP 8.1+
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('First-class callables require PHP >= 8.1');
public function setUp(): void
{
$this->setUpSmarty(__DIR__);
}
$this->setUpSmarty(__DIR__);
public function testRegisterFirstClassCallable()
{
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'testmodifier', eval('return strrev(...);'));
$this->assertEquals('mosredna', $this->smarty->fetch('string:{"andersom"|testmodifier}'));
}
public function testRegisterFirstClassCallableSameName()
{
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'mymodifier', eval('return strrev(...);'));
$this->assertEquals('mosredna', $this->smarty->fetch('string:{"andersom"|mymodifier}'));
}
public function testRegisterFirstClassCallableAsFunc()
{
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'kprint_r_out', eval('return strrev(...);'));
$this->smarty->assign('myVar', 'andersom');
$this->assertEquals('mosredna', $this->smarty->fetch('string:{kprint_r_out($myVar)}'));
}
public function testRegisterFirstClassCallableSameNameAsPhpFunc()
{
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'mymodifierfcc', eval('return strrev(...);'));
$this->assertEquals('mosredna', $this->smarty->fetch('string:{mymodifierfcc("andersom")}'));
}
}
public function testRegisterFirstClassCallable()
{
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'testmodifier', eval('return strrev(...);'));
$this->assertEquals('mosredna', $this->smarty->fetch('string:{"andersom"|testmodifier}'));
}
public function testRegisterFirstClassCallableSameName()
{
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'mymodifier', eval('return strrev(...);'));
$this->assertEquals('mosredna', $this->smarty->fetch('string:{"andersom"|mymodifier}'));
}
public function testRegisterFirstClassCallableAsFunc()
{
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'kprint_r_out', eval('return strrev(...);'));
$this->smarty->assign('myVar', 'andersom');
$this->assertEquals('mosredna', $this->smarty->fetch('string:{kprint_r_out($myVar)}'));
}
public function testRegisterFirstClassCallableSameNameAsPhpFunc()
{
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'mymodifierfcc', eval('return strrev(...);'));
$this->assertEquals('mosredna', $this->smarty->fetch('string:{mymodifierfcc("andersom")}'));
}
}
function mymodifierfcc($a, $b, $c)
{
return "$a function $b $c";
@@ -1,33 +0,0 @@
<?php
/**
* Smarty PHPunit test reproducing issue #1189.
*
* When a parent template is {include}d, then a child template that {extends}
* the parent overrides a {block}, a subsequent {include} of the parent in the
* same render must still show the parent's block content.
*
* The block override from the extending child must not leak into the later
* include of the parent template.
*
* @see https://github.com/smarty-php/smarty/issues/1189
*
* @preserveGlobalState disabled
*/
class IncludeExtendsBlockLeakIssue1189Test extends PHPUnit_Smarty
{
public function setUp(): void
{
$this->setUpSmarty(__DIR__);
}
/**
* Sequence: include parent -> include child(extends parent) -> include parent.
* Expected: PARENT CHILD PARENT
* Bug (#1189): PARENT CHILD CHILD
*/
public function testBlockOverrideDoesNotLeakIntoLaterParentInclude()
{
$result = $this->smarty->fetch('top.tpl');
$this->assertSame('PARENT CHILD PARENT', preg_replace('/\s+/', ' ', trim($result)));
}
}
@@ -1,2 +0,0 @@
{extends file="parent.tpl"}
{block name=message}CHILD{/block}
@@ -1 +0,0 @@
{block name=message}PARENT{/block}
@@ -1 +0,0 @@
{include file="parent.tpl"} {include file="child.tpl"} {include file="parent.tpl"}