mirror of
https://github.com/smarty-php/smarty.git
synced 2026-08-04 12:34:33 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b5470d627 |
@@ -1 +0,0 @@
|
||||
github: [wisskid]
|
||||
@@ -34,15 +34,35 @@ jobs:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
|
||||
compiler:
|
||||
- default
|
||||
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
php-version: "8.0"
|
||||
compiler: jit
|
||||
- os: ubuntu-latest
|
||||
php-version: "8.1"
|
||||
compiler: jit
|
||||
- os: ubuntu-latest
|
||||
php-version: "8.2"
|
||||
compiler: jit
|
||||
- os: ubuntu-latest
|
||||
php-version: "8.3"
|
||||
compiler: jit
|
||||
- os: ubuntu-latest
|
||||
php-version: "8.4"
|
||||
compiler: jit
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Override PHP ini values for JIT compiler
|
||||
if: matrix.compiler == 'jit'
|
||||
run: echo "PHP_INI_VALUES::assert.exception=1, zend.assertions=1, opcache.enable=1, opcache.enable_cli=1, opcache.optimization_level=-1, opcache.jit=1255, opcache.jit_buffer_size=32M" >> $GITHUB_ENV
|
||||
|
||||
- name: Install PHP with extensions
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
|
||||
@@ -7,5 +7,3 @@ phpunit*
|
||||
.phpunit.result.cache
|
||||
vendor/*
|
||||
composer.lock
|
||||
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
# 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,64 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [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
|
||||
|
||||
|
||||
## [5.6.0] - 2025-10-03
|
||||
- Added support for shorttags in functions [#1005](https://github.com/smarty-php/smarty/issues/1005)
|
||||
|
||||
|
||||
## [5.5.2] - 2025-08-26
|
||||
|
||||
|
||||
- Fixed escaping of array/object keys in debug_print_var
|
||||
|
||||
## [5.5.1] - 2025-05-19
|
||||
- Fix missing support for loading modifiercompilers from plugin dir in BCPluginsAdapter [#1132](https://github.com/smarty-php/smarty/pull/1132)
|
||||
|
||||
|
||||
## [5.5.0] - 2025-05-03
|
||||
- Support trailing comma in array [#1013](https://github.com/smarty-php/smarty/issues/1013)
|
||||
|
||||
|
||||
## [5.4.5] - 2025-04-15
|
||||
- Fixed deprecation notice in StringEval in PHP8.4 [#1119](https://github.com/smarty-php/smarty/issues/1119)
|
||||
|
||||
|
||||
## [5.4.4] - 2025-04-13
|
||||
- Fix syntax error occurring when registering a function plugin that ends with the string 'close' [#1122](https://github.com/smarty-php/smarty/issues/1122)
|
||||
- Replace SMARTY_VERSION constant with $smarty.version in debug.tpl [#1073](https://github.com/smarty-php/smarty/issues/1073)
|
||||
|
||||
|
||||
## [5.4.3] - 2024-12-23
|
||||
- Fix PHP backtraces by qualifying/replacing `call_user_func_array` calls [#1074](https://github.com/smarty-php/smarty/issues/1074)
|
||||
|
||||
|
||||
## [5.4.2] - 2024-11-20
|
||||
|
||||
|
||||
|
||||
@@ -100,11 +100,6 @@ 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
|
||||
@@ -120,7 +115,5 @@ If you are a maintainer, you can publish the document using [mike](https://githu
|
||||
mike deploy 5.x
|
||||
```
|
||||
|
||||
Then, push the `gh-pages` branch.
|
||||
|
||||
## Attribution
|
||||
This guide is based on the **contributing.md**. [Make your own](https://contributing.md/)!
|
||||
|
||||
@@ -7,7 +7,7 @@ Smarty is a template engine for PHP, facilitating the separation of presentation
|
||||
Read the [documentation](https://smarty-php.github.io/smarty/) to find out how to use it.
|
||||
|
||||
## Requirements
|
||||
Smarty v5 can be run with PHP 7.2 to PHP 8.5.
|
||||
Smarty v5 can be run with PHP 7.2 to PHP 8.4.
|
||||
|
||||
## Installation
|
||||
Smarty versions 3.1.11 or later can be installed with [Composer](https://getcomposer.org/).
|
||||
@@ -18,11 +18,3 @@ composer require smarty/smarty
|
||||
````
|
||||
|
||||
More in the [Getting Started](./docs/getting-started.md) section of the docs.
|
||||
|
||||
## Sponsors
|
||||
|
||||
Smarty is sponsored by:
|
||||
- Marc Laporte [@marclaporte](https://github.com/marclaporte)
|
||||
- [Temma](https://github.com/Digicreon/Temma), the MVC framework based on Smarty
|
||||
|
||||
Thank you!
|
||||
@@ -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?
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
- Fix PHP backtraces by qualifying/replacing `call_user_func_array` calls [#1074](https://github.com/smarty-php/smarty/issues/1074)
|
||||
@@ -1 +0,0 @@
|
||||
- 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)
|
||||
@@ -1 +0,0 @@
|
||||
- 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.
|
||||
@@ -50,120 +50,6 @@ 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
|
||||
|
||||
@@ -47,13 +47,11 @@ available within the included template.
|
||||
|
||||
## Option Flags
|
||||
|
||||
| Name | Description |
|
||||
|-----------|--------------------------------------------------------------------------------------|
|
||||
| nocache | Disables caching of this subtemplate |
|
||||
| caching | Enable caching of this subtemplate |
|
||||
| inline \* | If set, merge the compile-code of the subtemplate into the compiled calling template |
|
||||
|
||||
\* The `inline` option flag is currently not implemented in Smarty v5. Using it will not trigger an error, however.
|
||||
| Name | Description |
|
||||
|---------|--------------------------------------------------------------------------------------|
|
||||
| nocache | Disables caching of this subtemplate |
|
||||
| caching | Enable caching of this subtemplate |
|
||||
| inline | If set, merge the compile-code of the subtemplate into the compiled calling template |
|
||||
|
||||
## Examples
|
||||
```smarty
|
||||
|
||||
@@ -27,13 +27,6 @@ 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,13 +28,6 @@ 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,14 +47,6 @@ 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,14 +47,6 @@ 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,15 +31,6 @@ 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,14 +24,6 @@ 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
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# empty
|
||||
|
||||
Returns true if var does not exist or has a value that is empty or equal to zero, aka falsey, see conversion to boolean. Otherwise returns false.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```smarty
|
||||
{if $myVar|empty}it's an empty variable{/if}
|
||||
{if empty($myVar)}it's an empty variable{/if}
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
# in_array
|
||||
|
||||
test if value is contained in an array
|
||||
|
||||
## Basic usage
|
||||
|
||||
```smarty
|
||||
{if in_array('value2', $myarray)} value2 is in myarray{/if}
|
||||
```
|
||||
|
||||
Can be replaced by operator *is in*
|
||||
|
||||
```smarty
|
||||
{if 'value2' is in $myarray}value2 is in myarray{/if}
|
||||
```
|
||||
|
||||
See Also [operators](../language-basic-syntax/language-syntax-operators.md)
|
||||
@@ -123,89 +123,4 @@ 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,8 +22,6 @@ 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Getting started
|
||||
|
||||
## Requirements
|
||||
Smarty can be run with PHP 7.2 to PHP 8.5.
|
||||
Smarty can be run with PHP 7.2 to PHP 8.3.
|
||||
|
||||
## Installation
|
||||
Smarty can be installed with [Composer](https://getcomposer.org/).
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 343.3 174.5" style="enable-background:new 0 0 343.3 174.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#E60000;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M333.8,0H9.5C4.3,0,0,4.3,0,9.5v155.4c0,5.3,4.3,9.5,9.5,9.5h324.3c5.3,0,9.5-4.3,9.5-9.5V9.5
|
||||
C343.3,4.3,339,0,333.8,0z"/>
|
||||
<g>
|
||||
<path class="st1" d="M148.7,58.5c-5.2,0-4.7,4.3-4.7,9.5v38.6c0,5.3,4.3,9.5,9.5,9.5c5.2,0,9.5-4.3,9.5-9.5V81.5
|
||||
C163,65.7,154,58.5,148.7,58.5L148.7,58.5z"/>
|
||||
<path class="st1" d="M47.1,72.6c-5.2,0-4.7,4.3-4.7,9.5v24.5c0,5.2,4.3,9.5,9.5,9.5c5.2,0,9.5-4.3,9.5-9.5V95.5
|
||||
C61.4,79.8,52.4,72.6,47.1,72.6L47.1,72.6z"/>
|
||||
<path class="st1" d="M200.7,58.3c-7.4,0-13.8,6.9-15.2,11.4c-3-7.6-8.5-11.2-12.2-11.2c-5.2,0-4.7,4.3-4.7,9.5l0.1,9.5v29.1
|
||||
c0,5.2,4.3,9.5,9.5,9.5c5.2,0,9.5-4.3,9.5-9.5V84.4c0-3.8,1.2-8.4,7-8.4c1.7,0,6.8,0,6.8,8.4v22.2c0,5.2,4.3,9.5,9.5,9.5
|
||||
c5.2,0,9.5-4.3,9.5-9.5V79.8C220.5,66.5,212.9,58.3,200.7,58.3L200.7,58.3z"/>
|
||||
<path class="st1" d="M130.8,58.4c-4.4,0-7.7,2.6-9.1,7.1l-6.1,19.9l-6.1-19.7c-1.4-4.6-5.1-7.4-9.6-7.4h-0.9
|
||||
c-4.5,0-8.2,2.8-9.6,7.4l-5.4,17.5c-1.5-4.9-5.8-31.4-31.1-33c-8.7-0.5-11.2,4.7-12,7.6c-0.7,2.8-0.4,5.2,1.3,7.3
|
||||
c1.7,2.2,5,2.5,8.9,4C61,73,63.6,81.8,65.9,87.3c0.8,1.9,6.4,20.8,6.4,20.8c1.8,5.2,5.4,8.1,10.2,8.1H83c4.8,0,8.4-2.9,10-7.8
|
||||
l6.3-19.7l6.2,19.7c1.6,5,5.2,7.8,10,7.8h0.5c4.8,0,8.4-2.9,10.2-8L139.2,71l0-0.1c0.2-0.7,0.6-2.3,0.6-3.6
|
||||
C139.8,62.3,135.9,58.4,130.8,58.4L130.8,58.4z"/>
|
||||
<path class="st1" d="M276,99.9c-0.2-0.2-0.4-0.4-0.6-0.6l-14.1-14.4l11.3-9.6l0,0c1.9-1.8,4.1-4.3,4.1-8c0-4.3-3.2-8.7-8.6-8.7
|
||||
c-3.6,0-5.9,1.9-8,3.9l-15,14.2c-1.6-12.5-9.2-18.4-14-18.4c-4.2,0-4.8,2.7-4.8,6.5c-0.1,1.1-0.1,2.4-0.1,3.7v38
|
||||
c0,4.9,3.8,9.1,8.6,9.6c0.3,0,0.7,0.1,1,0.1c5.3,0,9.5-4.4,9.5-9.6v-7.5l3.3-3l14.5,16.4c0.6,0.6,1.3,1.3,2.1,1.9
|
||||
c1.1,0.9,2.5,1.5,4.2,1.8c0.1,0,1.2,0.1,1.8,0.1c4.3-0.1,8.7-3.2,8.6-8.6C279.6,104.1,277.9,101.9,276,99.9L276,99.9z"/>
|
||||
<circle class="st1" cx="293.3" cy="106.7" r="9.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="195mm"
|
||||
height="43.093071mm"
|
||||
viewBox="0 0 195 43.093071"
|
||||
version="1.1"
|
||||
id="svg1156"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1153" />
|
||||
<g
|
||||
id="layer1"
|
||||
transform="translate(-6.9662436,-7.8467567)">
|
||||
<g
|
||||
id="g21305-1"
|
||||
style="font-size:33.5139px;line-height:1.25;stroke-width:0.0700042"
|
||||
transform="translate(1.0553387,0.93988827)">
|
||||
<g
|
||||
id="g21248-5">
|
||||
<rect
|
||||
style="font-size:33.5139px;line-height:1.25;fill:#107ef4;fill-opacity:1;stroke:none;stroke-width:0.0727743"
|
||||
id="rect846-9-0-1-2-7-2-1-9"
|
||||
width="43.093071"
|
||||
height="43.093071"
|
||||
x="5.9109049"
|
||||
y="6.9068685"
|
||||
ry="2.4179134" />
|
||||
<path
|
||||
d="M 22.234889,18.179756 V 36.734061 H 19.567887 V 18.179756 h -6.882585 v -2.35155 h 16.403494 v 2.35155 z"
|
||||
style="font-size:31.75px;line-height:1.25;font-family:FreeSans;-inkscape-font-specification:FreeSans;fill:#ffffff;stroke-width:0.238978"
|
||||
id="path16874-7-9-2-4" />
|
||||
<path
|
||||
d="m 41.725989,35.328866 v 1.806679 q -0.946355,0.258097 -1.634613,0.258097 -2.380228,0 -2.46626,-2.380228 -1.691969,2.380228 -4.674422,2.380228 -1.548582,0 -2.580969,-0.745614 v 6.395069 H 27.989497 V 21.707084 h 2.380228 v 10.639329 q 0,1.347839 0.831645,2.150808 0.831646,0.802968 2.23684,0.802968 1.864034,0 2.982454,-1.376517 1.11842,-1.405195 1.11842,-3.699389 v -8.517199 h 2.380227 v 12.503362 q 0,1.175775 1.290485,1.175775 0.229419,0 0.516193,-0.05736 z"
|
||||
style="font-size:31.75px;line-height:1.25;font-family:FreeSans;-inkscape-font-specification:FreeSans;fill:#ffffff;stroke-width:0.238978"
|
||||
id="path16871-6-1-9-9" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="Temma"
|
||||
transform="matrix(0.91459055,0,0,0.9686482,-4.3308403,0.67015143)"
|
||||
id="text87274-2-5-0"
|
||||
style="font-size:39.8454px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';fill:#000000;stroke-width:0.0205099">
|
||||
<path
|
||||
d="m 89.719297,15.597759 v 4.50253 h -10.24872 v 23.30956 h -5.578356 v -23.30956 h -10.24872 v -4.50253 z"
|
||||
style="font-weight:600;font-family:Poppins;-inkscape-font-specification:'Poppins Semi-Bold';fill:#000000"
|
||||
id="path7781-61-0-9" />
|
||||
<path
|
||||
d="m 109.19647,31.894528 q 0,1.195362 -0.15939,2.151652 H 92.899673 q 0.199227,2.390724 1.673504,3.745467 1.4743,1.354744 3.62597,1.354744 3.107933,0 4.422823,-2.669642 h 6.01666 q -0.95629,3.187632 -3.66578,5.259593 -2.70948,2.032115 -6.654173,2.032115 -3.18765,0 -5.737768,-1.394588 -2.51026,-1.434435 -3.944694,-4.024386 -1.394589,-2.589951 -1.394589,-5.97681 0,-3.426705 1.394589,-6.016656 1.394589,-2.589951 3.904849,-3.98454 2.510273,-1.394589 5.777613,-1.394589 3.147783,0 5.618193,1.354743 2.51026,1.354744 3.86501,3.865004 1.39459,2.470415 1.39459,5.697893 z m -5.77759,-1.593816 q -0.0398,-2.151652 -1.55397,-3.426705 -1.51411,-1.314898 -3.705613,-1.314898 -2.07197,0 -3.50643,1.275053 -1.394586,1.235207 -1.713349,3.46655 z"
|
||||
style="font-weight:600;font-family:Poppins;-inkscape-font-specification:'Poppins Semi-Bold';fill:#000000"
|
||||
id="path7783-8-3-1" />
|
||||
<path
|
||||
d="m 140.67428,21.016733 q 4.06423,0 6.53465,2.51026 2.51026,2.470415 2.51026,6.9331 v 12.949756 h -5.57836 V 31.217156 q 0,-2.589951 -1.3149,-3.944695 -1.3149,-1.394589 -3.58608,-1.394589 -2.27119,0 -3.62594,1.394589 -1.31489,1.354744 -1.31489,3.944695 v 12.192693 h -5.57836 V 31.217156 q 0,-2.589951 -1.3149,-3.944695 -1.3149,-1.394589 -3.58608,-1.394589 -2.31104,0 -3.66578,1.394589 -1.3149,1.354744 -1.3149,3.944695 v 12.192693 h -5.57836 V 21.335496 h 5.57836 v 2.669642 q 1.07583,-1.394589 2.74933,-2.191497 1.71336,-0.796908 3.74547,-0.796908 2.58995,0 4.62207,1.115671 2.03211,1.075826 3.14778,3.107942 1.07583,-1.91258 3.10795,-3.068096 2.07196,-1.155517 4.46268,-1.155517 z"
|
||||
style="font-weight:600;font-family:Poppins;-inkscape-font-specification:'Poppins Semi-Bold';fill:#000000"
|
||||
id="path7785-7-6-7" />
|
||||
<path
|
||||
d="m 182.4322,21.016733 q 4.06423,0 6.53465,2.51026 2.51026,2.470415 2.51026,6.9331 v 12.949756 h -5.57836 V 31.217156 q 0,-2.589951 -1.31489,-3.944695 -1.3149,-1.394589 -3.58609,-1.394589 -2.27119,0 -3.62593,1.394589 -1.3149,1.354744 -1.3149,3.944695 v 12.192693 h -5.57836 V 31.217156 q 0,-2.589951 -1.31489,-3.944695 -1.3149,-1.394589 -3.58609,-1.394589 -2.31103,0 -3.66578,1.394589 -1.3149,1.354744 -1.3149,3.944695 v 12.192693 h -5.57835 V 21.335496 h 5.57835 v 2.669642 q 1.07583,-1.394589 2.74934,-2.191497 1.71335,-0.796908 3.74546,-0.796908 2.58996,0 4.62207,1.115671 2.03212,1.075826 3.14779,3.107942 1.07582,-1.91258 3.10794,-3.068096 2.07196,-1.155517 4.46268,-1.155517 z"
|
||||
style="font-weight:600;font-family:Poppins;-inkscape-font-specification:'Poppins Semi-Bold';fill:#000000"
|
||||
id="path7787-9-1-7" />
|
||||
<path
|
||||
d="m 195.34205,32.292982 q 0,-3.347014 1.3149,-5.936965 1.35474,-2.589951 3.62593,-3.98454 2.31104,-1.394589 5.14006,-1.394589 2.47041,0 4.3033,0.996135 1.87274,0.996135 2.98841,2.51026 v -3.147787 h 5.6182 v 22.074353 h -5.6182 v -3.227478 q -1.07583,1.553971 -2.98841,2.589952 -1.87273,0.996134 -4.34315,0.996134 -2.78917,0 -5.10021,-1.434434 -2.27119,-1.434434 -3.62593,-4.024385 -1.3149,-2.629797 -1.3149,-6.016656 z m 17.3726,0.07969 q 0,-2.032116 -0.79691,-3.46655 -0.79691,-1.47428 -2.15165,-2.231343 -1.35475,-0.796908 -2.90872,-0.796908 -1.55397,0 -2.86887,0.757063 -1.31489,0.757062 -2.15165,2.231342 -0.79691,1.434435 -0.79691,3.426705 0,1.99227 0.79691,3.506395 0.83676,1.47428 2.15165,2.271188 1.35475,0.796908 2.86887,0.796908 1.55397,0 2.90872,-0.757062 1.35474,-0.796908 2.15165,-2.231343 0.79691,-1.47428 0.79691,-3.506395 z"
|
||||
style="font-weight:600;font-family:Poppins;-inkscape-font-specification:'Poppins Semi-Bold';fill:#000000"
|
||||
id="path7789-2-0-1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.0 KiB |
+1
-30
@@ -8,25 +8,6 @@ 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,
|
||||
@@ -51,14 +32,4 @@ and 480 for $height, the result is:
|
||||
- [Troubleshooting](./appendixes/troubleshooting.md)
|
||||
|
||||
## Source code
|
||||
- [Smarty repository at GitHub](https://github.com/smarty-php/smarty)
|
||||
|
||||
## Sponsors
|
||||
|
||||
### <img src="img/iwink-logo.svg" alt="iWink logo" width="100">
|
||||
|
||||
[iWink](https://www.iwink.nl/), fullservice digital agency
|
||||
|
||||
### <img src="img/temma-logo.svg" alt="Temma logo" width="100">
|
||||
|
||||
[Temma](https://www.temma.net/), the MVC framework based on Smarty
|
||||
- [Smarty repository at GitHub](https://github.com/smarty-php/smarty)
|
||||
@@ -1,14 +0,0 @@
|
||||
# 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
|
||||
@@ -49,11 +49,6 @@ $smarty->registerPlugin('modifier', 'my_special_func', 'my_special_func');
|
||||
|
||||
But you may want to consider writing a proper [extension](api/extending/extensions.md).
|
||||
|
||||
#### Passing parameters by reference
|
||||
You cannot use plugins that expect a parameter by reference anymore. PHP-function such as `reset()`, `prev()`,
|
||||
`next()` and `end()` can be registered as plugin, but they won't work because they expect a parameter by
|
||||
reference and Smarty will try to pass it by value.
|
||||
|
||||
### Removed undocumented tags
|
||||
|
||||
Smarty 4 still supported some tags that have been carried over from previous version, but have never been documented.
|
||||
@@ -107,15 +102,6 @@ Search your code for the following changes:
|
||||
- `$smarty->assignByRef()` should be replaced with `$smarty->assign()`
|
||||
- `$smarty->loadPlugin()` should be replaced with `$smarty->registerPlugin()`
|
||||
|
||||
### Removed undocumented magic API methods
|
||||
|
||||
Smarty v4 allowed setting (internal) properties for which no setter existed by using magic methods
|
||||
that start with either `set` or `get`. This has been removed in Smarty 5.
|
||||
|
||||
For example, `$smarty->setErrorUnassigned(true);` would set the `error_unassigned` property.
|
||||
|
||||
Every setter of getter that was ever documented or unit tested has been kept.
|
||||
|
||||
### Removed PHP constants
|
||||
|
||||
The following constants have been removed to prevent global side effects.
|
||||
|
||||
@@ -57,11 +57,9 @@ nav:
|
||||
- 'date_format': 'designers/language-modifiers/language-modifier-date-format.md'
|
||||
- 'debug_print_var': 'designers/language-modifiers/language-modifier-debug-print-var.md'
|
||||
- 'default': 'designers/language-modifiers/language-modifier-default.md'
|
||||
- 'empty': "designers/language-modifiers/language-modifier-empty.md"
|
||||
- 'escape': 'designers/language-modifiers/language-modifier-escape.md'
|
||||
- 'from_charset': 'designers/language-modifiers/language-modifier-from-charset.md'
|
||||
- 'indent': 'designers/language-modifiers/language-modifier-indent.md'
|
||||
- 'in_array': 'designers/language-modifiers/language-modifier-in_array.md'
|
||||
- 'is_array': 'designers/language-modifiers/language-modifier-is_array.md'
|
||||
- 'isset': 'designers/language-modifiers/language-modifier-isset.md'
|
||||
- 'join': 'designers/language-modifiers/language-modifier-join.md'
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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="docker compose"
|
||||
COMPOSE_CMD="mutagen-compose"
|
||||
|
||||
$COMPOSE_CMD run --rm php72 ./run-tests.sh $@ && \
|
||||
$COMPOSE_CMD run --rm php73 ./run-tests.sh $@ && \
|
||||
|
||||
@@ -127,6 +127,8 @@ class File extends Base
|
||||
&& (!function_exists('ini_get') || strlen(ini_get('opcache.restrict_api'))) < 1
|
||||
) {
|
||||
opcache_invalidate($_template->getCached()->filepath, true);
|
||||
} elseif (function_exists('apc_compile_file')) {
|
||||
apc_compile_file($_template->getCached()->filepath);
|
||||
}
|
||||
$cached = $_template->getCached();
|
||||
$cached->timestamp = $cached->exists = is_file($cached->filepath);
|
||||
@@ -143,7 +145,7 @@ class File extends Base
|
||||
*
|
||||
* @param Template $_template template object
|
||||
*
|
||||
* @return string|false content
|
||||
* @return string content
|
||||
*/
|
||||
public function retrieveCachedContent(Template $_template)
|
||||
{
|
||||
@@ -221,8 +223,10 @@ class File extends Base
|
||||
$_filepath = (string)$_file;
|
||||
// directory ?
|
||||
if ($_file->isDir()) {
|
||||
// delete folder if empty
|
||||
@rmdir($_file->getPathname());
|
||||
if (!$_cache->isDot()) {
|
||||
// delete folder if empty
|
||||
@rmdir($_file->getPathname());
|
||||
}
|
||||
} else {
|
||||
// delete only php files
|
||||
if (substr($_filepath, -4) !== '.php') {
|
||||
@@ -275,6 +279,8 @@ class File extends Base
|
||||
&& (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1)
|
||||
) {
|
||||
opcache_invalidate($_filepath, true);
|
||||
} elseif (function_exists('apc_delete_file')) {
|
||||
apc_delete_file($_filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Smarty\Compile;
|
||||
|
||||
/**
|
||||
* This class handles compiling the attributes.
|
||||
*/
|
||||
class AttributeCompiler
|
||||
{
|
||||
/**
|
||||
* Array of names of required attributes required by tag
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $required_attributes = [];
|
||||
|
||||
/**
|
||||
* Array of names of optional attribute required by tag
|
||||
* use array('_any') if there is no restriction of attributes names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $optional_attributes = [];
|
||||
|
||||
/**
|
||||
* Shorttag attribute order defined by its names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $shorttag_order = [];
|
||||
|
||||
/**
|
||||
* Array of names of valid option flags
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $option_flags = [];
|
||||
|
||||
public function __construct(
|
||||
array $required_attributes = [],
|
||||
array $optional_attributes = [],
|
||||
array $shorttag_order = [],
|
||||
array $option_flags = []
|
||||
) {
|
||||
$this->required_attributes = $required_attributes;
|
||||
$this->optional_attributes = $optional_attributes;
|
||||
$this->shorttag_order = $shorttag_order;
|
||||
$this->option_flags = $option_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks if the attributes passed are valid
|
||||
* The attributes passed for the tag to compile are checked against the list of required and
|
||||
* optional attributes. Required attributes must be present. Optional attributes are check against
|
||||
* the corresponding list. The keyword '_any' specifies that any attribute will be accepted
|
||||
* as valid
|
||||
*
|
||||
* @param object $compiler compiler object
|
||||
* @param array $attributes attributes applied to the tag
|
||||
*
|
||||
* @return array of mapped attributes for further processing
|
||||
*/
|
||||
public function getAttributes($compiler, $attributes)
|
||||
{
|
||||
$_indexed_attr = [];
|
||||
$options = array_fill_keys($this->option_flags, true);
|
||||
foreach ($attributes as $key => $mixed) {
|
||||
// shorthand ?
|
||||
if (!is_array($mixed)) {
|
||||
// options flag ?
|
||||
if (isset($options[trim($mixed, '\'"')])) {
|
||||
$_indexed_attr[trim($mixed, '\'"')] = true;
|
||||
// shorthand attribute ?
|
||||
} elseif (isset($this->shorttag_order[$key])) {
|
||||
$_indexed_attr[$this->shorttag_order[$key]] = $mixed;
|
||||
} else {
|
||||
// too many shorthands
|
||||
$compiler->trigger_template_error('too many shorthand attributes', null, true);
|
||||
}
|
||||
// named attribute
|
||||
} else {
|
||||
foreach ($mixed as $k => $v) {
|
||||
// options flag?
|
||||
if (isset($options[$k])) {
|
||||
if (is_bool($v)) {
|
||||
$_indexed_attr[$k] = $v;
|
||||
} else {
|
||||
if (is_string($v)) {
|
||||
$v = trim($v, '\'" ');
|
||||
}
|
||||
|
||||
// Mapping array for boolean option value
|
||||
static $optionMap = [1 => true, 0 => false, 'true' => true, 'false' => false];
|
||||
|
||||
if (isset($optionMap[$v])) {
|
||||
$_indexed_attr[$k] = $optionMap[$v];
|
||||
} else {
|
||||
$compiler->trigger_template_error(
|
||||
"illegal value '" . var_export($v, true) .
|
||||
"' for options flag '{$k}'",
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
// must be named attribute
|
||||
} else {
|
||||
$_indexed_attr[$k] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if all required attributes present
|
||||
foreach ($this->required_attributes as $attr) {
|
||||
if (!isset($_indexed_attr[$attr])) {
|
||||
$compiler->trigger_template_error("missing '{$attr}' attribute", null, true);
|
||||
}
|
||||
}
|
||||
// check for not allowed attributes
|
||||
if ($this->optional_attributes !== ['_any']) {
|
||||
$allowedAttributes = array_fill_keys(
|
||||
array_merge(
|
||||
$this->required_attributes,
|
||||
$this->optional_attributes,
|
||||
$this->option_flags
|
||||
),
|
||||
true
|
||||
);
|
||||
foreach ($_indexed_attr as $key => $dummy) {
|
||||
if (!isset($allowedAttributes[$key]) && $key !== 0) {
|
||||
$compiler->trigger_template_error("unexpected '{$key}' attribute", null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// default 'false' for all options flags not set
|
||||
foreach ($this->option_flags as $flag) {
|
||||
if (!isset($_indexed_attr[$flag])) {
|
||||
$_indexed_attr[$flag] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $_indexed_attr;
|
||||
}
|
||||
}
|
||||
+78
-6
@@ -82,12 +82,84 @@ abstract class Base implements CompilerInterface {
|
||||
* @return array of mapped attributes for further processing
|
||||
*/
|
||||
protected function getAttributes($compiler, $attributes) {
|
||||
return (new AttributeCompiler(
|
||||
$this->required_attributes,
|
||||
$this->optional_attributes,
|
||||
$this->shorttag_order,
|
||||
$this->option_flags
|
||||
))->getAttributes($compiler, $attributes);
|
||||
$_indexed_attr = [];
|
||||
$options = array_fill_keys($this->option_flags, true);
|
||||
foreach ($attributes as $key => $mixed) {
|
||||
// shorthand ?
|
||||
if (!is_array($mixed)) {
|
||||
// options flag ?
|
||||
if (isset($options[trim($mixed, '\'"')])) {
|
||||
$_indexed_attr[trim($mixed, '\'"')] = true;
|
||||
// shorthand attribute ?
|
||||
} elseif (isset($this->shorttag_order[$key])) {
|
||||
$_indexed_attr[$this->shorttag_order[$key]] = $mixed;
|
||||
} else {
|
||||
// too many shorthands
|
||||
$compiler->trigger_template_error('too many shorthand attributes', null, true);
|
||||
}
|
||||
// named attribute
|
||||
} else {
|
||||
foreach ($mixed as $k => $v) {
|
||||
// options flag?
|
||||
if (isset($options[$k])) {
|
||||
if (is_bool($v)) {
|
||||
$_indexed_attr[$k] = $v;
|
||||
} else {
|
||||
if (is_string($v)) {
|
||||
$v = trim($v, '\'" ');
|
||||
}
|
||||
|
||||
// Mapping array for boolean option value
|
||||
static $optionMap = [1 => true, 0 => false, 'true' => true, 'false' => false];
|
||||
|
||||
if (isset($optionMap[$v])) {
|
||||
$_indexed_attr[$k] = $optionMap[$v];
|
||||
} else {
|
||||
$compiler->trigger_template_error(
|
||||
"illegal value '" . var_export($v, true) .
|
||||
"' for options flag '{$k}'",
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
// must be named attribute
|
||||
} else {
|
||||
$_indexed_attr[$k] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if all required attributes present
|
||||
foreach ($this->required_attributes as $attr) {
|
||||
if (!isset($_indexed_attr[$attr])) {
|
||||
$compiler->trigger_template_error("missing '{$attr}' attribute", null, true);
|
||||
}
|
||||
}
|
||||
// check for not allowed attributes
|
||||
if ($this->optional_attributes !== ['_any']) {
|
||||
$allowedAttributes = array_fill_keys(
|
||||
array_merge(
|
||||
$this->required_attributes,
|
||||
$this->optional_attributes,
|
||||
$this->option_flags
|
||||
),
|
||||
true
|
||||
);
|
||||
foreach ($_indexed_attr as $key => $dummy) {
|
||||
if (!isset($allowedAttributes[$key]) && $key !== 0) {
|
||||
$compiler->trigger_template_error("unexpected '{$key}' attribute", null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// default 'false' for all options flags not set
|
||||
foreach ($this->option_flags as $flag) {
|
||||
if (!isset($_indexed_attr[$flag])) {
|
||||
$_indexed_attr[$flag] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $_indexed_attr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,18 +3,24 @@
|
||||
* Smarty Internal Plugin Compile Registered Function
|
||||
* Compiles code for the execution of a registered function
|
||||
*
|
||||
|
||||
|
||||
* @author Uwe Tews
|
||||
*/
|
||||
|
||||
namespace Smarty\Compile;
|
||||
|
||||
use Smarty\Compiler\Template;
|
||||
use Smarty\FunctionHandler\AttributeFunctionHandlerInterface;
|
||||
use Smarty\CompilerException;
|
||||
|
||||
/**
|
||||
* Smarty Internal Plugin Compile Registered Function Class
|
||||
*
|
||||
|
||||
|
||||
*/
|
||||
class FunctionCallCompiler extends Base {
|
||||
|
||||
/**
|
||||
* Attribute definition: Overwrites base class.
|
||||
*
|
||||
@@ -45,27 +51,17 @@ class FunctionCallCompiler extends Base {
|
||||
*/
|
||||
public function compile($args, Template $compiler, $parameter = [], $tag = null, $function = null): string
|
||||
{
|
||||
|
||||
// check and get attributes
|
||||
$_attr = $this->getAttributes($compiler, $args);
|
||||
unset($_attr['nocache']);
|
||||
|
||||
$_paramsArray = $this->formatParamsArray($_attr);
|
||||
$_params = 'array(' . implode(',', $_paramsArray) . ')';
|
||||
|
||||
|
||||
if ($functionHandler = $compiler->getSmarty()->getFunctionHandler($function)) {
|
||||
|
||||
$attribute_overrides = [];
|
||||
|
||||
if ($functionHandler instanceof AttributeFunctionHandlerInterface) {
|
||||
$attribute_overrides = $functionHandler->getSupportedAttributes();
|
||||
}
|
||||
|
||||
// check and get attributes
|
||||
$_attr = (new AttributeCompiler(
|
||||
$attribute_overrides['required_attributes'] ?? $this->required_attributes,
|
||||
$attribute_overrides['optional_attributes'] ?? $this->optional_attributes,
|
||||
$attribute_overrides['shorttag_order'] ?? $this->shorttag_order,
|
||||
$attribute_overrides['option_flags'] ?? $this->option_flags
|
||||
))->getAttributes($compiler, $args);
|
||||
|
||||
unset($_attr['nocache']);
|
||||
|
||||
$_paramsArray = $this->formatParamsArray($_attr);
|
||||
$_params = 'array(' . implode(',', $_paramsArray) . ')';
|
||||
|
||||
// not cacheable?
|
||||
$compiler->tag_nocache = $compiler->tag_nocache || !$functionHandler->isCacheable();
|
||||
$output = "\$_smarty_tpl->getSmarty()->getFunctionHandler(" . var_export($function, true) . ")";
|
||||
|
||||
+12
-15
@@ -51,9 +51,8 @@ class ForTag extends Base {
|
||||
$var = $_statement['var'];
|
||||
$index = '';
|
||||
}
|
||||
$itemVar = "\$_smarty_tpl->getVariable({$var})";
|
||||
$output .= "\$_smarty_tpl->assign($var, []);\n";
|
||||
$output .= "{$itemVar}->value{$index} = {$_statement['value']};\n";
|
||||
$output .= "\$_smarty_tpl->assign($var, null);\n";
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->value{$index} = {$_statement['value']};\n";
|
||||
}
|
||||
if (is_array($_attr['var'])) {
|
||||
$var = $_attr['var']['var'];
|
||||
@@ -62,8 +61,7 @@ class ForTag extends Base {
|
||||
$var = $_attr['var'];
|
||||
$index = '';
|
||||
}
|
||||
$itemVar = "\$_smarty_tpl->getVariable({$var})";
|
||||
$output .= "if ($_attr[ifexp]) {\nfor (\$_foo=true;$_attr[ifexp]; {$itemVar}->value{$index}$_attr[step]) {\n";
|
||||
$output .= "if ($_attr[ifexp]) {\nfor (\$_foo=true;$_attr[ifexp]; \$_smarty_tpl->tpl_vars[$var]->value{$index}$_attr[step]) {\n";
|
||||
} else {
|
||||
$_statement = $_attr['start'];
|
||||
if (is_array($_statement['var'])) {
|
||||
@@ -73,22 +71,21 @@ class ForTag extends Base {
|
||||
$var = $_statement['var'];
|
||||
$index = '';
|
||||
}
|
||||
$itemVar = "\$_smarty_tpl->getVariable({$var})";
|
||||
$output .= "\$_smarty_tpl->assign($var, []);";
|
||||
$output .= "\$_smarty_tpl->assign($var, null);";
|
||||
if (isset($_attr['step'])) {
|
||||
$output .= "{$itemVar}->step = $_attr[step];";
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->step = $_attr[step];";
|
||||
} else {
|
||||
$output .= "{$itemVar}->step = 1;";
|
||||
$output .= "\$_smarty_tpl->tpl_vars[$var]->step = 1;";
|
||||
}
|
||||
if (isset($_attr['max'])) {
|
||||
$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";
|
||||
$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";
|
||||
} else {
|
||||
$output .= "{$itemVar}->total = (int) ceil(({$itemVar}->step > 0 ? $_attr[to]+1 - ($_statement[value]) : $_statement[value]-($_attr[to])+1)/abs({$itemVar}->step));\n";
|
||||
$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 .= "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 .= "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 .= '?>';
|
||||
|
||||
|
||||
@@ -505,7 +505,7 @@ class Template extends BaseCompiler {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function triggerTagNoCache($variable): void {
|
||||
public function compileVariable($variable) {
|
||||
if (!strpos($variable, '(')) {
|
||||
// not a variable variable
|
||||
$var = trim($variable, '\'');
|
||||
@@ -516,6 +516,7 @@ class Template extends BaseCompiler {
|
||||
false
|
||||
)->isNocache();
|
||||
}
|
||||
return '$_smarty_tpl->getValue(' . $variable . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1146,12 +1147,12 @@ class Template extends BaseCompiler {
|
||||
}
|
||||
|
||||
// check if tag is a function
|
||||
if ($this->smarty->getFunctionHandler($tag)) {
|
||||
if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
|
||||
if ($this->smarty->getFunctionHandler($base_tag)) {
|
||||
if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($base_tag, $this)) {
|
||||
return (new \Smarty\Compile\PrintExpressionCompiler())->compile(
|
||||
['nofilter'], // functions are never auto-escaped
|
||||
$this,
|
||||
['value' => $this->compileFunctionCall($tag, $args, $parameter)]
|
||||
['value' => $this->compileFunctionCall($base_tag, $args, $parameter)]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1164,16 +1165,16 @@ class Template extends BaseCompiler {
|
||||
}
|
||||
|
||||
// the default plugin handler is a handler of last resort, it may also handle not specifically registered tags.
|
||||
if ($callback = $this->getPluginFromDefaultHandler($tag, Smarty::PLUGIN_COMPILER)) {
|
||||
if ($callback = $this->getPluginFromDefaultHandler($base_tag, Smarty::PLUGIN_COMPILER)) {
|
||||
if (!empty($parameter['modifierlist'])) {
|
||||
throw new CompilerException('No modifiers allowed on ' . $tag);
|
||||
throw new CompilerException('No modifiers allowed on ' . $base_tag);
|
||||
}
|
||||
$tagCompiler = new \Smarty\Compile\Tag\BCPluginWrapper($callback);
|
||||
return $tagCompiler->compile($args, $this, $parameter);
|
||||
}
|
||||
|
||||
if ($this->getPluginFromDefaultHandler($base_tag, Smarty::PLUGIN_FUNCTION)) {
|
||||
return $this->defaultHandlerFunctionCallCompiler->compile($args, $this, $parameter, $tag, $tag);
|
||||
return $this->defaultHandlerFunctionCallCompiler->compile($args, $this, $parameter, $tag, $base_tag);
|
||||
}
|
||||
|
||||
if ($this->getPluginFromDefaultHandler($base_tag, Smarty::PLUGIN_BLOCK)) {
|
||||
|
||||
@@ -168,6 +168,7 @@ class BCPluginsAdapter extends Base {
|
||||
}
|
||||
|
||||
public function loadPluginsFromDir(string $path) {
|
||||
|
||||
foreach([
|
||||
'function',
|
||||
'modifier',
|
||||
@@ -176,7 +177,6 @@ class BCPluginsAdapter extends Base {
|
||||
'prefilter',
|
||||
'postfilter',
|
||||
'outputfilter',
|
||||
'modifiercompiler',
|
||||
] as $type) {
|
||||
foreach (glob($path . $type . '.?*.php') as $filename) {
|
||||
$pluginName = $this->getPluginNameFromFilename($filename);
|
||||
@@ -226,4 +226,4 @@ class BCPluginsAdapter extends Base {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -320,7 +320,7 @@ class DefaultExtension extends Base {
|
||||
break;
|
||||
}
|
||||
foreach ($var as $curr_key => $curr_val) {
|
||||
$results .= '<br>' . str_repeat(' ', $depth * 2) . '<b>' . htmlspecialchars(strtr($curr_key, $_replace)) .
|
||||
$results .= '<br>' . str_repeat(' ', $depth * 2) . '<b>' . strtr($curr_key, $_replace) .
|
||||
'</b> => ' .
|
||||
$this->smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects);
|
||||
$depth--;
|
||||
@@ -338,7 +338,7 @@ class DefaultExtension extends Base {
|
||||
}
|
||||
$objects[] = $var;
|
||||
foreach ($object_vars as $curr_key => $curr_val) {
|
||||
$results .= '<br>' . str_repeat(' ', $depth * 2) . '<b> ->' . htmlspecialchars(strtr($curr_key, $_replace)) .
|
||||
$results .= '<br>' . str_repeat(' ', $depth * 2) . '<b> ->' . strtr($curr_key, $_replace) .
|
||||
'</b> = ' . $this->smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects);
|
||||
$depth--;
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Smarty\FunctionHandler;
|
||||
|
||||
use Smarty\Template;
|
||||
|
||||
/**
|
||||
* Abstract implementation for function handlers which support custom attributes
|
||||
*/
|
||||
abstract class AttributeBase implements AttributeFunctionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Array of names of required attribute required by tag
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $required_attributes = [];
|
||||
|
||||
/**
|
||||
* Array of names of optional attribute required by tag
|
||||
* use array('_any') if there is no restriction of attributes names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $optional_attributes = [];
|
||||
|
||||
/**
|
||||
* Shorttag attribute order defined by its names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $shorttag_order = [];
|
||||
|
||||
/**
|
||||
* Array of names of valid option flags
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $option_flags = [];
|
||||
|
||||
/**
|
||||
* Return whether the output is cacheable.
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $cacheable = true;
|
||||
|
||||
/**
|
||||
* Return whether the output is cacheable.
|
||||
* @return bool
|
||||
*/
|
||||
public function isCacheable(): bool
|
||||
{
|
||||
return $this->cacheable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function body
|
||||
* @param mixed $params The supplied parameters.
|
||||
* @param Smarty\Template $template
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function handle($params, Template $template): ?string;
|
||||
|
||||
/**
|
||||
* Return the support attributes for this function.
|
||||
* @return array<string, array>
|
||||
*/
|
||||
public function getSupportedAttributes(): array
|
||||
{
|
||||
return [
|
||||
'required_attributes' => $this->required_attributes,
|
||||
'optional_attributes' => $this->optional_attributes,
|
||||
'shorttag_order' => $this->shorttag_order,
|
||||
'option_flags' => $this->option_flags,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Smarty\FunctionHandler;
|
||||
|
||||
/**
|
||||
* Function handler interface with support for specifying supported properties
|
||||
*/
|
||||
interface AttributeFunctionHandlerInterface extends FunctionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Returns an array with the supported attributes, flags, and shorttags
|
||||
* @return array<string, array>
|
||||
*/
|
||||
public function getSupportedAttributes(): array;
|
||||
}
|
||||
@@ -189,22 +189,7 @@ class Fetch extends Base {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
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']);
|
||||
}
|
||||
$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="' . smarty_function_escape_special_chars($_val) . '">';
|
||||
$prefix = '<a href="' . $_val . '">';
|
||||
$suffix = '</a>';
|
||||
break;
|
||||
default:
|
||||
@@ -143,12 +143,7 @@ class HtmlImage extends Base {
|
||||
$width = round($width * $_resize);
|
||||
$height = round($height * $_resize);
|
||||
}
|
||||
// $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;
|
||||
return $prefix . '<img src="' . $path_prefix . $file . '" alt="' . $alt . '" width="' . $width . '" height="' .
|
||||
$height . '"' . $extra . ' />' . $suffix;
|
||||
}
|
||||
}
|
||||
@@ -120,6 +120,9 @@ 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':
|
||||
@@ -137,13 +140,6 @@ 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':
|
||||
|
||||
+38
-44
@@ -160,7 +160,6 @@ class TemplateLexer
|
||||
'LOGOP' => '"<", "==" ... logical operator',
|
||||
'TLOGOP' => '"lt", "eq" ... logical operator; "is div by" ... if condition',
|
||||
'SCOND' => '"is even" ... if condition',
|
||||
'MATCHES' => '"matches" regex operator',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -568,7 +567,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+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");
|
||||
$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");
|
||||
}
|
||||
if (!isset($this->dataLength)) {
|
||||
$this->dataLength = strlen($this->data);
|
||||
@@ -663,124 +662,119 @@ class TemplateLexer
|
||||
public function yy_r3_10()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_MATCHES;
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_AS;
|
||||
}
|
||||
public function yy_r3_11()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_AS;
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TO;
|
||||
}
|
||||
public function yy_r3_12()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TO;
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_STEP;
|
||||
}
|
||||
public function yy_r3_13()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_STEP;
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_INSTANCEOF;
|
||||
}
|
||||
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_17()
|
||||
public function yy_r3_16()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_SLOGOP;
|
||||
}
|
||||
public function yy_r3_19()
|
||||
public function yy_r3_18()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TLOGOP;
|
||||
}
|
||||
public function yy_r3_22()
|
||||
public function yy_r3_21()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_SINGLECOND;
|
||||
}
|
||||
public function yy_r3_25()
|
||||
public function yy_r3_24()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_NOT;
|
||||
}
|
||||
public function yy_r3_26()
|
||||
public function yy_r3_25()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TYPECAST;
|
||||
}
|
||||
public function yy_r3_30()
|
||||
public function yy_r3_29()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_OPENP;
|
||||
}
|
||||
public function yy_r3_31()
|
||||
public function yy_r3_30()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_CLOSEP;
|
||||
}
|
||||
public function yy_r3_32()
|
||||
public function yy_r3_31()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_OPENB;
|
||||
}
|
||||
public function yy_r3_33()
|
||||
public function yy_r3_32()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_CLOSEB;
|
||||
}
|
||||
public function yy_r3_34()
|
||||
public function yy_r3_33()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_PTR;
|
||||
}
|
||||
public function yy_r3_35()
|
||||
public function yy_r3_34()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_APTR;
|
||||
}
|
||||
public function yy_r3_36()
|
||||
public function yy_r3_35()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_EQUAL;
|
||||
}
|
||||
public function yy_r3_37()
|
||||
public function yy_r3_36()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_INCDEC;
|
||||
}
|
||||
public function yy_r3_39()
|
||||
public function yy_r3_38()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_UNIMATH;
|
||||
}
|
||||
public function yy_r3_41()
|
||||
public function yy_r3_40()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_MATH;
|
||||
}
|
||||
public function yy_r3_43()
|
||||
public function yy_r3_42()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_AT;
|
||||
}
|
||||
public function yy_r3_44()
|
||||
public function yy_r3_43()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_ARRAYOPEN;
|
||||
}
|
||||
public function yy_r3_45()
|
||||
public function yy_r3_44()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_HATCH;
|
||||
}
|
||||
public function yy_r3_46()
|
||||
public function yy_r3_45()
|
||||
{
|
||||
|
||||
// resolve conflicts with shorttag and right_delimiter starting with '='
|
||||
@@ -792,73 +786,73 @@ class TemplateLexer
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_ATTR;
|
||||
}
|
||||
}
|
||||
public function yy_r3_47()
|
||||
public function yy_r3_46()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_NAMESPACE;
|
||||
}
|
||||
public function yy_r3_50()
|
||||
public function yy_r3_49()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_ID;
|
||||
}
|
||||
public function yy_r3_51()
|
||||
public function yy_r3_50()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_INTEGER;
|
||||
}
|
||||
public function yy_r3_52()
|
||||
public function yy_r3_51()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_BACKTICK;
|
||||
$this->yypopstate();
|
||||
}
|
||||
public function yy_r3_53()
|
||||
public function yy_r3_52()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_VERT;
|
||||
}
|
||||
public function yy_r3_54()
|
||||
public function yy_r3_53()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_DOT;
|
||||
}
|
||||
public function yy_r3_55()
|
||||
public function yy_r3_54()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_COMMA;
|
||||
}
|
||||
public function yy_r3_56()
|
||||
public function yy_r3_55()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_SEMICOLON;
|
||||
}
|
||||
public function yy_r3_57()
|
||||
public function yy_r3_56()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_DOUBLECOLON;
|
||||
}
|
||||
public function yy_r3_58()
|
||||
public function yy_r3_57()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_COLON;
|
||||
}
|
||||
public function yy_r3_59()
|
||||
public function yy_r3_58()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_QMARK;
|
||||
}
|
||||
public function yy_r3_60()
|
||||
public function yy_r3_59()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_HEX;
|
||||
}
|
||||
public function yy_r3_61()
|
||||
public function yy_r3_60()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_SPACE;
|
||||
}
|
||||
public function yy_r3_62()
|
||||
public function yy_r3_61()
|
||||
{
|
||||
|
||||
$this->token = \Smarty\Parser\TemplateParser::TP_TEXT;
|
||||
|
||||
@@ -160,7 +160,6 @@ class TemplateLexer
|
||||
'LOGOP' => '"<", "==" ... logical operator',
|
||||
'TLOGOP' => '"lt", "eq" ... logical operator; "is div by" ... if condition',
|
||||
'SCOND' => '"is even" ... if condition',
|
||||
'MATCHES' => '"matches" regex operator',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -326,7 +325,6 @@ 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+~
|
||||
@@ -471,9 +469,6 @@ 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;
|
||||
}
|
||||
|
||||
+1150
-1347
File diff suppressed because it is too large
Load Diff
+32
-138
@@ -19,7 +19,7 @@ use \Smarty\ParseTree\Code;
|
||||
use \Smarty\ParseTree\Dq;
|
||||
use \Smarty\ParseTree\DqContent;
|
||||
use \Smarty\ParseTree\Tag;
|
||||
use \Smarty\CompilerException;
|
||||
|
||||
|
||||
/**
|
||||
* Smarty Template Parser Class
|
||||
@@ -306,8 +306,7 @@ smartytag(A) ::= SIMPELOUTPUT(B). {
|
||||
$attributes[] = 'nocache';
|
||||
$var = $match[1];
|
||||
}
|
||||
$this->compiler->triggerTagNoCache($var);
|
||||
A = $this->compiler->compilePrintExpression('$_smarty_tpl->getValue(\''.$var.'\')', $attributes);
|
||||
A = $this->compiler->compilePrintExpression($this->compiler->compileVariable('\''.$var.'\''), $attributes);
|
||||
}
|
||||
|
||||
// simple tag like {name}
|
||||
@@ -376,7 +375,7 @@ outattr(A) ::= output(B) attributes(C). {
|
||||
A = array(B,C);
|
||||
}
|
||||
|
||||
output(A) ::= variablevalue(B). {
|
||||
output(A) ::= variable(B). {
|
||||
A = B;
|
||||
}
|
||||
output(A) ::= value(B). {
|
||||
@@ -681,11 +680,6 @@ 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;
|
||||
@@ -695,8 +689,7 @@ nullcoalescing(res) ::= expr(v) QMARK QMARK expr(e2). {
|
||||
// ternary
|
||||
//
|
||||
ternary(res) ::= expr(v) QMARK DOLLARID(e1) COLON expr(e2). {
|
||||
$this->compiler->triggerTagNoCache(substr(e1,1));
|
||||
res = v.' ? $_smarty_tpl->getValue(\''.substr(e1,1).'\') : '.e2;
|
||||
res = v.' ? '. $this->compiler->compileVariable('\''.substr(e1,1).'\'') . ' : '.e2;
|
||||
}
|
||||
|
||||
ternary(res) ::= expr(v) QMARK value(e1) COLON expr(e2). {
|
||||
@@ -713,7 +706,7 @@ ternary(res) ::= expr(v) QMARK COLON expr(e2). {
|
||||
}
|
||||
|
||||
// value
|
||||
value(res) ::= variablevalue(v). {
|
||||
value(res) ::= variable(v). {
|
||||
res = v;
|
||||
}
|
||||
|
||||
@@ -731,7 +724,7 @@ value(res) ::= TYPECAST(t) value(v). {
|
||||
res = t.v;
|
||||
}
|
||||
|
||||
value(res) ::= variablevalue(v) INCDEC(o). {
|
||||
value(res) ::= variable(v) INCDEC(o). {
|
||||
res = v.o;
|
||||
}
|
||||
|
||||
@@ -778,10 +771,10 @@ value(res) ::= OPENP expr(e) CLOSEP. {
|
||||
res = '('. e .')';
|
||||
}
|
||||
|
||||
value(res) ::= variablevalue(v1) INSTANCEOF(i) ns1(v2). {
|
||||
value(res) ::= variable(v1) INSTANCEOF(i) ns1(v2). {
|
||||
res = v1.i.v2;
|
||||
}
|
||||
value(res) ::= variablevalue(v1) INSTANCEOF(i) variablevalue(v2). {
|
||||
value(res) ::= variable(v1) INSTANCEOF(i) variable(v2). {
|
||||
res = v1.i.v2;
|
||||
}
|
||||
|
||||
@@ -804,8 +797,7 @@ value(res) ::= varindexed(vi) DOUBLECOLON static_class_access(r). {
|
||||
if (vi['var'] === '\'smarty\'') {
|
||||
$this->compiler->appendPrefixCode("<?php {$prefixVar} = ". (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,vi['smarty_internal_index']).';?>');
|
||||
} else {
|
||||
$this->compiler->triggerTagNoCache(vi['var']);
|
||||
$this->compiler->appendPrefixCode("<?php {$prefixVar} = \$_smarty_tpl->getValue(" . vi['var'] . ')'.vi['smarty_internal_index'].';?>');
|
||||
$this->compiler->appendPrefixCode("<?php {$prefixVar} = ". $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index'].';?>');
|
||||
}
|
||||
res = $prefixVar .'::'.r[0].r[1];
|
||||
}
|
||||
@@ -855,88 +847,54 @@ ns1(res) ::= NAMESPACE(i). {
|
||||
}
|
||||
|
||||
|
||||
// variable lists
|
||||
|
||||
// multiple variables
|
||||
variablelist(res) ::= variablelist(l) COMMA variable(v). {
|
||||
res = array_merge(l,array(v));
|
||||
}
|
||||
|
||||
variablelist(res) ::= variablelist(l) COMMA expr(e). {
|
||||
res = array_merge(l,array(e));
|
||||
}
|
||||
|
||||
// single variable
|
||||
variablelist(res) ::= variable(v). {
|
||||
res = array(v);
|
||||
}
|
||||
|
||||
// single expression
|
||||
variablelist(res) ::= expr(e). {
|
||||
res = array(e);
|
||||
}
|
||||
|
||||
// no variable
|
||||
variablelist(res) ::= . {
|
||||
res = array();
|
||||
}
|
||||
|
||||
//
|
||||
// variables
|
||||
//
|
||||
// Smarty variable (optional array)
|
||||
variable(res) ::= DOLLARID(i). {
|
||||
$this->compiler->triggerTagNoCache(substr(i,1));
|
||||
res = array('$_smarty_tpl->hasVariable(\''.substr(i,1).'\')','$_smarty_tpl->getValue(\''.substr(i,1).'\')');
|
||||
res = $this->compiler->compileVariable('\''.substr(i,1).'\'');
|
||||
}
|
||||
variable(res) ::= varindexed(vi). {
|
||||
if (vi['var'] === '\'smarty\'') {
|
||||
$smarty_var = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,vi['smarty_internal_index']);
|
||||
res = array('true', $smarty_var);
|
||||
res = $smarty_var;
|
||||
} else {
|
||||
// used for array reset,next,prev,end,current
|
||||
$this->last_variable = vi['var'];
|
||||
$this->last_index = vi['smarty_internal_index'];
|
||||
$this->compiler->triggerTagNoCache(vi['var']);
|
||||
res = array('true', '$_smarty_tpl->getValue(' . vi['var'] . ')'.vi['smarty_internal_index']);
|
||||
res = $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index'];
|
||||
}
|
||||
}
|
||||
|
||||
// variable with property
|
||||
variable(res) ::= varvar(v) AT ID(p). {
|
||||
res = array('true', '$_smarty_tpl->getVariable('. v .')->'.p);
|
||||
res = '$_smarty_tpl->getVariable('. v .')->'.p;
|
||||
}
|
||||
|
||||
// object
|
||||
variable(res) ::= object(o). {
|
||||
res = array('true', o);
|
||||
res = o;
|
||||
}
|
||||
|
||||
// config variable
|
||||
configvariable(res) ::= HATCH ID(i) HATCH. {
|
||||
variable(res) ::= HATCH ID(i) HATCH. {
|
||||
res = $this->compiler->compileConfigVariable('\'' . i . '\'');
|
||||
}
|
||||
|
||||
configvariable(res) ::= HATCH ID(i) HATCH arrayindex(a). {
|
||||
variable(res) ::= HATCH ID(i) HATCH arrayindex(a). {
|
||||
res = '(is_array($tmp = ' . $this->compiler->compileConfigVariable('\'' . i . '\'') . ') ? $tmp'.a.' :null)';
|
||||
}
|
||||
|
||||
configvariable(res) ::= HATCH variablevalue(v) HATCH. {
|
||||
variable(res) ::= HATCH variable(v) HATCH. {
|
||||
res = $this->compiler->compileConfigVariable(v);
|
||||
}
|
||||
|
||||
configvariable(res) ::= HATCH variablevalue(v) HATCH arrayindex(a). {
|
||||
variable(res) ::= HATCH variable(v) HATCH arrayindex(a). {
|
||||
res = '(is_array($tmp = ' . $this->compiler->compileConfigVariable(v) . ') ? $tmp'.a.' : null)';
|
||||
}
|
||||
|
||||
variablevalue(res) ::= variable(v). {
|
||||
res = v[1];
|
||||
}
|
||||
|
||||
variablevalue(res) ::= configvariable(v). {
|
||||
res = v;
|
||||
}
|
||||
|
||||
varindexed(res) ::= DOLLARID(i) arrayindex(a). {
|
||||
res = array('var'=>'\''.substr(i,1).'\'', 'smarty_internal_index'=>a);
|
||||
}
|
||||
@@ -960,17 +918,14 @@ arrayindex ::= . {
|
||||
// single index definition
|
||||
// Smarty2 style index
|
||||
indexdef(res) ::= DOT DOLLARID(i). {
|
||||
$this->compiler->triggerTagNoCache(substr(i,1));
|
||||
res = '[$_smarty_tpl->getValue(\''.substr(i,1).'\')]';
|
||||
res = '['.$this->compiler->compileVariable('\''.substr(i,1).'\'').']';
|
||||
}
|
||||
indexdef(res) ::= DOT varvar(v). {
|
||||
$this->compiler->triggerTagNoCache(v);
|
||||
res = '[$_smarty_tpl->getValue(' . v . ')]';
|
||||
res = '['.$this->compiler->compileVariable(v).']';
|
||||
}
|
||||
|
||||
indexdef(res) ::= DOT varvar(v) AT ID(p). {
|
||||
$this->compiler->triggerTagNoCache(v);
|
||||
res = '[$_smarty_tpl->getValue(' . v . ')->'.p.']';
|
||||
res = '['.$this->compiler->compileVariable(v).'->'.p.']';
|
||||
}
|
||||
|
||||
indexdef(res) ::= DOT ID(i). {
|
||||
@@ -1001,10 +956,9 @@ indexdef(res) ::= OPENB INTEGER(n) CLOSEB. {
|
||||
res = '['.n.']';
|
||||
}
|
||||
indexdef(res) ::= OPENB DOLLARID(i) CLOSEB. {
|
||||
$this->compiler->triggerTagNoCache(substr(i,1));
|
||||
res = '[$_smarty_tpl->getValue(\''.substr(i,1).'\')]';
|
||||
res = '['.$this->compiler->compileVariable('\''.substr(i,1).'\'').']';
|
||||
}
|
||||
indexdef(res) ::= OPENB variablevalue(v) CLOSEB. {
|
||||
indexdef(res) ::= OPENB variable(v) CLOSEB. {
|
||||
res = '['.v.']';
|
||||
}
|
||||
indexdef(res) ::= OPENB value(v) CLOSEB. {
|
||||
@@ -1046,8 +1000,7 @@ varvarele(res) ::= ID(s). {
|
||||
}
|
||||
varvarele(res) ::= SIMPELOUTPUT(i). {
|
||||
$var = trim(substr(i, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $');
|
||||
$this->compiler->triggerTagNoCache($var);
|
||||
res = '$_smarty_tpl->getValue(\''.$var.'\')';
|
||||
res = $this->compiler->compileVariable('\''.$var.'\'');
|
||||
}
|
||||
|
||||
// variable sections of element
|
||||
@@ -1062,27 +1015,16 @@ object(res) ::= varindexed(vi) objectchain(oc). {
|
||||
if (vi['var'] === '\'smarty\'') {
|
||||
res = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,vi['smarty_internal_index']).oc;
|
||||
} else {
|
||||
$this->compiler->triggerTagNoCache(vi['var']);
|
||||
res = '$_smarty_tpl->getValue(' . vi['var'] . ')'.vi['smarty_internal_index'].oc;
|
||||
res = $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index'].oc;
|
||||
}
|
||||
}
|
||||
|
||||
// optional objectchain - empty
|
||||
optobjectchain(res) ::= . {
|
||||
res = '';
|
||||
}
|
||||
|
||||
// optional objectchain - present
|
||||
optobjectchain(res) ::= objectchain(oc). {
|
||||
res = oc;
|
||||
}
|
||||
|
||||
// single element
|
||||
objectchain(res) ::= objectelement(oe). {
|
||||
res = oe;
|
||||
}
|
||||
|
||||
// chain of elements
|
||||
// chain of elements
|
||||
objectchain(res) ::= objectchain(oc) objectelement(oe). {
|
||||
res = oc.oe;
|
||||
}
|
||||
@@ -1099,8 +1041,7 @@ objectelement(res)::= PTR varvar(v) arrayindex(a). {
|
||||
if ($this->security) {
|
||||
$this->compiler->trigger_template_error (self::ERR2);
|
||||
}
|
||||
$this->compiler->triggerTagNoCache(v);
|
||||
res = '->{$_smarty_tpl->getValue(' . v . ')'.a.'}';
|
||||
res = '->{'.$this->compiler->compileVariable(v).a.'}';
|
||||
}
|
||||
|
||||
objectelement(res)::= PTR LDEL expr(e) RDEL arrayindex(a). {
|
||||
@@ -1126,41 +1067,8 @@ objectelement(res)::= PTR method(f). {
|
||||
//
|
||||
// function
|
||||
//
|
||||
function(res) ::= ns1(f) OPENP variablelist(v) CLOSEP optobjectchain(oc). {
|
||||
|
||||
if (f == 'isset') {
|
||||
res = '(true';
|
||||
if (count(v) == 0) {
|
||||
throw new CompilerException("Invalid number of arguments for isset. isset expects at least one parameter.");
|
||||
}
|
||||
foreach (v as $value) {
|
||||
if (is_array($value)) {
|
||||
res .= ' && (' . $value[0] . ' && null !== (' . $value[1] . ' ?? null))';
|
||||
} else {
|
||||
res .= ' && (' . $value . ' !== null)';
|
||||
}
|
||||
}
|
||||
res .= ')' . oc;
|
||||
} elseif (f == 'empty') {
|
||||
if (count(v) != 1) {
|
||||
throw new CompilerException("Invalid number of arguments for empty. empty expects at exactly one parameter.");
|
||||
}
|
||||
if (is_array(v[0])) {
|
||||
res = '( !' . v[0][0] . ' || empty(' . v[0][1] . '))' . oc;
|
||||
} else {
|
||||
res = 'false == ' . v[0] . oc;
|
||||
}
|
||||
} else {
|
||||
$p = array();
|
||||
foreach (v as $value) {
|
||||
if (is_array($value)) {
|
||||
$p[] = $value[1];
|
||||
} else {
|
||||
$p[] = $value;
|
||||
}
|
||||
}
|
||||
res = $this->compiler->compileModifierInExpression(f, $p) . oc;
|
||||
}
|
||||
function(res) ::= ns1(f) OPENP params(p) CLOSEP. {
|
||||
res = $this->compiler->compileModifierInExpression(f, p);
|
||||
}
|
||||
|
||||
|
||||
@@ -1179,8 +1087,7 @@ method(res) ::= DOLLARID(f) OPENP params(p) CLOSEP. {
|
||||
$this->compiler->trigger_template_error (self::ERR2);
|
||||
}
|
||||
$prefixVar = $this->compiler->getNewPrefixVariable();
|
||||
$this->compiler->triggerTagNoCache(substr(f,1));
|
||||
$this->compiler->appendPrefixCode("<?php {$prefixVar} = \$_smarty_tpl->getValue('".substr(f,1).'\')'.';?>');
|
||||
$this->compiler->appendPrefixCode("<?php {$prefixVar} = ".$this->compiler->compileVariable('\''.substr(f,1).'\'').';?>');
|
||||
res = $prefixVar .'('. implode(',',p) .')';
|
||||
}
|
||||
|
||||
@@ -1259,11 +1166,6 @@ 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');
|
||||
@@ -1323,10 +1225,6 @@ scond(res) ::= SINGLECOND(o). {
|
||||
res = $scond[$op];
|
||||
}
|
||||
|
||||
matchop(res) ::= MATCHES(o). {
|
||||
res = 'preg_match(';
|
||||
}
|
||||
|
||||
//
|
||||
// ARRAY element assignment
|
||||
//
|
||||
@@ -1345,10 +1243,6 @@ arrayelements(res) ::= arrayelements(a1) COMMA arrayelement(a). {
|
||||
res = a1.','.a;
|
||||
}
|
||||
|
||||
arrayelements(res) ::= arrayelements(a) COMMA. {
|
||||
res = a.',';
|
||||
}
|
||||
|
||||
arrayelements ::= . {
|
||||
return;
|
||||
}
|
||||
@@ -1388,7 +1282,7 @@ doublequoted(res) ::= doublequotedcontent(o). {
|
||||
res = new Dq($this, o);
|
||||
}
|
||||
|
||||
doublequotedcontent(res) ::= BACKTICK variablevalue(v) BACKTICK. {
|
||||
doublequotedcontent(res) ::= BACKTICK variable(v) BACKTICK. {
|
||||
res = new Code('(string)'.v);
|
||||
}
|
||||
|
||||
@@ -1400,7 +1294,7 @@ doublequotedcontent(res) ::= DOLLARID(i). {
|
||||
res = new Code('(string)$_smarty_tpl->getValue(\''. substr(i,1) .'\')');
|
||||
}
|
||||
|
||||
doublequotedcontent(res) ::= LDEL variablevalue(v) RDEL. {
|
||||
doublequotedcontent(res) ::= LDEL variable(v) RDEL. {
|
||||
res = new Code('(string)'.v);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,19 +54,8 @@ 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) {
|
||||
|
||||
@@ -31,7 +31,7 @@ class StringEval extends RecompiledPlugin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function populate(\Smarty\Template\Source $source, ?\Smarty\Template $_template = null)
|
||||
public function populate(\Smarty\Template\Source $source, \Smarty\Template $_template = null)
|
||||
{
|
||||
$source->uid = sha1($source->name);
|
||||
$source->timestamp = $source->exists = true;
|
||||
|
||||
+4
-30
@@ -52,7 +52,7 @@ class Security {
|
||||
/**
|
||||
* This is an array of trusted static classes.
|
||||
* If empty access to all static classes is allowed.
|
||||
* To disable access to all static classes set $static_classes = null.
|
||||
* If set to 'none' none is allowed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@@ -206,11 +206,7 @@ class Security {
|
||||
* @return boolean true if class is trusted
|
||||
*/
|
||||
public function isTrustedStaticClass($class_name, $compiler) {
|
||||
// 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)
|
||||
if (isset($this->static_classes)
|
||||
&& (empty($this->static_classes) || in_array($class_name, $this->static_classes))
|
||||
) {
|
||||
return true;
|
||||
@@ -478,34 +474,12 @@ class Security {
|
||||
* @throws \Smarty\Exception
|
||||
*/
|
||||
private function _checkDir($filepath, $dirs) {
|
||||
// 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 = dirname($this->smarty->_realpath($filepath, true)) . DIRECTORY_SEPARATOR;
|
||||
$_directory = [];
|
||||
if (!preg_match('#[\\\\/][.][.][\\\\/]#', $directory)) {
|
||||
while (true) {
|
||||
// test if the directory is trusted
|
||||
if (isset($trusted[$directory])) {
|
||||
if (isset($dirs[$directory])) {
|
||||
return $_directory;
|
||||
}
|
||||
// abort if we've reached root
|
||||
|
||||
+7
-3
@@ -54,7 +54,7 @@ class Smarty extends \Smarty\TemplateBase {
|
||||
/**
|
||||
* smarty version
|
||||
*/
|
||||
const SMARTY_VERSION = '5.8.3';
|
||||
const SMARTY_VERSION = '5.4.2';
|
||||
|
||||
/**
|
||||
* define caching modes
|
||||
@@ -1344,8 +1344,10 @@ class Smarty extends \Smarty\TemplateBase {
|
||||
}
|
||||
$_filepath = (string)$_file;
|
||||
if ($_file->isDir()) {
|
||||
// delete folder if empty
|
||||
@rmdir($_file->getPathname());
|
||||
if (!$_compile->isDot()) {
|
||||
// delete folder if empty
|
||||
@rmdir($_file->getPathname());
|
||||
}
|
||||
} else {
|
||||
// delete only php files
|
||||
if (substr($_filepath, -4) !== '.php') {
|
||||
@@ -1383,6 +1385,8 @@ class Smarty extends \Smarty\TemplateBase {
|
||||
&& (!function_exists('ini_get') || strlen(ini_get('opcache.restrict_api')) < 1)
|
||||
) {
|
||||
opcache_invalidate($_filepath, true);
|
||||
} elseif (function_exists('apc_delete_file')) {
|
||||
apc_delete_file($_filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-5
@@ -260,11 +260,7 @@ class Template extends TemplateBase {
|
||||
|
||||
$tpl = $this->smarty->doCreateTemplate($template_name, $cache_id, $compile_id, $this, $caching, $cache_lifetime);
|
||||
|
||||
// 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;
|
||||
$tpl->inheritance = $this->getInheritance(); // re-use the same Inheritance object inside the inheritance tree
|
||||
|
||||
if ($scope) {
|
||||
$tpl->defaultScope = $scope;
|
||||
|
||||
@@ -251,6 +251,8 @@ class Compiled extends GeneratedPhpFile {
|
||||
&& (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1)
|
||||
) {
|
||||
opcache_invalidate($this->filepath, true);
|
||||
} elseif (function_exists('apc_compile_file')) {
|
||||
apc_compile_file($this->filepath);
|
||||
}
|
||||
}
|
||||
if (defined('HHVM_VERSION')) {
|
||||
|
||||
@@ -104,7 +104,7 @@ abstract class TemplateBase extends Data {
|
||||
}
|
||||
// register the object
|
||||
$smarty->registered_objects[$object_name] =
|
||||
[$object, (array)$allowed_methods_properties, (bool)$format, (array)$block_methods];
|
||||
[$object, (array)$allowed_methods_properties, (boolean)$format, (array)$block_methods];
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Smarty {$smarty.version} Debug Console
|
||||
<h1>Smarty {\Smarty\Smarty::SMARTY_VERSION} Debug Console
|
||||
- {if isset($template_name)}{$template_name|debug_print_var nofilter} {/if}{if !empty($template_data)}Total Time {$execution_time|string_format:"%.5f"}{/if}</h1>
|
||||
|
||||
{if !empty($template_data)}
|
||||
|
||||
@@ -15,3 +15,4 @@ if (!ini_get('date.timezone')) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
/*
|
||||
* Smarty PHPUnit Config
|
||||
*/
|
||||
define('individualFolders', true);
|
||||
define('MysqlCacheEnable', false);
|
||||
define('PdoCacheEnable', false);
|
||||
define('PdoGzipCacheEnable', false);
|
||||
|
||||
+47
-129
@@ -49,37 +49,6 @@ 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
|
||||
*
|
||||
@@ -106,37 +75,8 @@ class PHPUnit_Smarty extends PHPUnit\Framework\TestCase
|
||||
*/
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
// 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);
|
||||
//self::$pdo = null;
|
||||
self::$testNumber = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,46 +89,12 @@ 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
|
||||
*
|
||||
@@ -196,37 +102,47 @@ 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(self::$tempBase . 'templates_c')) {
|
||||
mkdir(self::$tempBase . 'templates_c', 0775, true);
|
||||
if (!is_dir($dir . '/templates')) {
|
||||
mkdir($dir . '/templates');
|
||||
}
|
||||
if (!is_dir(self::$tempBase . 'cache')) {
|
||||
mkdir(self::$tempBase . 'cache', 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 . 'templates_tmp')) {
|
||||
mkdir(self::$tempBase . 'templates_tmp', 0775, true);
|
||||
}
|
||||
self::$init = false;
|
||||
}
|
||||
clearstatcache();
|
||||
|
||||
// instance Smarty class
|
||||
$this->smarty = new \Smarty\Smarty();
|
||||
$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();
|
||||
if (individualFolders != 'true') {
|
||||
$this->smarty->setCompileDir(__DIR__ . '/templates_c');
|
||||
$this->smarty->setCacheDir(__DIR__ . '/cache');
|
||||
}
|
||||
self::$testNumber++;
|
||||
|
||||
}
|
||||
|
||||
@@ -314,24 +230,24 @@ KEY `name` (`name`)
|
||||
{
|
||||
$this->cleanCompileDir();
|
||||
$this->cleanCacheDir();
|
||||
$templatesTmpDir = self::getTempBase() . 'templates_tmp';
|
||||
if (is_dir($templatesTmpDir)) {
|
||||
$this->cleanDir($templatesTmpDir);
|
||||
if (is_dir(self::$cwd . '/templates_tmp')) {
|
||||
$this->cleanDir(self::$cwd . '/templates_tmp');
|
||||
}
|
||||
}
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make temporary template file
|
||||
*
|
||||
*/
|
||||
protected function makeTemplateFile($name, $code)
|
||||
public function makeTemplateFile($name, $code)
|
||||
{
|
||||
file_put_contents(self::getTempBase() . 'templates_tmp' . '/' . $name, $code);
|
||||
}
|
||||
|
||||
protected function removeTemplateFile($name)
|
||||
{
|
||||
unlink(self::getTempBase() . 'templates_tmp' . '/' . $name);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,8 +291,10 @@ KEY `name` (`name`)
|
||||
}
|
||||
// directory ?
|
||||
if ($file->isDir()) {
|
||||
// delete folder if empty
|
||||
@rmdir($file->getPathname());
|
||||
if (!$ri->isDot()) {
|
||||
// delete folder if empty
|
||||
@rmdir($file->getPathname());
|
||||
}
|
||||
} else {
|
||||
unlink($file->getPathname());
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ class TemplateNormalizationTest extends PHPUnit_Smarty
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->smarty->setTemplateDir(__DIR__ . '/templates');
|
||||
}
|
||||
|
||||
public function testGetTemplateDir()
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -18,6 +18,10 @@ class UndefinedTemplateVarTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
/**
|
||||
* Test Error suppression template fetched by Smarty object
|
||||
*/
|
||||
@@ -61,34 +65,15 @@ class UndefinedTemplateVarTest extends PHPUnit_Smarty
|
||||
*/
|
||||
public function testError()
|
||||
{
|
||||
$this->smarty->error_unassigned = true;
|
||||
$this->smarty->error_unassigned = true;
|
||||
$this->expectException(PHPUnit\Framework\Error\Error::class);
|
||||
$this->expectExceptionMessage('Undefined ');
|
||||
$this->expectExceptionMessage('Undefined ');
|
||||
$e1 = error_reporting();
|
||||
$this->assertEquals('undefined = ', $this->smarty->fetch('001_main.tpl'));
|
||||
$e2 = error_reporting();
|
||||
$this->assertEquals($e1, $e2);
|
||||
}
|
||||
|
||||
public function testNoError()
|
||||
{
|
||||
$this->smarty->error_unassigned = false;
|
||||
$e1 = error_reporting();
|
||||
$this->assertEquals('undefined = ', $this->smarty->fetch('001_main.tpl'));
|
||||
$e2 = error_reporting();
|
||||
$this->assertEquals($e1, $e2);
|
||||
}
|
||||
|
||||
public function testNoErrorForIssetOrEmpty()
|
||||
{
|
||||
$this->smarty->error_unassigned = true;
|
||||
$e1 = error_reporting();
|
||||
$this->assertEquals('undefined = ', $this->smarty->fetch('001_isset.tpl'));
|
||||
$this->assertEquals('undefined = ', $this->smarty->fetch('001_empty.tpl'));
|
||||
$e2 = error_reporting();
|
||||
$this->assertEquals($e1, $e2);
|
||||
}
|
||||
|
||||
public function testUndefinedSimpleVar() {
|
||||
$this->smarty->muteUndefinedOrNullWarnings();
|
||||
$tpl = $this->smarty->createTemplate('string:a{if $undef}def{/if}b');
|
||||
@@ -148,30 +133,30 @@ class UndefinedTemplateVarTest extends PHPUnit_Smarty
|
||||
}
|
||||
|
||||
|
||||
public function testDereferenceOnNull() {
|
||||
$this->smarty->setErrorReporting(E_ALL & ~E_WARNING & ~E_NOTICE);
|
||||
$this->smarty->muteUndefinedOrNullWarnings();
|
||||
$tpl = $this->smarty->createTemplate('string:a{if $object->myprop}def{/if}b');
|
||||
$this->smarty->assign('object', null);
|
||||
$this->assertEquals("ab", $this->smarty->fetch($tpl));
|
||||
}
|
||||
public function testDereferenceOnNull() {
|
||||
$this->smarty->setErrorReporting(E_ALL & ~E_WARNING & ~E_NOTICE);
|
||||
$this->smarty->muteUndefinedOrNullWarnings();
|
||||
$tpl = $this->smarty->createTemplate('string:a{if $object->myprop}def{/if}b');
|
||||
$this->smarty->assign('object', null);
|
||||
$this->assertEquals("ab", $this->smarty->fetch($tpl));
|
||||
}
|
||||
|
||||
|
||||
public function testDereferenceOnBool() {
|
||||
$this->smarty->setErrorReporting(E_ALL & ~E_NOTICE);
|
||||
$this->smarty->muteUndefinedOrNullWarnings();
|
||||
$tpl = $this->smarty->createTemplate('string:a{if $object->myprop}def{/if}b');
|
||||
$this->smarty->assign('object', false);
|
||||
$this->assertEquals("ab", $this->smarty->fetch($tpl));
|
||||
}
|
||||
public function testDereferenceOnBool() {
|
||||
$this->smarty->setErrorReporting(E_ALL & ~E_NOTICE);
|
||||
$this->smarty->muteUndefinedOrNullWarnings();
|
||||
$tpl = $this->smarty->createTemplate('string:a{if $object->myprop}def{/if}b');
|
||||
$this->smarty->assign('object', false);
|
||||
$this->assertEquals("ab", $this->smarty->fetch($tpl));
|
||||
}
|
||||
|
||||
|
||||
public function testDereferenceOnString() {
|
||||
$this->smarty->setErrorReporting(E_ALL & ~E_NOTICE);
|
||||
$this->smarty->muteUndefinedOrNullWarnings();
|
||||
$tpl = $this->smarty->createTemplate('string:a{if $object->myprop}def{/if}b');
|
||||
$this->smarty->assign('object', 'xyz');
|
||||
$this->assertEquals("ab", $this->smarty->fetch($tpl));
|
||||
}
|
||||
public function testDereferenceOnString() {
|
||||
$this->smarty->setErrorReporting(E_ALL & ~E_NOTICE);
|
||||
$this->smarty->muteUndefinedOrNullWarnings();
|
||||
$tpl = $this->smarty->createTemplate('string:a{if $object->myprop}def{/if}b');
|
||||
$this->smarty->assign('object', 'xyz');
|
||||
$this->assertEquals("ab", $this->smarty->fetch($tpl));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -1 +0,0 @@
|
||||
undefined = {if empty($foo)}{/if}
|
||||
@@ -1 +0,0 @@
|
||||
undefined = {if isset($foo)}{/if}
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -21,6 +21,10 @@ class FilterTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test loaded filter
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -20,6 +20,10 @@ class GetterSetterTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test setter on Smarty object
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -24,6 +24,10 @@ 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'));
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -21,6 +21,10 @@ class HttpModifiedSinceTest extends PHPUnit_Smarty
|
||||
$this->setUpSmarty(__DIR__);
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Smarty PHPunit tests for cache resource Apc
|
||||
*
|
||||
|
||||
* @author Uwe Tews
|
||||
*/
|
||||
include_once __DIR__ . '/../Memcache/CacheResourceCustomMemcacheTest.php';
|
||||
include_once __DIR__ . '/../_shared/PHPunitplugins/cacheresource.apctest.php';
|
||||
|
||||
/**
|
||||
* class for cache resource file tests
|
||||
*
|
||||
*
|
||||
* @preserveGlobalState disabled
|
||||
*
|
||||
*/
|
||||
class CacheResourceCustomApcTest extends CacheResourceCustomMemcacheTest
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
if (!function_exists('apc_cache_info') || ini_get('apc.enable_cli')) {
|
||||
$this->markTestSkipped('APC cache not available');
|
||||
}
|
||||
$this->setUpSmarty(__DIR__);
|
||||
parent::setUp();
|
||||
$this->smarty->setCachingType('apc');
|
||||
$this->smarty->registerCacheResource('apc', new Smarty_CacheResource_Apctest());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@ class CacheResourceFileTest extends CacheResourceTestCommon
|
||||
}
|
||||
|
||||
|
||||
public function testInit()
|
||||
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* test getCachedFilepath with configuration['useSubDirs'] enabled
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -32,6 +32,10 @@ class CacheResourceCustomMemcacheTest extends CacheResourceTestCommon
|
||||
$this->smarty->setCachingType('memcachetest');
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
}
|
||||
|
||||
protected function doClearCacheAssertion($a, $b)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -29,11 +29,15 @@ if (MysqlCacheEnable == true) {
|
||||
$this->getConnection();
|
||||
}
|
||||
$this->setUpSmarty(__DIR__);
|
||||
$this->initMysqlCache();
|
||||
parent::setUp();
|
||||
$this->smarty->setCachingType('mysqltest');
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
$this->initMysqlCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -28,11 +28,15 @@ 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,12 +27,16 @@ 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,7 +26,6 @@ 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");
|
||||
@@ -35,6 +34,11 @@ if (MysqlCacheEnable == true) {
|
||||
$this->smarty->registerCacheResource('foobar', new Smarty_CacheResource_Mysqltest());
|
||||
}
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->cleanDirs();
|
||||
$this->initMysqlCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore anything in here, but keep this directory
|
||||
*
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Smarty\Smarty;
|
||||
use Smarty\Template;
|
||||
use Smarty\Template\Cached;
|
||||
|
||||
require_once __DIR__ . '/../../../__shared/cacheresources/cacheresource.apc.php';
|
||||
|
||||
class Smarty_CacheResource_Apctest extends Smarty_CacheResource_Apc
|
||||
{
|
||||
public $lockTime = 0;
|
||||
|
||||
public function hasLock(Smarty $smarty, Cached $cached)
|
||||
{
|
||||
if ($this->lockTime) {
|
||||
$this->lockTime--;
|
||||
if (!$this->lockTime) {
|
||||
$this->releaseLock($smarty, $cached);
|
||||
}
|
||||
}
|
||||
return parent::hasLock($smarty, $cached);
|
||||
}
|
||||
|
||||
public function get(Template $_template)
|
||||
{
|
||||
$this->contents = array();
|
||||
$this->timestamps = array();
|
||||
$t = $this->getContent($_template);
|
||||
|
||||
return $t ? $t : null;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
use Smarty\Template;
|
||||
use Smarty\Template\Cached;
|
||||
|
||||
require_once __DIR__ . '/../cacheresources/cacheresource.memcache.php';
|
||||
require_once __DIR__ . '/../../../__shared/cacheresources/cacheresource.memcache.php';
|
||||
|
||||
class Smarty_CacheResource_Memcachetest extends Smarty_CacheResource_Memcache
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user