mirror of
https://github.com/smarty-php/smarty.git
synced 2026-08-03 20:14:31 +02:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f9eef51c7 | |||
| 94a27cbbc7 | |||
| badc5ef3a0 | |||
| 2ae0f9a65f | |||
| b668745acf | |||
| 3c9f77a2e0 | |||
| 042dff64a9 | |||
| 1830aa7a12 | |||
| b83ffdd2d6 | |||
| ac27e1e5b6 | |||
| 17fae11a38 | |||
| 06594a1149 | |||
| 75a738ad44 | |||
| 11e69eca68 | |||
| 31e06fc087 | |||
| 99c048ce7a | |||
| 1c9b2ce1d3 | |||
| 29a77ded2f | |||
| 6d06c5e61f | |||
| ba745f5b91 | |||
| 0775fa84d1 | |||
| 56ab75df2c | |||
| 383e20e6b4 | |||
| c139883770 | |||
| 6e648ed809 | |||
| 3577fc7091 | |||
| ff2ef3b0cb | |||
| aa2dcd82cf | |||
| 78d259d3b9 | |||
| 4ed569c674 | |||
| a46478d0cb | |||
| 5487e31c4b | |||
| 12ce28e265 |
@@ -7,3 +7,5 @@ phpunit*
|
||||
.phpunit.result.cache
|
||||
vendor/*
|
||||
composer.lock
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# 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.2–8.5.
|
||||
|
||||
Do not use PHP syntax newer than 7.2 in `src/` unless it is guarded for older runtimes.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
composer install
|
||||
|
||||
# Regenerate lexers and parsers (required before tests; CI runs this)
|
||||
make -B
|
||||
|
||||
# Run all tests
|
||||
php ./vendor/phpunit/phpunit/phpunit
|
||||
|
||||
# Run a single test file
|
||||
php ./vendor/phpunit/phpunit/phpunit tests/UnitTests/TemplateSource/TagTests/If/CompileIfTest.php
|
||||
|
||||
# Run tests matching a PHPUnit group
|
||||
php ./vendor/phpunit/phpunit/phpunit --group 20221124
|
||||
|
||||
# Run tests excluding slow group
|
||||
php ./vendor/phpunit/phpunit/phpunit --exclude-group slow
|
||||
```
|
||||
|
||||
There is no linter or static analysis configured. No typecheck step.
|
||||
|
||||
## Generated code — do not hand-edit
|
||||
|
||||
Four files are generated from grammar/lexer definitions via `make`:
|
||||
|
||||
| Source (edit this) | Generated (do not edit) |
|
||||
|---|---|
|
||||
| `src/Lexer/TemplateLexer.plex` | `src/Lexer/TemplateLexer.php` |
|
||||
| `src/Lexer/ConfigfileLexer.plex` | `src/Lexer/ConfigfileLexer.php` |
|
||||
| `src/Parser/TemplateParser.y` | `src/Parser/TemplateParser.php` |
|
||||
| `src/Parser/ConfigfileParser.y` | `src/Parser/ConfigfileParser.php` |
|
||||
|
||||
After editing a `.plex` or `.y` file, run `make -B` to regenerate. The generators require the `smarty/smarty-lexer` dev dependency.
|
||||
|
||||
## Architecture
|
||||
|
||||
- `src/Smarty.php` — main class, extends `TemplateBase`. Version constant: `Smarty::SMARTY_VERSION`.
|
||||
- `src/Compile/`, `src/Compiler/` — template compilation pipeline.
|
||||
- `src/Lexer/`, `src/Parser/` — lexer/parser (generated, see above).
|
||||
- `src/Extension/` — extension system (`ExtensionInterface`, `CoreExtension`, `DefaultExtension`, `BCPluginsAdapter`).
|
||||
- `src/Runtime/` — runtime helpers (foreach, capture, inheritance, tpl functions).
|
||||
- `src/Resource/`, `src/Cacheresource/` — template resource and cache resource handlers.
|
||||
- `src/BlockHandler/`, `src/FunctionHandler/`, `src/Filter/` — built-in tags, functions, and filters.
|
||||
- `libs/Smarty.class.php` — non-Composer autoload stub. Points to `src/`. Not the main source.
|
||||
- `src/functions.php` — global helper functions, always loaded via Composer `files` autoload.
|
||||
|
||||
## Tests
|
||||
|
||||
- Framework: PHPUnit 7.5/8.5 (bootstrap: `tests/Bootstrap.php`).
|
||||
- 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).
|
||||
|
||||
## CI
|
||||
|
||||
GitHub Actions (`.github/workflows/ci.yml`): matrix of PHP 7.2–8.5 on ubuntu + windows. Steps: `composer install` → `make -B` → `phpunit`. No deploy step.
|
||||
|
||||
## Docs
|
||||
|
||||
Markdown in `docs/`, built with mkdocs + Material theme. Install the toolchain with `pip install -r docs/requirements.txt`, then preview with `mkdocs serve`. Published via `mike deploy 5.x`.
|
||||
|
||||
## Release
|
||||
|
||||
`./make-release.sh <version>` — only v5.x.x. Updates changelog and version constant, creates a merge commit and tag on `master`.
|
||||
|
||||
### Changelog
|
||||
|
||||
Every change that should appear in `CHANGELOG.md` must add a new markdown file under `changelog/`. At release time `utilities/update-changelog.php` concatenates all `changelog/*.md` files into the `## [Unreleased]` section, so:
|
||||
|
||||
- One file per change, containing a single line that starts with a dash (`- ...`). The filename is arbitrary; name it after the issue number (e.g. `1036.md`) when there is one, otherwise use a short descriptive slug.
|
||||
- Include a markdown link to the relevant issue when one exists, e.g. `[#1036](https://github.com/smarty-php/smarty/issues/1036)`. Omit the link when there is no public issue (e.g. embargoed security reports).
|
||||
@@ -6,6 +6,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [5.8.4] - 2026-06-29
|
||||
- Fixed a `TypeError` on PHP 8 when `Security::$static_classes` was set to a non-array value (e.g. the string `'none'`) to disable static class access; any non-array value now cleanly denies access. Use `Security::$static_classes = null` to disable access to all static classes.
|
||||
- Security: the built-in `stream:` resource type now validates the nested stream wrapper against the security policy, so a template such as `stream:php://filter/...` can no longer bypass `Security::$streams` (including `Security::$streams = null`) to read local files (CWE-22)
|
||||
|
||||
|
||||
## [5.8.3] - 2026-06-28
|
||||
- fixed a regression from #1189 where a child template's block override no longer applied to a template {include}d by the parent [#1192](https://github.com/smarty-php/smarty/issues/1192)
|
||||
|
||||
|
||||
## [5.8.2] - 2026-06-24
|
||||
- Security: prevent symlinks inside a trusted `secure_dir`/template directory from being used to read files outside of it (CWE-22 path traversal), affecting `{include}` and `{fetch}` of local files
|
||||
- Security: `{html_image}` now escapes the `file`, `path_prefix`, `href`/`link`, `width` and `height` attributes (it already escaped `alt` and pass-through attributes), and `{html_select_date}` casts `day_size`/`month_size`/`year_size` to int (matching `{html_select_time}`), preventing untrusted values passed into these attributes from breaking out of the generated HTML (CWE-79)
|
||||
- Security: `{fetch}` no longer follows HTTP redirects for remote resources while a security policy is active, preventing an open redirect on a trusted host from bypassing `trusted_uri` (CWE-918 server-side request forgery)
|
||||
- Fixed "Attempt to assign property step on null" error when using a {for} loop inside a block of an extended template [#1036](https://github.com/smarty-php/smarty/issues/1036)
|
||||
|
||||
|
||||
## [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)
|
||||
- Update documentation to clarify that include inline is currently not implemented in Smarty v5 [#1152](https://github.com/smarty-php/smarty/issues/1152)
|
||||
- Support for Laravel Collections style object chaining for objects return from function calls implemented as modifiers [#1151](https://github.com/smarty-php/smarty/issues/1151)
|
||||
|
||||
|
||||
## [5.7.0] - 2025-11-19
|
||||
- PHP 8.5 support
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/smarty
|
||||
The [docs](docs/index.md) are written in markdown, configured in [mkdocs.yml](mkdocs.yml) and published
|
||||
to [GitHub pages](https://smarty-php.github.io/smarty) using [mkdocs](https://www.mkdocs.org/) and [mike](https://github.com/jimporter/mike).
|
||||
|
||||
You need Python to build the docs. Install the required packages first:
|
||||
```bash
|
||||
pip install -r docs/requirements.txt
|
||||
```
|
||||
|
||||
To preview the docs while you are writing, run:
|
||||
```bash
|
||||
mkdocs serve
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
## include inline
|
||||
- Re-introduce merge_compiled_includes and the {include inline} attribute?
|
||||
|
||||
## Output buffering
|
||||
- Fix ob_ output buffering commands being scattered around the codebase
|
||||
## 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.
|
||||
|
||||
## Review public static vars
|
||||
## Review public static vars (major)
|
||||
- such as _CHARSET and _IS_WINDOWS
|
||||
|
||||
## Block / inheritance
|
||||
@@ -24,9 +24,8 @@
|
||||
## Plugin system
|
||||
- fix template security checks in one place in compiler
|
||||
|
||||
## Beatify output
|
||||
## Beatify output (major)
|
||||
- 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 +0,0 @@
|
||||
- Support for Laravel Collections style object chaining for objects return from function calls implemented as modifiers [#1151](https://github.com/smarty-php/smarty/issues/1151)
|
||||
@@ -1 +0,0 @@
|
||||
- Update documentation to clarify that include inline is currently not implemented in Smarty v5 [#1152](https://github.com/smarty-php/smarty/issues/1152)
|
||||
@@ -1,4 +1,3 @@
|
||||
version: "2"
|
||||
services:
|
||||
base:
|
||||
build:
|
||||
|
||||
@@ -50,6 +50,120 @@ separated from surrounding elements by spaces. Note that items listed in
|
||||
| is \[not\] odd by | | $a is not odd by $b | \[not\] an odd grouping | ($a / $b) % 2 != 0 |
|
||||
| is in | | $a is in $b | exists in array | in_array($a, $b) |
|
||||
| is \[not\] in | | $a is not in $b | does not exist in array | !in_array($a, $b) |
|
||||
| matches | | $a matches $b | regex pattern match | preg_match($b, $a) |
|
||||
|
||||
## Regex Matching Operator
|
||||
|
||||
The `matches` operator allows you to test if a string matches a regular expression pattern.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```smarty
|
||||
{if "hello" matches "/^[a-z]+$/"}
|
||||
String matches the pattern!
|
||||
{/if}
|
||||
|
||||
{if $email matches "/^[^@]+@[^@]+\.[^@]+$/"}
|
||||
Valid email format
|
||||
{else}
|
||||
Invalid email format
|
||||
{/if}
|
||||
```
|
||||
|
||||
### Using Variables
|
||||
|
||||
```smarty
|
||||
{$pattern = '/^[a-zA-Z0-9]{8,}$/'}
|
||||
{if $password matches $pattern}
|
||||
Password meets requirements
|
||||
{else}
|
||||
Password must be at least 8 alphanumeric characters
|
||||
{/if}
|
||||
```
|
||||
|
||||
### Pattern Modifiers
|
||||
|
||||
The `matches` operator supports all standard PHP regex modifiers:
|
||||
|
||||
```smarty
|
||||
{* Case insensitive matching *}
|
||||
{if "HELLO" matches "/hello/i"}
|
||||
Matches (case insensitive)
|
||||
{/if}
|
||||
|
||||
{* Multiline mode *}
|
||||
{if "line1\nline2" matches "/line2$/m"}
|
||||
Matches in multiline mode
|
||||
{/if}
|
||||
|
||||
{* Dot matches newlines *}
|
||||
{if "hello\nworld" matches "/hello.world/s"}
|
||||
Matches with dotall modifier
|
||||
{/if}
|
||||
```
|
||||
|
||||
### Complex Conditions
|
||||
|
||||
The `matches` operator can be combined with other operators:
|
||||
|
||||
```smarty
|
||||
{if $username matches "/^[a-z]+$/" && $username|length > 3}
|
||||
Valid username
|
||||
{else}
|
||||
Username must be lowercase letters and at least 4 characters
|
||||
{/if}
|
||||
|
||||
{if $input matches "/^[0-9]+$/" || $input matches "/^[a-z]+$/"}
|
||||
Input is either numeric or lowercase letters
|
||||
{else}
|
||||
Invalid input format
|
||||
{/if}
|
||||
```
|
||||
|
||||
### Practical Examples
|
||||
|
||||
**Email Validation:**
|
||||
```smarty
|
||||
{if $email matches "/^[^@\s]+@[^@\s]+\.[^@\s]+$/"}
|
||||
Valid email address
|
||||
{else}
|
||||
Please enter a valid email address
|
||||
{/if}
|
||||
```
|
||||
|
||||
**Password Strength:**
|
||||
```smarty
|
||||
{if $password matches "/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/"}
|
||||
Strong password
|
||||
{else}
|
||||
Password must contain uppercase, lowercase, numbers and be at least 8 characters
|
||||
{/if}
|
||||
```
|
||||
|
||||
**URL Validation:**
|
||||
```smarty
|
||||
{if $url matches "/^https?:\/\/(www\.)?[a-z0-9\-]+(\.[a-z]{2,})+/i"}
|
||||
Valid URL format
|
||||
{else}
|
||||
Please enter a valid URL
|
||||
{/if}
|
||||
```
|
||||
|
||||
**Numeric Validation:**
|
||||
```smarty
|
||||
{if $input matches "/^[0-9]+$/"}
|
||||
Valid numeric input
|
||||
{else}
|
||||
Please enter numbers only
|
||||
{/if}
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- The `matches` operator uses PHP's `preg_match()` function internally
|
||||
- Pattern delimiters must be valid regex delimiters (typically `/`)
|
||||
- Invalid patterns will cause PHP warnings but won't break template execution
|
||||
- For complex regex patterns, consider using variables for better readability
|
||||
|
||||
## Ternary
|
||||
You can use the `?:` (or ternary) operator to test one expression and present the value
|
||||
|
||||
@@ -27,6 +27,13 @@ which item(s) are selected by default as well.
|
||||
- All parameters that are not in the list above are printed as
|
||||
name/value-pairs inside each of the created <input\>-tags.
|
||||
|
||||
> **Security note**
|
||||
>
|
||||
> The `separator` attribute is written into the generated HTML without escaping,
|
||||
> so it can contain markup such as `separator='<br />'`. If its value originates
|
||||
> from untrusted input, escape it yourself first to avoid cross-site scripting
|
||||
> (XSS). Option values and labels are escaped automatically.
|
||||
|
||||
## Examples
|
||||
```php
|
||||
<?php
|
||||
|
||||
@@ -28,6 +28,13 @@ selected by default as well.
|
||||
- All parameters that are not in the list above are output as
|
||||
name/value-pairs inside each of the created `<input>`-tags.
|
||||
|
||||
> **Security note**
|
||||
>
|
||||
> The `separator` attribute is written into the generated HTML without escaping,
|
||||
> so it can contain markup such as `separator='<br />'`. If its value originates
|
||||
> from untrusted input, escape it yourself first to avoid cross-site scripting
|
||||
> (XSS). Option values and labels are escaped automatically.
|
||||
|
||||
## Examples
|
||||
|
||||
```php
|
||||
|
||||
@@ -47,6 +47,14 @@ name/value-pairs inside the `<select>` tags of day, month and year.
|
||||
> There is an useful php function on the [date tips page](../../appendixes/tips.md)
|
||||
> for converting `{html_select_date}` form values to a timestamp.
|
||||
|
||||
> **Security note**
|
||||
>
|
||||
> The `*_extra` attributes, `field_separator`/`option_separator`, and any
|
||||
> unrecognised parameter (which is emitted as a raw attribute on the `<select>`
|
||||
> tag) are written into the generated HTML without escaping. If any of these
|
||||
> values originate from untrusted input, escape them yourself first to avoid
|
||||
> cross-site scripting (XSS).
|
||||
|
||||
## Exaples
|
||||
|
||||
Template code
|
||||
|
||||
@@ -47,6 +47,14 @@ parseable by PHP's [`strtotime()`](https://www.php.net/strtotime).
|
||||
| meridian\_empty | null | If supplied then the first element of the meridian's select-box has this value as it's label and "" as it's value. This is useful to make the select-box read "Please select an meridian" for example. |
|
||||
|
||||
|
||||
> **Security note**
|
||||
>
|
||||
> The `*_extra` attributes, `field_separator`/`option_separator`, and any
|
||||
> unrecognised parameter (which is emitted as a raw attribute on the `<select>`
|
||||
> tag) are written into the generated HTML without escaping. If any of these
|
||||
> values originate from untrusted input, escape them yourself first to avoid
|
||||
> cross-site scripting (XSS).
|
||||
|
||||
## Examples
|
||||
|
||||
```smarty
|
||||
|
||||
@@ -31,6 +31,15 @@ dumps an array of data into an HTML `<table>`.
|
||||
- `trailpad` is the value put into the trailing cells on the last
|
||||
table row if there are any present.
|
||||
|
||||
> **Security note**
|
||||
>
|
||||
> The `loop`/`cols` data and the `caption`, `trailpad`, `table_attr`, `tr_attr`,
|
||||
> `td_attr` and `th_attr` attributes are written into the generated HTML without
|
||||
> escaping (this is by design — e.g. `table_attr='border="1"'`). If any of these
|
||||
> values originate from untrusted input, escape them yourself first (e.g. with the
|
||||
> [`escape`](../language-modifiers/language-modifier-escape.md) modifier) to avoid
|
||||
> cross-site scripting (XSS).
|
||||
|
||||
## Examples
|
||||
|
||||
```php
|
||||
|
||||
@@ -24,6 +24,14 @@ spiders to lift email addresses off of a site.
|
||||
> you can use hex encoding too.
|
||||
|
||||
|
||||
> **Security note**
|
||||
>
|
||||
> The `extra` attribute is written into the generated `<a>` tag without escaping,
|
||||
> so that you can add attributes such as `extra='class="mailto"'`. If you pass a
|
||||
> value that originates from untrusted input, escape it yourself first (e.g. with
|
||||
> the [`escape`](../language-modifiers/language-modifier-escape.md) modifier) to
|
||||
> avoid cross-site scripting (XSS).
|
||||
|
||||
## Examples
|
||||
|
||||
```smarty
|
||||
|
||||
@@ -123,4 +123,89 @@ this will output:
|
||||
```html
|
||||
name: Zaphod Beeblebrox<br />
|
||||
email: zaphod@slartibartfast.example.com<br />
|
||||
```
|
||||
```
|
||||
|
||||
## Backed Enums (PHP 8.1+)
|
||||
|
||||
Smarty supports accessing properties of [backed enums](https://www.php.net/manual/en/language.enumerations.backed.php) introduced in PHP 8.1.
|
||||
|
||||
### Accessing Enum Properties
|
||||
|
||||
You can access the `name` and `value` properties of backed enum cases:
|
||||
|
||||
```smarty
|
||||
{* Access enum case properties *}
|
||||
<option id="{MyEnum::Foo->name}">{MyEnum::Foo->value}</option>
|
||||
```
|
||||
|
||||
### Complete Example
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Smarty\Smarty;
|
||||
|
||||
// Define a backed enum
|
||||
enum Status: string {
|
||||
case Active = 'active';
|
||||
case Inactive = 'inactive';
|
||||
case Pending = 'pending';
|
||||
}
|
||||
|
||||
$smarty = new Smarty();
|
||||
$smarty->assign('currentStatus', Status::Active);
|
||||
$smarty->display('template.tpl');
|
||||
```
|
||||
|
||||
`template.tpl`:
|
||||
|
||||
```smarty
|
||||
{* Display enum properties *}
|
||||
Current status: {$currentStatus->name} (value: {$currentStatus->value})
|
||||
|
||||
{* Use in HTML attributes *}
|
||||
<select name="status">
|
||||
<option value="{Status::Active->value}" {if $currentStatus->name === 'Active'}selected{/if}>Active</option>
|
||||
<option value="{Status::Inactive->value}" {if $currentStatus->name === 'Inactive'}selected{/if}>Inactive</option>
|
||||
<option value="{Status::Pending->value}" {if $currentStatus->name === 'Pending'}selected{/if}>Pending</option>
|
||||
</select>
|
||||
```
|
||||
|
||||
This would output:
|
||||
|
||||
```html
|
||||
Current status: Active (value: active)
|
||||
|
||||
<select name="status">
|
||||
<option value="active" selected>Active</option>
|
||||
<option value="inactive">Inactive</option>
|
||||
<option value="pending">Pending</option>
|
||||
</select>
|
||||
```
|
||||
|
||||
### Integer-backed Enums
|
||||
|
||||
Integer-backed enums work the same way:
|
||||
|
||||
```php
|
||||
<?php
|
||||
enum Priority: int {
|
||||
case Low = 1;
|
||||
case Medium = 2;
|
||||
case High = 3;
|
||||
}
|
||||
|
||||
$smarty->assign('priority', Priority::High);
|
||||
```
|
||||
|
||||
```smarty
|
||||
{* Access integer enum properties *}
|
||||
Priority level: {$priority->value} ({$priority->name})
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```html
|
||||
Priority level: 3 (High)
|
||||
```
|
||||
|
||||
> **Note**: Backed enum support requires PHP 8.1 or higher. The enum must be registered or available in the current namespace.
|
||||
@@ -22,6 +22,8 @@ Some of Smarty's features:
|
||||
- [Template Inheritance](api/inheritance.md) for
|
||||
easy management of template content.
|
||||
- [Plugin](api/extending/introduction.md) architecture
|
||||
- Regex pattern matching with the [`matches`](designers/language-basic-syntax/language-syntax-operators.md#regex-matching-operator) operator
|
||||
- Support for PHP 8.1+ [backed enums](designers/language-variables/language-assigned-variables.md#backed-enums-php-81)
|
||||
|
||||
## Separation of presentation from application code
|
||||
- This means templates can certainly contain logic under the condition
|
||||
|
||||
@@ -8,6 +8,25 @@ It allows you to write **templates**, using **variables**, **modifiers**, **func
|
||||
<p>
|
||||
The number of pixels is: {math equation="x * y" x=$height y=$width}.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{if $email matches "/^[^@]+@[^@]+\.[^@]+$/"}
|
||||
Valid email address
|
||||
{else}
|
||||
Please enter a valid email
|
||||
{/if}
|
||||
</p>
|
||||
```
|
||||
```html
|
||||
<h1>Hello world</h1>
|
||||
|
||||
<p>
|
||||
The number of pixels is: 307200.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Valid email address
|
||||
</p>
|
||||
```
|
||||
|
||||
When this template is rendered, with the value "Hello world" for the variable $title, 640 for $width,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Python dependencies for building/previewing the docs.
|
||||
#
|
||||
# pip install -r docs/requirements.txt
|
||||
# mkdocs serve # local preview
|
||||
# mike deploy 5.x # publish
|
||||
#
|
||||
# pymdown-extensions must be >=11: earlier releases pass filename=None to
|
||||
# Pygments, and Pygments >=2.19 then crashes with
|
||||
# "'NoneType' object has no attribute 'replace'" on any untitled code block.
|
||||
mkdocs>=1.6
|
||||
mkdocs-material>=9.7
|
||||
pymdown-extensions>=11
|
||||
Pygments>=2.19
|
||||
mike>=2.2
|
||||
@@ -20,7 +20,6 @@
|
||||
<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>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# - ./run-tests-for-all-php-versions.sh --group 20221124
|
||||
# - ./run-tests-for-all-php-versions.sh --exclude-group slow
|
||||
|
||||
COMPOSE_CMD="mutagen-compose"
|
||||
COMPOSE_CMD="docker compose"
|
||||
|
||||
$COMPOSE_CMD run --rm php72 ./run-tests.sh $@ && \
|
||||
$COMPOSE_CMD run --rm php73 ./run-tests.sh $@ && \
|
||||
|
||||
@@ -143,7 +143,7 @@ class File extends Base
|
||||
*
|
||||
* @param Template $_template template object
|
||||
*
|
||||
* @return string content
|
||||
* @return string|false content
|
||||
*/
|
||||
public function retrieveCachedContent(Template $_template)
|
||||
{
|
||||
|
||||
+15
-12
@@ -51,8 +51,9 @@ class ForTag extends Base {
|
||||
$var = $_statement['var'];
|
||||
$index = '';
|
||||
}
|
||||
$output .= "\$_smarty_tpl->assign($var, null);\n";
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->value{$index} = {$_statement['value']};\n";
|
||||
$itemVar = "\$_smarty_tpl->getVariable({$var})";
|
||||
$output .= "\$_smarty_tpl->assign($var, []);\n";
|
||||
$output .= "{$itemVar}->value{$index} = {$_statement['value']};\n";
|
||||
}
|
||||
if (is_array($_attr['var'])) {
|
||||
$var = $_attr['var']['var'];
|
||||
@@ -61,7 +62,8 @@ class ForTag extends Base {
|
||||
$var = $_attr['var'];
|
||||
$index = '';
|
||||
}
|
||||
$output .= "if ($_attr[ifexp]) {\nfor (\$_foo=true;$_attr[ifexp]; \$_smarty_tpl->tpl_vars[$var]->value{$index}$_attr[step]) {\n";
|
||||
$itemVar = "\$_smarty_tpl->getVariable({$var})";
|
||||
$output .= "if ($_attr[ifexp]) {\nfor (\$_foo=true;$_attr[ifexp]; {$itemVar}->value{$index}$_attr[step]) {\n";
|
||||
} else {
|
||||
$_statement = $_attr['start'];
|
||||
if (is_array($_statement['var'])) {
|
||||
@@ -71,21 +73,22 @@ class ForTag extends Base {
|
||||
$var = $_statement['var'];
|
||||
$index = '';
|
||||
}
|
||||
$output .= "\$_smarty_tpl->assign($var, null);";
|
||||
$itemVar = "\$_smarty_tpl->getVariable({$var})";
|
||||
$output .= "\$_smarty_tpl->assign($var, []);";
|
||||
if (isset($_attr['step'])) {
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->step = $_attr[step];";
|
||||
$output .= "{$itemVar}->step = $_attr[step];";
|
||||
} else {
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->step = 1;";
|
||||
$output .= "{$itemVar}->step = 1;";
|
||||
}
|
||||
if (isset($_attr['max'])) {
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->total = (int) min(ceil((\$_smarty_tpl->tpl_vars[$var]->step > 0 ? $_attr[to]+1 - ($_statement[value]) : $_statement[value]-($_attr[to])+1)/abs(\$_smarty_tpl->tpl_vars[$var]->step)),$_attr[max]);\n";
|
||||
$output .= "{$itemVar}->total = (int) min(ceil(({$itemVar}->step > 0 ? $_attr[to]+1 - ($_statement[value]) : $_statement[value]-($_attr[to])+1)/abs({$itemVar}->step)),$_attr[max]);\n";
|
||||
} else {
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->total = (int) ceil((\$_smarty_tpl->tpl_vars[$var]->step > 0 ? $_attr[to]+1 - ($_statement[value]) : $_statement[value]-($_attr[to])+1)/abs(\$_smarty_tpl->tpl_vars[$var]->step));\n";
|
||||
$output .= "{$itemVar}->total = (int) ceil(({$itemVar}->step > 0 ? $_attr[to]+1 - ($_statement[value]) : $_statement[value]-($_attr[to])+1)/abs({$itemVar}->step));\n";
|
||||
}
|
||||
$output .= "if (\$_smarty_tpl->tpl_vars[$var]->total > 0) {\n";
|
||||
$output .= "for (\$_smarty_tpl->tpl_vars[$var]->value{$index} = $_statement[value], \$_smarty_tpl->tpl_vars[$var]->iteration = 1;\$_smarty_tpl->tpl_vars[$var]->iteration <= \$_smarty_tpl->tpl_vars[$var]->total;\$_smarty_tpl->tpl_vars[$var]->value{$index} += \$_smarty_tpl->tpl_vars[$var]->step, \$_smarty_tpl->tpl_vars[$var]->iteration++) {\n";
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->first = \$_smarty_tpl->tpl_vars[$var]->iteration === 1;";
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->last = \$_smarty_tpl->tpl_vars[$var]->iteration === \$_smarty_tpl->tpl_vars[$var]->total;";
|
||||
$output .= "if ({$itemVar}->total > 0) {\n";
|
||||
$output .= "for ({$itemVar}->value{$index} = $_statement[value], {$itemVar}->iteration = 1;{$itemVar}->iteration <= {$itemVar}->total;{$itemVar}->value{$index} += {$itemVar}->step, {$itemVar}->iteration++) {\n";
|
||||
$output .= "{$itemVar}->first = {$itemVar}->iteration === 1;";
|
||||
$output .= "{$itemVar}->last = {$itemVar}->iteration === {$itemVar}->total;";
|
||||
}
|
||||
$output .= '?>';
|
||||
|
||||
|
||||
@@ -189,7 +189,22 @@ class Fetch extends Base {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$content = @file_get_contents($params['file']);
|
||||
if ($protocol && isset($template->getSmarty()->security_policy)) {
|
||||
// Remote resource (e.g. https://) reached through file_get_contents().
|
||||
// isTrustedUri() only validates the initial URL, but file_get_contents()
|
||||
// follows redirects by default, so an open redirect on an otherwise
|
||||
// trusted host could be used to reach a non-trusted target (SSRF).
|
||||
// Disable redirect-following while a security policy is in effect.
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'follow_location' => 0,
|
||||
'max_redirects' => 1,
|
||||
],
|
||||
]);
|
||||
$content = @file_get_contents($params['file'], false, $context);
|
||||
} else {
|
||||
$content = @file_get_contents($params['file']);
|
||||
}
|
||||
if ($content === false) {
|
||||
throw new Exception("{fetch} cannot read resource '" . $params['file'] . "'");
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class HtmlImage extends Base {
|
||||
break;
|
||||
case 'link':
|
||||
case 'href':
|
||||
$prefix = '<a href="' . $_val . '">';
|
||||
$prefix = '<a href="' . smarty_function_escape_special_chars($_val) . '">';
|
||||
$suffix = '</a>';
|
||||
break;
|
||||
default:
|
||||
@@ -143,7 +143,12 @@ class HtmlImage extends Base {
|
||||
$width = round($width * $_resize);
|
||||
$height = round($height * $_resize);
|
||||
}
|
||||
return $prefix . '<img src="' . $path_prefix . $file . '" alt="' . $alt . '" width="' . $width . '" height="' .
|
||||
$height . '"' . $extra . ' />' . $suffix;
|
||||
// $alt and the pass-through attributes ($extra) are already escaped above;
|
||||
// escape the remaining value-context params at output time so untrusted
|
||||
// values cannot break out of the attribute (CWE-79). The unescaped $file/
|
||||
// $width/$height are still used for getimagesize()/DPI math above.
|
||||
return $prefix . '<img src="' . smarty_function_escape_special_chars($path_prefix . $file) . '" alt="' . $alt
|
||||
. '" width="' . smarty_function_escape_special_chars($width) . '" height="'
|
||||
. smarty_function_escape_special_chars($height) . '"' . $extra . ' />' . $suffix;
|
||||
}
|
||||
}
|
||||
@@ -120,9 +120,6 @@ class HtmlSelectDate extends Base {
|
||||
case 'day_value_format':
|
||||
case 'month_format':
|
||||
case 'month_value_format':
|
||||
case 'day_size':
|
||||
case 'month_size':
|
||||
case 'year_size':
|
||||
case 'all_extra':
|
||||
case 'day_extra':
|
||||
case 'month_extra':
|
||||
@@ -140,6 +137,13 @@ class HtmlSelectDate extends Base {
|
||||
case 'year_id':
|
||||
$$_key = (string)$_value;
|
||||
break;
|
||||
case 'day_size':
|
||||
case 'month_size':
|
||||
case 'year_size':
|
||||
// numeric HTML size attribute; cast to int (consistent with
|
||||
// html_select_time) so it cannot break out of size="…" (CWE-79)
|
||||
$$_key = (int)$_value;
|
||||
break;
|
||||
case 'display_days':
|
||||
case 'display_months':
|
||||
case 'display_years':
|
||||
|
||||
+44
-38
@@ -160,6 +160,7 @@ class TemplateLexer
|
||||
'LOGOP' => '"<", "==" ... logical operator',
|
||||
'TLOGOP' => '"lt", "eq" ... logical operator; "is div by" ... if condition',
|
||||
'SCOND' => '"is even" ... if condition',
|
||||
'MATCHES' => '"matches" regex operator',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -567,7 +568,7 @@ class TemplateLexer
|
||||
public function yylex3()
|
||||
{
|
||||
if (!isset($this->yy_global_pattern3)) {
|
||||
$this->yy_global_pattern3 = $this->replace("/\G(\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal)|\G([\"])|\G('[^'\\\\]*(?:\\\\.[^'\\\\]*)*')|\G([$][0-9]*[a-zA-Z_]\\w*)|\G([$])|\G(\\s+is\\s+(not\\s+)?in\\s+)|\G(\\s+as\\s+)|\G(\\s+to\\s+)|\G(\\s+step\\s+)|\G(\\s+instanceof\\s+)|\G(\\s*([!=][=]{1,2}|[<][=>]?|[>][=]?|[&|]{2})\\s*)|\G(\\s+(eq|ne|neq|gt|ge|gte|lt|le|lte|mod|and|or|xor)\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even|div)\\s+by\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even))|\G([!]\\s*|not\\s+)|\G([(](int(eger)?|bool(ean)?|float|double|real|string|binary|array|object)[)]\\s*)|\G(\\s*[(]\\s*)|\G(\\s*[)])|\G(\\[\\s*)|\G(\\s*\\])|\G(\\s*[-][>]\\s*)|\G(\\s*[=][>]\\s*)|\G(\\s*[=]\\s*)|\G(([+]|[-]){2})|\G(\\s*([+]|[-])\\s*)|\G(\\s*([*]{1,2}|[%\/^&]|[<>]{2})\\s*)|\G([@])|\G(array\\s*[(]\\s*)|\G([#])|\G(\\s+[0-9]*[a-zA-Z_][a-zA-Z0-9_\-:]*\\s*[=]\\s*)|\G(([0-9]*[a-zA-Z_]\\w*)?(\\\\[0-9]*[a-zA-Z_]\\w*)+)|\G([0-9]*[a-zA-Z_]\\w*)|\G(\\d+)|\G([`])|\G([|][@]?)|\G([.])|\G(\\s*[,]\\s*)|\G(\\s*[;]\\s*)|\G([:]{2})|\G(\\s*[:]\\s*)|\G(\\s*[?]\\s*)|\G(0[xX][0-9a-fA-F]+)|\G(\\s+)|\G([\S\s])/isS");
|
||||
$this->yy_global_pattern3 = $this->replace("/\G(\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal)|\G([\"])|\G('[^'\\\\]*(?:\\\\.[^'\\\\]*)*')|\G([$][0-9]*[a-zA-Z_]\\w*)|\G([$])|\G(\\s+is\\s+(not\\s+)?in\\s+)|\G(\\s+matches\\s+)|\G(\\s+as\\s+)|\G(\\s+to\\s+)|\G(\\s+step\\s+)|\G(\\s+instanceof\\s+)|\G(\\s*([!=][=]{1,2}|[<][=>]?|[>][=]?|[&|]{2})\\s*)|\G(\\s+(eq|ne|neq|gt|ge|gte|lt|le|lte|mod|and|or|xor)\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even|div)\\s+by\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even))|\G([!]\\s*|not\\s+)|\G([(](int(eger)?|bool(ean)?|float|double|real|string|binary|array|object)[)]\\s*)|\G(\\s*[(]\\s*)|\G(\\s*[)])|\G(\\[\\s*)|\G(\\s*\\])|\G(\\s*[-][>]\\s*)|\G(\\s*[=][>]\\s*)|\G(\\s*[=]\\s*)|\G(([+]|[-]){2})|\G(\\s*([+]|[-])\\s*)|\G(\\s*([*]{1,2}|[%\/^&]|[<>]{2})\\s*)|\G([@])|\G(array\\s*[(]\\s*)|\G([#])|\G(\\s+[0-9]*[a-zA-Z_][a-zA-Z0-9_\-:]*\\s*[=]\\s*)|\G(([0-9]*[a-zA-Z_]\\w*)?(\\\\[0-9]*[a-zA-Z_]\\w*)+)|\G([0-9]*[a-zA-Z_]\\w*)|\G(\\d+)|\G([`])|\G([|][@]?)|\G([.])|\G(\\s*[,]\\s*)|\G(\\s*[;]\\s*)|\G([:]{2})|\G(\\s*[:]\\s*)|\G(\\s*[?]\\s*)|\G(0[xX][0-9a-fA-F]+)|\G(\\s+)|\G([\S\s])/isS");
|
||||
}
|
||||
if (!isset($this->dataLength)) {
|
||||
$this->dataLength = strlen($this->data);
|
||||
@@ -662,119 +663,124 @@ class TemplateLexer
|
||||
public function yy_r3_10()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_AS;
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_MATCHES;
|
||||
}
|
||||
public function yy_r3_11()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TO;
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_AS;
|
||||
}
|
||||
public function yy_r3_12()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_STEP;
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TO;
|
||||
}
|
||||
public function yy_r3_13()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_INSTANCEOF;
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_STEP;
|
||||
}
|
||||
public function yy_r3_14()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_INSTANCEOF;
|
||||
}
|
||||
public function yy_r3_15()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_LOGOP;
|
||||
}
|
||||
public function yy_r3_16()
|
||||
public function yy_r3_17()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_SLOGOP;
|
||||
}
|
||||
public function yy_r3_18()
|
||||
public function yy_r3_19()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TLOGOP;
|
||||
}
|
||||
public function yy_r3_21()
|
||||
public function yy_r3_22()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_SINGLECOND;
|
||||
}
|
||||
public function yy_r3_24()
|
||||
public function yy_r3_25()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_NOT;
|
||||
}
|
||||
public function yy_r3_25()
|
||||
public function yy_r3_26()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TYPECAST;
|
||||
}
|
||||
public function yy_r3_29()
|
||||
public function yy_r3_30()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_OPENP;
|
||||
}
|
||||
public function yy_r3_30()
|
||||
public function yy_r3_31()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_CLOSEP;
|
||||
}
|
||||
public function yy_r3_31()
|
||||
public function yy_r3_32()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_OPENB;
|
||||
}
|
||||
public function yy_r3_32()
|
||||
public function yy_r3_33()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_CLOSEB;
|
||||
}
|
||||
public function yy_r3_33()
|
||||
public function yy_r3_34()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_PTR;
|
||||
}
|
||||
public function yy_r3_34()
|
||||
public function yy_r3_35()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_APTR;
|
||||
}
|
||||
public function yy_r3_35()
|
||||
public function yy_r3_36()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_EQUAL;
|
||||
}
|
||||
public function yy_r3_36()
|
||||
public function yy_r3_37()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_INCDEC;
|
||||
}
|
||||
public function yy_r3_38()
|
||||
public function yy_r3_39()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_UNIMATH;
|
||||
}
|
||||
public function yy_r3_40()
|
||||
public function yy_r3_41()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_MATH;
|
||||
}
|
||||
public function yy_r3_42()
|
||||
public function yy_r3_43()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_AT;
|
||||
}
|
||||
public function yy_r3_43()
|
||||
public function yy_r3_44()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_ARRAYOPEN;
|
||||
}
|
||||
public function yy_r3_44()
|
||||
public function yy_r3_45()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_HATCH;
|
||||
}
|
||||
public function yy_r3_45()
|
||||
public function yy_r3_46()
|
||||
{
|
||||
|
||||
// resolve conflicts with shorttag and right_delimiter starting with '='
|
||||
@@ -786,73 +792,73 @@ class TemplateLexer
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_ATTR;
|
||||
}
|
||||
}
|
||||
public function yy_r3_46()
|
||||
public function yy_r3_47()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_NAMESPACE;
|
||||
}
|
||||
public function yy_r3_49()
|
||||
public function yy_r3_50()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_ID;
|
||||
}
|
||||
public function yy_r3_50()
|
||||
public function yy_r3_51()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_INTEGER;
|
||||
}
|
||||
public function yy_r3_51()
|
||||
public function yy_r3_52()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_BACKTICK;
|
||||
$this->yypopstate();
|
||||
}
|
||||
public function yy_r3_52()
|
||||
public function yy_r3_53()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_VERT;
|
||||
}
|
||||
public function yy_r3_53()
|
||||
public function yy_r3_54()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_DOT;
|
||||
}
|
||||
public function yy_r3_54()
|
||||
public function yy_r3_55()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_COMMA;
|
||||
}
|
||||
public function yy_r3_55()
|
||||
public function yy_r3_56()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_SEMICOLON;
|
||||
}
|
||||
public function yy_r3_56()
|
||||
public function yy_r3_57()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_DOUBLECOLON;
|
||||
}
|
||||
public function yy_r3_57()
|
||||
public function yy_r3_58()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_COLON;
|
||||
}
|
||||
public function yy_r3_58()
|
||||
public function yy_r3_59()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_QMARK;
|
||||
}
|
||||
public function yy_r3_59()
|
||||
public function yy_r3_60()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_HEX;
|
||||
}
|
||||
public function yy_r3_60()
|
||||
public function yy_r3_61()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_SPACE;
|
||||
}
|
||||
public function yy_r3_61()
|
||||
public function yy_r3_62()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TEXT;
|
||||
|
||||
@@ -160,6 +160,7 @@ class TemplateLexer
|
||||
'LOGOP' => '"<", "==" ... logical operator',
|
||||
'TLOGOP' => '"lt", "eq" ... logical operator; "is div by" ... if condition',
|
||||
'SCOND' => '"is even" ... if condition',
|
||||
'MATCHES' => '"matches" regex operator',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -325,6 +326,7 @@ class TemplateLexer
|
||||
tlop = ~\s+is\s+(not\s+)?(odd|even|div)\s+by\s+~
|
||||
scond = ~\s+is\s+(not\s+)?(odd|even)~
|
||||
isin = ~\s+is\s+(not\s+)?in\s+~
|
||||
matches = ~\s+matches\s+~
|
||||
as = ~\s+as\s+~
|
||||
to = ~\s+to\s+~
|
||||
step = ~\s+step\s+~
|
||||
@@ -469,6 +471,9 @@ class TemplateLexer
|
||||
isin {
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_ISIN;
|
||||
}
|
||||
matches {
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_MATCHES;
|
||||
}
|
||||
as {
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_AS;
|
||||
}
|
||||
|
||||
+1179
-1120
File diff suppressed because it is too large
Load Diff
@@ -681,6 +681,11 @@ expr(res) ::= expr(e1) isin(c) value(v). {
|
||||
res = c . e1.',(array)'.v.')';
|
||||
}
|
||||
|
||||
// regex matching
|
||||
expr(res) ::= expr(e1) matchop(c) value(e2). {
|
||||
res = c . e2 . ',' . e1 . ') ';
|
||||
}
|
||||
|
||||
// null coalescing
|
||||
nullcoalescing(res) ::= expr(v) QMARK QMARK expr(e2). {
|
||||
res = v.' ?? '.e2;
|
||||
@@ -1254,6 +1259,11 @@ static_class_access(res) ::= ID(v). {
|
||||
res = array(v, '');
|
||||
}
|
||||
|
||||
// static class constant with object chain
|
||||
static_class_access(res) ::= ID(v) objectchain(oc). {
|
||||
res = array(v, oc);
|
||||
}
|
||||
|
||||
// static class variables
|
||||
static_class_access(res) ::= DOLLARID(v) arrayindex(a). {
|
||||
res = array(v, a, 'property');
|
||||
@@ -1313,6 +1323,10 @@ scond(res) ::= SINGLECOND(o). {
|
||||
res = $scond[$op];
|
||||
}
|
||||
|
||||
matchop(res) ::= MATCHES(o). {
|
||||
res = 'preg_match(';
|
||||
}
|
||||
|
||||
//
|
||||
// ARRAY element assignment
|
||||
//
|
||||
|
||||
@@ -54,8 +54,19 @@ class StreamPlugin extends RecompiledPlugin {
|
||||
$filepath = str_replace(':', '://', $source->getFullResourceName());
|
||||
}
|
||||
|
||||
// Validate the underlying stream wrapper against the security policy.
|
||||
// When the built-in "stream" resource type is used (e.g.
|
||||
// stream:php://filter/...), BasePlugin::load() matches the "stream"
|
||||
// sysplugin before the stream_get_wrappers()/isTrustedStream() check,
|
||||
// so the nested wrapper ("php" here) is never validated. Parse the
|
||||
// wrapper scheme from the resolved path and check it explicitly so that
|
||||
// e.g. Security::$streams = null blocks it before fopen() (CWE-22/-441).
|
||||
$smarty = $source->getSmarty();
|
||||
if (is_object($smarty->security_policy) && ($_pos = strpos($filepath, '://')) !== false) {
|
||||
$smarty->security_policy->isTrustedStream(strtolower(substr($filepath, 0, $_pos)));
|
||||
}
|
||||
|
||||
$t = '';
|
||||
// the availability of the stream has already been checked in Smarty\Resource\Base::fetch()
|
||||
$fp = fopen($filepath, 'r+');
|
||||
if ($fp) {
|
||||
while (!feof($fp) && ($current_line = fgets($fp)) !== false) {
|
||||
|
||||
+30
-4
@@ -52,7 +52,7 @@ class Security {
|
||||
/**
|
||||
* This is an array of trusted static classes.
|
||||
* If empty access to all static classes is allowed.
|
||||
* If set to 'none' none is allowed.
|
||||
* To disable access to all static classes set $static_classes = null.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@@ -206,7 +206,11 @@ class Security {
|
||||
* @return boolean true if class is trusted
|
||||
*/
|
||||
public function isTrustedStaticClass($class_name, $compiler) {
|
||||
if (isset($this->static_classes)
|
||||
// Only an array enables access: an empty array allows all classes, a
|
||||
// populated array is an allowlist. Any other value (null, or the
|
||||
// documented "none") denies all. Using is_array() rather than isset()
|
||||
// also avoids a PHP 8 TypeError from passing a non-array to in_array().
|
||||
if (is_array($this->static_classes)
|
||||
&& (empty($this->static_classes) || in_array($class_name, $this->static_classes))
|
||||
) {
|
||||
return true;
|
||||
@@ -474,12 +478,34 @@ class Security {
|
||||
* @throws \Smarty\Exception
|
||||
*/
|
||||
private function _checkDir($filepath, $dirs) {
|
||||
$directory = dirname($this->smarty->_realpath($filepath, true)) . DIRECTORY_SEPARATOR;
|
||||
// Resolve the canonical, symlink-free path of the requested file so that
|
||||
// a symlink located inside a trusted directory cannot be abused to read
|
||||
// a file outside of it (CWE-22 path traversal). Smarty::_realpath() only
|
||||
// normalizes the path as a string and does not follow symlinks, so we
|
||||
// fall back to it only when the file does not yet exist on disk (e.g.
|
||||
// config/cache paths that are validated before being written).
|
||||
$realpath = @realpath($filepath);
|
||||
$resolved = $realpath !== false ? $realpath : $this->smarty->_realpath($filepath, true);
|
||||
$directory = dirname($resolved) . DIRECTORY_SEPARATOR;
|
||||
|
||||
// Canonicalize the trusted directories the same way. This keeps
|
||||
// legitimate symlinked deployment paths working (e.g. a Capistrano-style
|
||||
// "current" release symlink, or macOS' /var -> /private/var): both the
|
||||
// file and the trusted directories are compared after symlinks have been
|
||||
// resolved.
|
||||
$trusted = [];
|
||||
foreach ($dirs as $dir => $unused) {
|
||||
$trusted[$dir] = true;
|
||||
if (($dirRealpath = @realpath($dir)) !== false) {
|
||||
$trusted[rtrim($dirRealpath, '\\/') . DIRECTORY_SEPARATOR] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$_directory = [];
|
||||
if (!preg_match('#[\\\\/][.][.][\\\\/]#', $directory)) {
|
||||
while (true) {
|
||||
// test if the directory is trusted
|
||||
if (isset($dirs[$directory])) {
|
||||
if (isset($trusted[$directory])) {
|
||||
return $_directory;
|
||||
}
|
||||
// abort if we've reached root
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ class Smarty extends \Smarty\TemplateBase {
|
||||
/**
|
||||
* smarty version
|
||||
*/
|
||||
const SMARTY_VERSION = '5.7.0';
|
||||
const SMARTY_VERSION = '5.8.4';
|
||||
|
||||
/**
|
||||
* define caching modes
|
||||
|
||||
+6
-2
@@ -260,7 +260,11 @@ class Template extends TemplateBase {
|
||||
|
||||
$tpl = $this->smarty->doCreateTemplate($template_name, $cache_id, $compile_id, $this, $caching, $cache_lifetime);
|
||||
|
||||
$tpl->inheritance = $this->getInheritance(); // re-use the same Inheritance object inside the inheritance tree
|
||||
// Re-use the same Inheritance object only inside an active inheritance tree, i.e. when this
|
||||
// (including) template already has one. A template outside any inheritance tree has no
|
||||
// Inheritance object (null); sub-templates it {include}s must then start with their own, so an
|
||||
// {include}d template that uses {block}/{extends} creates a fresh root via getInheritance().
|
||||
$tpl->inheritance = $this->inheritance;
|
||||
|
||||
if ($scope) {
|
||||
$tpl->defaultScope = $scope;
|
||||
@@ -531,7 +535,7 @@ class Template extends TemplateBase {
|
||||
*/
|
||||
public function getRightDelimiter()
|
||||
{
|
||||
return $this->right_delimiter ?? $this->getSmarty()->getRightDelimiter();;
|
||||
return $this->right_delimiter ?? $this->getSmarty()->getRightDelimiter();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,4 +15,3 @@ if (!ini_get('date.timezone')) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
/*
|
||||
* Smarty PHPUnit Config
|
||||
*/
|
||||
define('individualFolders', true);
|
||||
define('MysqlCacheEnable', false);
|
||||
define('PdoCacheEnable', false);
|
||||
define('PdoGzipCacheEnable', false);
|
||||
|
||||
+127
-43
@@ -49,6 +49,37 @@ class PHPUnit_Smarty extends PHPUnit\Framework\TestCase
|
||||
*/
|
||||
public static $cwd = null;
|
||||
|
||||
/**
|
||||
* Temp directory base for this test class (compile, cache, templates_tmp)
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private static $tempBase = null;
|
||||
|
||||
/**
|
||||
* Unique token for the current test class's temp directory.
|
||||
* Generated once per class.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private static $tempId = null;
|
||||
|
||||
/**
|
||||
* Return the temp directory base for the current test class.
|
||||
*
|
||||
* @return string
|
||||
* @throws \LogicException If the temp directory base has not been initialized yet.
|
||||
*/
|
||||
public static function getTempBase(): string
|
||||
{
|
||||
if (self::$tempBase === null) {
|
||||
throw new \LogicException(
|
||||
'Temp directory base has not been initialized. Call setUpSmarty() before using temp-path helpers.'
|
||||
);
|
||||
}
|
||||
return self::$tempBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* PDO object for Mysql tests
|
||||
*
|
||||
@@ -75,8 +106,37 @@ class PHPUnit_Smarty extends PHPUnit\Framework\TestCase
|
||||
*/
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
//self::$pdo = null;
|
||||
self::$testNumber = 0;
|
||||
// Remove the unique temp directory for this test class unless the caller
|
||||
// wants to inspect the artifacts (e.g. for debugging a failure).
|
||||
if (!getenv('KEEP_SMARTY_TEST_ARTIFACTS') && self::$tempBase !== null && is_dir(self::$tempBase)) {
|
||||
self::removeDir(self::$tempBase);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively remove a directory, silently ignoring any errors.
|
||||
*
|
||||
* @param string $dir
|
||||
*/
|
||||
private static function removeDir(string $dir): void
|
||||
{
|
||||
$dir = rtrim($dir, DIRECTORY_SEPARATOR);
|
||||
$items = @scandir($dir);
|
||||
if ($items === false) {
|
||||
return;
|
||||
}
|
||||
foreach ($items as $item) {
|
||||
if ($item === '.' || $item === '..') {
|
||||
continue;
|
||||
}
|
||||
$path = $dir . DIRECTORY_SEPARATOR . $item;
|
||||
if (is_dir($path) && !is_link($path)) {
|
||||
self::removeDir($path);
|
||||
} else {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
@rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,12 +149,46 @@ class PHPUnit_Smarty extends PHPUnit\Framework\TestCase
|
||||
public function __construct($name = null, array $data = array(), $dataName = '')
|
||||
{
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
if (!defined('individualFolders')) {
|
||||
define('individualFolders', true);
|
||||
}
|
||||
parent::__construct($name, $data, $dataName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the temp directory base for a given test directory.
|
||||
*
|
||||
* Returns a path unique to this test class run under sys_get_temp_dir(),
|
||||
* so that concurrent or sequential runs of different test classes never
|
||||
* share compiled/cached output. The unique token is generated once per
|
||||
* class lifetime and reset in tearDownAfterClass().
|
||||
*
|
||||
* Example:
|
||||
* /path/to/smarty/tests/UnitTests/TagTests/If
|
||||
* → /tmp/smarty-tests/UnitTests/TagTests/If/<unique-id>/
|
||||
*
|
||||
* @param string $dir absolute test directory
|
||||
* @return string absolute temp base directory (with trailing separator)
|
||||
*/
|
||||
private static function getTempDir($dir)
|
||||
{
|
||||
// Lazily generate a unique token for this test class.
|
||||
if (self::$tempId === null) {
|
||||
self::$tempId = uniqid('', true);
|
||||
}
|
||||
$testsRoot = realpath(__DIR__);
|
||||
$realDir = realpath($dir) ?: $dir;
|
||||
// compute relative path from tests/ root
|
||||
if (strpos($realDir, $testsRoot) === 0) {
|
||||
$relative = substr($realDir, strlen($testsRoot));
|
||||
} else {
|
||||
// fallback: use full path hash
|
||||
$relative = DIRECTORY_SEPARATOR . md5($realDir);
|
||||
}
|
||||
return rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR)
|
||||
. DIRECTORY_SEPARATOR . 'smarty-tests'
|
||||
. $relative
|
||||
. DIRECTORY_SEPARATOR . self::$tempId
|
||||
. DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Smarty instance called for each test
|
||||
*
|
||||
@@ -102,47 +196,37 @@ class PHPUnit_Smarty extends PHPUnit\Framework\TestCase
|
||||
*/
|
||||
public function setUpSmarty($dir)
|
||||
{
|
||||
static $s_dir;
|
||||
// set up current working directory
|
||||
chdir($dir);
|
||||
self::$cwd = getcwd();
|
||||
// compute temp base for this test directory
|
||||
self::$tempBase = self::getTempDir($dir);
|
||||
// create missing folders for test
|
||||
if (self::$init) {
|
||||
if (!is_dir($dir . '/templates')) {
|
||||
mkdir($dir . '/templates');
|
||||
if (!is_dir(self::$tempBase . 'templates_c')) {
|
||||
mkdir(self::$tempBase . 'templates_c', 0775, true);
|
||||
}
|
||||
if (!is_dir($dir . '/configs')) {
|
||||
mkdir($dir . '/configs');
|
||||
}
|
||||
if (individualFolders != 'true') {
|
||||
if (!isset($s_dir[ $dir ])) {
|
||||
$this->cleanDir($dir . '/templates_c');
|
||||
$this->cleanDir($dir . '/cache');
|
||||
if (is_dir($dir . '/templates_tmp')) {
|
||||
$this->cleanDir($dir . '/templates_tmp');
|
||||
}
|
||||
$s_dir[ $dir ] = true;
|
||||
}
|
||||
$dir = __DIR__;
|
||||
}
|
||||
if (!is_dir($dir . '/templates_c')) {
|
||||
mkdir($dir . '/templates_c');
|
||||
}
|
||||
chmod($dir . '/templates_c', 0775);
|
||||
if (!is_dir($dir . '/cache')) {
|
||||
mkdir($dir . '/cache');
|
||||
chmod($dir . '/cache', 0775);
|
||||
if (!is_dir(self::$tempBase . 'cache')) {
|
||||
mkdir(self::$tempBase . 'cache', 0775, true);
|
||||
}
|
||||
if (!is_dir(self::$tempBase . 'templates_tmp')) {
|
||||
mkdir(self::$tempBase . 'templates_tmp', 0775, true);
|
||||
}
|
||||
self::$init = false;
|
||||
}
|
||||
clearstatcache();
|
||||
|
||||
// instance Smarty class
|
||||
$this->smarty = new \Smarty\Smarty();
|
||||
if (individualFolders != 'true') {
|
||||
$this->smarty->setCompileDir(__DIR__ . '/templates_c');
|
||||
$this->smarty->setCacheDir(__DIR__ . '/cache');
|
||||
$this->smarty->setCompileDir(self::getTempBase() . 'templates_c');
|
||||
$this->smarty->setCacheDir(self::getTempBase() . 'cache');
|
||||
$this->smarty->addTemplateDir(self::getTempBase() . 'templates_tmp');
|
||||
|
||||
// Clean output dirs once at the start of each test class run
|
||||
if (self::$testNumber === 0) {
|
||||
$this->cleanDirs();
|
||||
}
|
||||
self::$testNumber++;
|
||||
|
||||
}
|
||||
|
||||
@@ -230,24 +314,24 @@ KEY `name` (`name`)
|
||||
{
|
||||
$this->cleanCompileDir();
|
||||
$this->cleanCacheDir();
|
||||
if (is_dir(self::$cwd . '/templates_tmp')) {
|
||||
$this->cleanDir(self::$cwd . '/templates_tmp');
|
||||
$templatesTmpDir = self::getTempBase() . 'templates_tmp';
|
||||
if (is_dir($templatesTmpDir)) {
|
||||
$this->cleanDir($templatesTmpDir);
|
||||
}
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make temporary template file
|
||||
*
|
||||
*/
|
||||
public function makeTemplateFile($name, $code)
|
||||
protected function makeTemplateFile($name, $code)
|
||||
{
|
||||
if (!is_dir(self::$cwd . '/templates_tmp')) {
|
||||
mkdir(self::$cwd . '/templates_tmp');
|
||||
chmod(self::$cwd . '/templates_tmp', 0775);
|
||||
}
|
||||
$fileName = self::$cwd . '/templates_tmp/' . "{$name}";
|
||||
file_put_contents($fileName, $code);
|
||||
file_put_contents(self::getTempBase() . 'templates_tmp' . '/' . $name, $code);
|
||||
}
|
||||
|
||||
protected function removeTemplateFile($name)
|
||||
{
|
||||
unlink(self::getTempBase() . 'templates_tmp' . '/' . $name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@ class TemplateNormalizationTest extends PHPUnit_Smarty
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->smarty->setTemplateDir(__DIR__ . '/templates');
|
||||
}
|
||||
|
||||
public function testGetTemplateDir()
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -18,10 +18,6 @@ class UndefinedTemplateVarTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
/**
|
||||
* Test Error suppression template fetched by Smarty object
|
||||
*/
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -21,10 +21,6 @@ class FilterTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test loaded filter
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -20,10 +20,6 @@ class GetterSetterTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test setter on Smarty object
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -24,10 +24,6 @@ class DefaultPluginHandlerTest extends PHPUnit_Smarty
|
||||
}
|
||||
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
public function testDefaultFunctionScript()
|
||||
{
|
||||
$this->assertEquals("scriptfunction foo bar", $this->smarty->fetch('test_default_function_script.tpl'));
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -21,10 +21,6 @@ class HttpModifiedSinceTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -25,11 +25,6 @@ class CacheResourceFileTest extends CacheResourceTestCommon
|
||||
}
|
||||
|
||||
|
||||
public function testInit()
|
||||
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test getCachedFilepath with configuration['useSubDirs'] enabled
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -32,10 +32,6 @@ class CacheResourceCustomMemcacheTest extends CacheResourceTestCommon
|
||||
$this->smarty->setCachingType('memcachetest');
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
protected function doClearCacheAssertion($a, $b)
|
||||
{
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -29,15 +29,11 @@ if (MysqlCacheEnable == true) {
|
||||
$this->getConnection();
|
||||
}
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->initMysqlCache();
|
||||
parent::setUp();
|
||||
$this->smarty->setCachingType('mysqltest');
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
$this->initMysqlCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -28,15 +28,11 @@ class CacheResourceCustomPDOTest extends CacheResourceTestCommon
|
||||
$this->getConnection();
|
||||
}
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->initMysqlCache();
|
||||
parent::setUp();
|
||||
$this->smarty->registerCacheResource('pdo',
|
||||
new Smarty_CacheResource_Pdotest($this->getPDO(), 'output_cache'));
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
$this->initMysqlCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use Smarty\Template\Cached;
|
||||
|
||||
require_once __DIR__ . '/../../__shared/cacheresources/cacheresource.pdo.php';
|
||||
require_once __DIR__ . '/../_shared/cacheresources/cacheresource.pdo.php';
|
||||
|
||||
class Smarty_CacheResource_Pdotest extends Smarty_CacheResource_Pdo
|
||||
{
|
||||
|
||||
@@ -27,16 +27,12 @@ class CacheResourceCustomPDOGzipTest extends CacheResourceTestCommon
|
||||
$this->getConnection();
|
||||
}
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->initMysqlCache();
|
||||
parent::setUp();
|
||||
$this->smarty->setCachingType('pdo');
|
||||
$this->smarty->registerCacheResource('pdo', new Smarty_CacheResource_Pdo_Gziptest($this->getPDO(),
|
||||
'output_cache'));
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
$this->initMysqlCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use Smarty\Template\Cached;
|
||||
|
||||
require_once __DIR__ . '/../../__shared/cacheresources/cacheresource.pdo_gzip.php';
|
||||
require_once __DIR__ . '/../_shared/cacheresources/cacheresource.pdo_gzip.php';
|
||||
|
||||
class Smarty_CacheResource_Pdo_Gziptest extends Smarty_CacheResource_Pdo_Gzip
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@ if (MysqlCacheEnable == true) {
|
||||
$this->getConnection();
|
||||
}
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->initMysqlCache();
|
||||
parent::setUp();
|
||||
if (!class_exists('Smarty_CacheResource_Mysqltest', false)) {
|
||||
require_once(__DIR__ . "/../_shared/PHPunitplugins/cacheresource.mysqltest.php");
|
||||
@@ -34,11 +35,6 @@ if (MysqlCacheEnable == true) {
|
||||
$this->smarty->registerCacheResource('foobar', new Smarty_CacheResource_Mysqltest());
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
$this->initMysqlCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
use Smarty\Template;
|
||||
use Smarty\Template\Cached;
|
||||
|
||||
require_once __DIR__ . '/../../../__shared/cacheresources/cacheresource.memcache.php';
|
||||
require_once __DIR__ . '/../cacheresources/cacheresource.memcache.php';
|
||||
|
||||
class Smarty_CacheResource_Memcachetest extends Smarty_CacheResource_Memcache
|
||||
{
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
use Smarty\Exception;
|
||||
use Smarty\Template\Cached;
|
||||
|
||||
require_once __DIR__ . '/../../../__shared/cacheresources/cacheresource.mysql.php';
|
||||
require_once __DIR__ . '/../cacheresources/cacheresource.mysql.php';
|
||||
|
||||
class Smarty_CacheResource_Mysqltest extends Smarty_CacheResource_Mysql
|
||||
{
|
||||
|
||||
@@ -20,10 +20,6 @@ class CompileCompilerPluginTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test compiler plugin tag in template file
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -17,13 +17,9 @@ class AutoliteralTest extends PHPUnit_Smarty
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->smarty->addPluginsDir("../../__shared/PHPunitplugins/");
|
||||
$this->smarty->addPluginsDir("./plugins/");
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test '{ ' delimiter
|
||||
|
||||
@@ -20,10 +20,6 @@ class DelimiterTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test <{ }> delimiter
|
||||
|
||||
@@ -17,17 +17,9 @@ class UserliteralTest extends PHPUnit_Smarty
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
if (!property_exists('Smarty', 'literals')) {
|
||||
$this->markTestSkipped('user literal support');
|
||||
} else {
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
public function testUserLiteral()
|
||||
{
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -28,10 +28,6 @@ class ConfigVarTest extends PHPUnit_Smarty
|
||||
/**
|
||||
* empty templat_c and cache folders
|
||||
*/
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test number config variable
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -25,10 +25,6 @@ class CustomResourceAmbiguousTest extends PHPUnit_Smarty
|
||||
// Smarty::$_resource_cache = array();
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
protected function relative($path)
|
||||
{
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
+1
-5
@@ -6,7 +6,7 @@
|
||||
* @author Uwe Tews
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../__shared/resources/resource.extendsall.php';
|
||||
require_once __DIR__ . '/resources/resource.extendsall.php';
|
||||
|
||||
/**
|
||||
* class for demo resource plugin extendsall tests
|
||||
@@ -22,10 +22,6 @@ class ResourceExtendsAllPluginTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test extendsall
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -24,17 +24,9 @@ if (MysqlResourceEnable == true) {
|
||||
$this->getConnection();
|
||||
}
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->smarty->addPluginsDir("./PHPunitplugins/");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
$this->initMysqlResource();
|
||||
PHPUnit_Smarty::$pdo->exec("REPLACE INTO templates (name, source) VALUES ('test.tpl', '{\$x = \'hello world\'}{\$x}')");
|
||||
$this->smarty->addPluginsDir("./PHPunitplugins/");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1,2 +0,0 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user