summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpharixces <pharixces@gmail.com>2025-10-03 23:17:55 +0200
committerGitHub <noreply@github.com>2025-10-03 23:17:55 +0200
commitb390e509747c42e940008b042fb0d3137dbbf5df (patch)
treedb1f36591f2ebdc3db94d3bf85602bb223de447d /src
parent0e46ae3add2c6213612951464b4edc44828fbc01 (diff)
downloadsmarty-b390e509747c42e940008b042fb0d3137dbbf5df.tar.gz
smarty-b390e509747c42e940008b042fb0d3137dbbf5df.tar.bz2
smarty-b390e509747c42e940008b042fb0d3137dbbf5df.zip
Add support for shorttags in functions (#1142)
* Add support for shorttags in functions Co-authored-by: Anne Zijlstra <a.zijlstra@iwink.nl> Co-authored-by: Simon Wisselink <s.wisselink@iwink.nl>
Diffstat (limited to 'src')
-rw-r--r--src/Compile/AttributeCompiler.php144
-rw-r--r--src/Compile/Base.php84
-rw-r--r--src/Compile/FunctionCallCompiler.php30
-rw-r--r--src/FunctionHandler/AttributeBase.php77
-rw-r--r--src/FunctionHandler/AttributeFunctionHandlerInterface.php15
5 files changed, 259 insertions, 91 deletions
diff --git a/src/Compile/AttributeCompiler.php b/src/Compile/AttributeCompiler.php
new file mode 100644
index 00000000..077c4cfc
--- /dev/null
+++ b/src/Compile/AttributeCompiler.php
@@ -0,0 +1,144 @@
+<?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;
+ }
+}
diff --git a/src/Compile/Base.php b/src/Compile/Base.php
index 2d5c0c0e..10153501 100644
--- a/src/Compile/Base.php
+++ b/src/Compile/Base.php
@@ -82,84 +82,12 @@ abstract class Base implements CompilerInterface {
* @return array of mapped attributes for further processing
*/
protected 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;
+ return (new AttributeCompiler(
+ $this->required_attributes,
+ $this->optional_attributes,
+ $this->shorttag_order,
+ $this->option_flags
+ ))->getAttributes($compiler, $attributes);
}
/**
diff --git a/src/Compile/FunctionCallCompiler.php b/src/Compile/FunctionCallCompiler.php
index 107dd98b..3ce50da2 100644
--- a/src/Compile/FunctionCallCompiler.php
+++ b/src/Compile/FunctionCallCompiler.php
@@ -3,24 +3,18 @@
* 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\CompilerException;
+use Smarty\FunctionHandler\AttributeFunctionHandlerInterface;
/**
* Smarty Internal Plugin Compile Registered Function Class
- *
-
-
*/
class FunctionCallCompiler extends Base {
-
/**
* Attribute definition: Overwrites base class.
*
@@ -51,16 +45,26 @@ class FunctionCallCompiler extends Base {
*/
public function compile($args, Template $compiler, $parameter = [], $tag = null, $function = null): string
{
+ if ($functionHandler = $compiler->getSmarty()->getFunctionHandler($function)) {
- // check and get attributes
- $_attr = $this->getAttributes($compiler, $args);
- unset($_attr['nocache']);
+ $attribute_overrides = [];
- $_paramsArray = $this->formatParamsArray($_attr);
- $_params = 'array(' . implode(',', $_paramsArray) . ')';
+ 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);
- if ($functionHandler = $compiler->getSmarty()->getFunctionHandler($function)) {
+ unset($_attr['nocache']);
+
+ $_paramsArray = $this->formatParamsArray($_attr);
+ $_params = 'array(' . implode(',', $_paramsArray) . ')';
// not cacheable?
$compiler->tag_nocache = $compiler->tag_nocache || !$functionHandler->isCacheable();
diff --git a/src/FunctionHandler/AttributeBase.php b/src/FunctionHandler/AttributeBase.php
new file mode 100644
index 00000000..71a035dc
--- /dev/null
+++ b/src/FunctionHandler/AttributeBase.php
@@ -0,0 +1,77 @@
+<?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,
+ ];
+ }
+}
diff --git a/src/FunctionHandler/AttributeFunctionHandlerInterface.php b/src/FunctionHandler/AttributeFunctionHandlerInterface.php
new file mode 100644
index 00000000..f76259ec
--- /dev/null
+++ b/src/FunctionHandler/AttributeFunctionHandlerInterface.php
@@ -0,0 +1,15 @@
+<?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;
+}