diff --git a/_build/redirection_map b/_build/redirection_map index 064f9c8261a..764f690c213 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -538,3 +538,5 @@ /components/security /security /components/var_dumper/advanced /components/var_dumper#advanced-usage /components/yaml/yaml_format /components/yaml#yaml-format +/components/expression_language/syntax /components/expression_language#expression-language-syntax +/components/expression_language/extending /components/expression_language#expression-language-extending diff --git a/components/expression_language.rst b/components/expression_language.rst index 988bda75884..be84457995f 100644 --- a/components/expression_language.rst +++ b/components/expression_language.rst @@ -73,11 +73,317 @@ The main class of the component is var_dump($expressionLanguage->compile('1 + 2')); // displays (1 + 2) +.. _expression-language-syntax: + +.. index:: + single: Syntax; ExpressionLanguage + Expression Syntax ----------------- -See :doc:`/components/expression_language/syntax` to learn the syntax of the -ExpressionLanguage component. +The ExpressionLanguage component uses a specific syntax which is based on the +expression syntax of Twig. In this document, you can find all supported +syntaxes. + +Supported Literals +~~~~~~~~~~~~~~~~~~ + +The component supports: + +* **strings** - single and double quotes (e.g. ``'hello'``) +* **numbers** - e.g. ``103`` +* **arrays** - using JSON-like notation (e.g. ``[1, 2]``) +* **hashes** - using JSON-like notation (e.g. ``{ foo: 'bar' }``) +* **booleans** - ``true`` and ``false`` +* **null** - ``null`` +* **exponential** - also known as scientific (e.g. ``1.99E+3`` or ``1e-2``) + +.. caution:: + + A backslash (``\``) must be escaped by 4 backslashes (``\\\\``) in a string + and 8 backslashes (``\\\\\\\\``) in a regex:: + + echo $expressionLanguage->evaluate('"\\\\"'); // prints \ + $expressionLanguage->evaluate('"a\\\\b" matches "/^a\\\\\\\\b$/"'); // returns true + + Control characters (e.g. ``\n``) in expressions are replaced with + whitespace. To avoid this, escape the sequence with a single backslash + (e.g. ``\\n``). + +.. _component-expression-objects: + +Working with Objects +~~~~~~~~~~~~~~~~~~~~ + +When passing objects into an expression, you can use different syntaxes to +access properties and call methods on the object. + +Accessing Public Properties +........................... + +Public properties on objects can be accessed by using the ``.`` syntax, similar +to JavaScript:: + + class Apple + { + public $variety; + } + + $apple = new Apple(); + $apple->variety = 'Honeycrisp'; + + var_dump($expressionLanguage->evaluate( + 'fruit.variety', + [ + 'fruit' => $apple, + ] + )); + +This will print out ``Honeycrisp``. + +Calling Methods +............... + +The ``.`` syntax can also be used to call methods on an object, similar to +JavaScript:: + + class Robot + { + public function sayHi($times) + { + $greetings = []; + for ($i = 0; $i < $times; $i++) { + $greetings[] = 'Hi'; + } + + return implode(' ', $greetings).'!'; + } + } + + $robot = new Robot(); + + var_dump($expressionLanguage->evaluate( + 'robot.sayHi(3)', + [ + 'robot' => $robot, + ] + )); + +This will print out ``Hi Hi Hi!``. + +.. _component-expression-functions: + +Working with Functions +~~~~~~~~~~~~~~~~~~~~~~ + +You can also use registered functions in the expression by using the same +syntax as PHP and JavaScript. The ExpressionLanguage component comes with one +function by default: ``constant()``, which will return the value of the PHP +constant:: + + define('DB_USER', 'root'); + + var_dump($expressionLanguage->evaluate( + 'constant("DB_USER")' + )); + +This will print out ``root``. + +.. tip:: + + To read how to register your own functions to use in an expression, see + ":ref:`expression-language-extending`". + +.. _component-expression-arrays: + +Working with Arrays +~~~~~~~~~~~~~~~~~~~ + +If you pass an array into an expression, use the ``[]`` syntax to access +array keys, similar to JavaScript:: + + $data = ['life' => 10, 'universe' => 10, 'everything' => 22]; + + var_dump($expressionLanguage->evaluate( + 'data["life"] + data["universe"] + data["everything"]', + [ + 'data' => $data, + ] + )); + +This will print out ``42``. + +Supported Operators +~~~~~~~~~~~~~~~~~~~ + +The component comes with a lot of operators: + +Arithmetic Operators +.................... + +* ``+`` (addition) +* ``-`` (subtraction) +* ``*`` (multiplication) +* ``/`` (division) +* ``%`` (modulus) +* ``**`` (pow) + +For example:: + + var_dump($expressionLanguage->evaluate( + 'life + universe + everything', + [ + 'life' => 10, + 'universe' => 10, + 'everything' => 22, + ] + )); + +This will print out ``42``. + +Bitwise Operators +................. + +* ``&`` (and) +* ``|`` (or) +* ``^`` (xor) + +Comparison Operators +.................... + +* ``==`` (equal) +* ``===`` (identical) +* ``!=`` (not equal) +* ``!==`` (not identical) +* ``<`` (less than) +* ``>`` (greater than) +* ``<=`` (less than or equal to) +* ``>=`` (greater than or equal to) +* ``matches`` (regex match) + +.. tip:: + + To test if a string does *not* match a regex, use the logical ``not`` + operator in combination with the ``matches`` operator:: + + $expressionLanguage->evaluate('not ("foo" matches "/bar/")'); // returns true + + You must use parentheses because the unary operator ``not`` has precedence + over the binary operator ``matches``. + +Examples:: + + $ret1 = $expressionLanguage->evaluate( + 'life == everything', + [ + 'life' => 10, + 'everything' => 22, + ] + ); + + $ret2 = $expressionLanguage->evaluate( + 'life > everything', + [ + 'life' => 10, + 'everything' => 22, + ] + ); + +Both variables would be set to ``false``. + +Logical Operators +................. + +* ``not`` or ``!`` +* ``and`` or ``&&`` +* ``or`` or ``||`` + +For example:: + + $ret = $expressionLanguage->evaluate( + 'life < universe or life < everything', + [ + 'life' => 10, + 'universe' => 10, + 'everything' => 22, + ] + ); + +This ``$ret`` variable will be set to ``true``. + +String Operators +................ + +* ``~`` (concatenation) + +For example:: + + var_dump($expressionLanguage->evaluate( + 'firstName~" "~lastName', + [ + 'firstName' => 'Arthur', + 'lastName' => 'Dent', + ] + )); + +This would print out ``Arthur Dent``. + +Array Operators +............... + +* ``in`` (contain) +* ``not in`` (does not contain) + +For example:: + + class User + { + public $group; + } + + $user = new User(); + $user->group = 'human_resources'; + + $inGroup = $expressionLanguage->evaluate( + 'user.group in ["human_resources", "marketing"]', + [ + 'user' => $user, + ] + ); + +The ``$inGroup`` would evaluate to ``true``. + +Numeric Operators +................. + +* ``..`` (range) + +For example:: + + class User + { + public $age; + } + + $user = new User(); + $user->age = 34; + + $expressionLanguage->evaluate( + 'user.age in 18..45', + [ + 'user' => $user, + ] + ); + +This will evaluate to ``true``, because ``user.age`` is in the range from +``18`` to ``45``. + +Ternary Operators +................. + +* ``foo ? 'yes' : 'no'`` +* ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``) +* ``foo ? 'yes'`` (equal to ``foo ? 'yes' : ''``) Passing in Variables -------------------- @@ -104,8 +410,13 @@ PHP type (including objects):: ] )); // displays "Honeycrisp" -For more information, see the :doc:`/components/expression_language/syntax` -entry, especially :ref:`Working with Objects ` and :ref:`Working with Arrays `. +When using this component inside a Symfony application, certain objects and +variables are automatically injected by Symfony so you can use them in your +expressions (e.g. the request, the current user, etc.): + +* :doc:`Variables available in security expressions `; +* :doc:`Variables available in service container expressions `; +* :ref:`Variables available in routing expressions `. .. caution:: @@ -114,25 +425,260 @@ entry, especially :ref:`Working with Objects ` and characters in untrusted data to prevent malicious users from injecting control characters and altering the expression. +.. index:: + single: Caching; ExpressionLanguage + Caching ------- -The component provides some different caching strategies, read more about them -in :doc:`/components/expression_language/caching`. +The ExpressionLanguage component provides a +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::compile` +method to be able to cache the expressions in plain PHP. But internally, the +component also caches the parsed expressions, so duplicated expressions can be +compiled/evaluated quicker. + +The Workflow +~~~~~~~~~~~~ + +Both :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::evaluate` +and ``compile()`` need to do some things before each can provide the return +values. For ``evaluate()``, this overhead is even bigger. + +Both methods need to tokenize and parse the expression. This is done by the +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::parse` +method. It returns a :class:`Symfony\\Component\\ExpressionLanguage\\ParsedExpression`. +Now, the ``compile()`` method just returns the string conversion of this object. +The ``evaluate()`` method needs to loop through the "nodes" (pieces of an +expression saved in the ``ParsedExpression``) and evaluate them on the fly. + +To save time, the ``ExpressionLanguage`` caches the ``ParsedExpression`` so +it can skip the tokenization and parsing steps with duplicate expressions. The +caching is done by a PSR-6 `CacheItemPoolInterface`_ instance (by default, it +uses an :class:`Symfony\\Component\\Cache\\Adapter\\ArrayAdapter`). You can +customize this by creating a custom cache pool or using one of the available +ones and injecting this using the constructor:: + + use Symfony\Component\Cache\Adapter\RedisAdapter; + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $cache = new RedisAdapter(...); + $expressionLanguage = new ExpressionLanguage($cache); + +.. seealso:: + + See the :doc:`/components/cache` documentation for more information about + available cache adapters. + +Using Parsed and Serialized Expressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and +``SerializedParsedExpression``:: + + // ... + + // the parse() method returns a ParsedExpression + $expression = $expressionLanguage->parse('1 + 4', []); + + var_dump($expressionLanguage->evaluate($expression)); // prints 5 + +.. code-block:: php + + use Symfony\Component\ExpressionLanguage\SerializedParsedExpression; + // ... + + $expression = new SerializedParsedExpression( + '1 + 4', + serialize($expressionLanguage->parse('1 + 4', [])->getNodes()) + ); + + var_dump($expressionLanguage->evaluate($expression)); // prints 5 + +.. index:: + single: AST; ExpressionLanguage + single: AST; Abstract Syntax Tree AST Dumping and Editing ----------------------- -The AST (*Abstract Syntax Tree*) of expressions can be dumped and manipulated -as explained in :doc:`/components/expression_language/ast`. +It's difficult to manipulate or inspect the expressions created with the ExpressionLanguage +component, because the expressions are plain strings. A better approach is to +turn those expressions into an AST. In computer science, `AST`_ (*Abstract +Syntax Tree*) is *"a tree representation of the structure of source code written +in a programming language"*. In Symfony, a ExpressionLanguage AST is a set of +nodes that contain PHP classes representing the given expression. + +Dumping the AST +~~~~~~~~~~~~~~~ + +Call the :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::getNodes` +method after parsing any expression to get its AST:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $ast = (new ExpressionLanguage()) + ->parse('1 + 2', []) + ->getNodes() + ; + + // dump the AST nodes for inspection + var_dump($ast); + + // dump the AST nodes as a string representation + $astAsString = $ast->dump(); + +Manipulating the AST +~~~~~~~~~~~~~~~~~~~~ + +The nodes of the AST can also be dumped into a PHP array of nodes to allow +manipulating them. Call the :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::toArray` +method to turn the AST into an array:: + + // ... + + $astAsArray = (new ExpressionLanguage()) + ->parse('1 + 2', []) + ->getNodes() + ->toArray() + ; + +.. _expression-language-extending: + +.. index:: + single: Extending; ExpressionLanguage + +Extending the ExpressionLanguage +-------------------------------- + +The ExpressionLanguage can be extended by adding custom functions. For +instance, in the Symfony Framework, the security has custom functions to check +the user's role. + +.. note:: + + If you want to learn how to use functions in an expression, read + ":ref:`component-expression-functions`". + +Registering Functions +~~~~~~~~~~~~~~~~~~~~~ + +Functions are registered on each specific ``ExpressionLanguage`` instance. +That means the functions can be used in any expression executed by that +instance. + +To register a function, use +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::register`. +This method has 3 arguments: + +* **name** - The name of the function in an expression; +* **compiler** - A function executed when compiling an expression using the + function; +* **evaluator** - A function executed when the expression is evaluated. + +Example:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->register('lowercase', function ($str) { + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); + }, function ($arguments, $str) { + if (!is_string($str)) { + return $str; + } + + return strtolower($str); + }); + + var_dump($expressionLanguage->evaluate('lowercase("HELLO")')); + // this will print: hello + +In addition to the custom function arguments, the **evaluator** is passed an +``arguments`` variable as its first argument, which is equal to the second +argument of ``evaluate()`` (e.g. the "values" when evaluating an expression). + +.. _components-expression-language-provider: + +Using Expression Providers +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you use the ``ExpressionLanguage`` class in your library, you often want +to add custom functions. To do so, you can create a new expression provider by +creating a class that implements +:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface`. + +This interface requires one method: +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface::getFunctions`, +which returns an array of expression functions (instances of +:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction`) to +register:: + + use Symfony\Component\ExpressionLanguage\ExpressionFunction; + use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + + class StringExpressionLanguageProvider implements ExpressionFunctionProviderInterface + { + public function getFunctions() + { + return [ + new ExpressionFunction('lowercase', function ($str) { + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); + }, function ($arguments, $str) { + if (!is_string($str)) { + return $str; + } + + return strtolower($str); + }), + ]; + } + } + +.. tip:: + + To create an expression function from a PHP function with the + :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction::fromPhp` static method:: + + ExpressionFunction::fromPhp('strtoupper'); + + Namespaced functions are supported, but they require a second argument to + define the name of the expression:: + + ExpressionFunction::fromPhp('My\strtoupper', 'my_strtoupper'); + +You can register providers using +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::registerProvider` +or by using the second argument of the constructor:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + // using the constructor + $expressionLanguage = new ExpressionLanguage(null, [ + new StringExpressionLanguageProvider(), + // ... + ]); + + // using registerProvider() + $expressionLanguage->registerProvider(new StringExpressionLanguageProvider()); + +.. tip:: + + It is recommended to create your own ``ExpressionLanguage`` class in your + library. Now you can add the extension by overriding the constructor:: + + use Psr\Cache\CacheItemPoolInterface; + use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; -Learn More ----------- + class ExpressionLanguage extends BaseExpressionLanguage + { + public function __construct(CacheItemPoolInterface $cache = null, array $providers = []) + { + // prepends the default provider to let users override it + array_unshift($providers, new StringExpressionLanguageProvider()); -.. toctree:: - :maxdepth: 1 - :glob: + parent::__construct($cache, $providers); + } + } - /components/expression_language/* - /service_container/expression_language - /reference/constraints/Expression +.. _`AST`: https://en.wikipedia.org/wiki/Abstract_syntax_tree +.. _`CacheItemPoolInterface`: https://github.com/php-fig/cache/blob/master/src/CacheItemPoolInterface.php diff --git a/components/expression_language/ast.rst b/components/expression_language/ast.rst deleted file mode 100644 index 2bd2bf80023..00000000000 --- a/components/expression_language/ast.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. index:: - single: AST; ExpressionLanguage - single: AST; Abstract Syntax Tree - -Dumping and Manipulating the AST of Expressions -=============================================== - -It’s difficult to manipulate or inspect the expressions created with the ExpressionLanguage -component, because the expressions are plain strings. A better approach is to -turn those expressions into an AST. In computer science, `AST`_ (*Abstract -Syntax Tree*) is *"a tree representation of the structure of source code written -in a programming language"*. In Symfony, a ExpressionLanguage AST is a set of -nodes that contain PHP classes representing the given expression. - -Dumping the AST ---------------- - -Call the :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::getNodes` -method after parsing any expression to get its AST:: - - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; - - $ast = (new ExpressionLanguage()) - ->parse('1 + 2', []) - ->getNodes() - ; - - // dump the AST nodes for inspection - var_dump($ast); - - // dump the AST nodes as a string representation - $astAsString = $ast->dump(); - -Manipulating the AST --------------------- - -The nodes of the AST can also be dumped into a PHP array of nodes to allow -manipulating them. Call the :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::toArray` -method to turn the AST into an array:: - - // ... - - $astAsArray = (new ExpressionLanguage()) - ->parse('1 + 2', []) - ->getNodes() - ->toArray() - ; - -.. _`AST`: https://en.wikipedia.org/wiki/Abstract_syntax_tree diff --git a/components/expression_language/caching.rst b/components/expression_language/caching.rst deleted file mode 100644 index 29e1e0116f7..00000000000 --- a/components/expression_language/caching.rst +++ /dev/null @@ -1,70 +0,0 @@ -.. index:: - single: Caching; ExpressionLanguage - -Caching Expressions Using Parser Caches -======================================= - -The ExpressionLanguage component already provides a -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::compile` -method to be able to cache the expressions in plain PHP. But internally, the -component also caches the parsed expressions, so duplicated expressions can be -compiled/evaluated quicker. - -The Workflow ------------- - -Both :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::evaluate` -and ``compile()`` need to do some things before each can provide the return -values. For ``evaluate()``, this overhead is even bigger. - -Both methods need to tokenize and parse the expression. This is done by the -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::parse` -method. It returns a :class:`Symfony\\Component\\ExpressionLanguage\\ParsedExpression`. -Now, the ``compile()`` method just returns the string conversion of this object. -The ``evaluate()`` method needs to loop through the "nodes" (pieces of an -expression saved in the ``ParsedExpression``) and evaluate them on the fly. - -To save time, the ``ExpressionLanguage`` caches the ``ParsedExpression`` so -it can skip the tokenization and parsing steps with duplicate expressions. The -caching is done by a PSR-6 `CacheItemPoolInterface`_ instance (by default, it -uses an :class:`Symfony\\Component\\Cache\\Adapter\\ArrayAdapter`). You can -customize this by creating a custom cache pool or using one of the available -ones and injecting this using the constructor:: - - use Symfony\Component\Cache\Adapter\RedisAdapter; - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; - - $cache = new RedisAdapter(...); - $expressionLanguage = new ExpressionLanguage($cache); - -.. seealso:: - - See the :doc:`/components/cache` documentation for more information about - available cache adapters. - -Using Parsed and Serialized Expressions ---------------------------------------- - -Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and -``SerializedParsedExpression``:: - - // ... - - // the parse() method returns a ParsedExpression - $expression = $expressionLanguage->parse('1 + 4', []); - - var_dump($expressionLanguage->evaluate($expression)); // prints 5 - -.. code-block:: php - - use Symfony\Component\ExpressionLanguage\SerializedParsedExpression; - // ... - - $expression = new SerializedParsedExpression( - '1 + 4', - serialize($expressionLanguage->parse('1 + 4', [])->getNodes()) - ); - - var_dump($expressionLanguage->evaluate($expression)); // prints 5 - -.. _`CacheItemPoolInterface`: https://github.com/php-fig/cache/blob/master/src/CacheItemPoolInterface.php diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst deleted file mode 100644 index 787d0f61d31..00000000000 --- a/components/expression_language/extending.rst +++ /dev/null @@ -1,136 +0,0 @@ -.. index:: - single: Extending; ExpressionLanguage - -Extending the ExpressionLanguage -================================ - -The ExpressionLanguage can be extended by adding custom functions. For -instance, in the Symfony Framework, the security has custom functions to check -the user's role. - -.. note:: - - If you want to learn how to use functions in an expression, read - ":ref:`component-expression-functions`". - -Registering Functions ---------------------- - -Functions are registered on each specific ``ExpressionLanguage`` instance. -That means the functions can be used in any expression executed by that -instance. - -To register a function, use -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::register`. -This method has 3 arguments: - -* **name** - The name of the function in an expression; -* **compiler** - A function executed when compiling an expression using the - function; -* **evaluator** - A function executed when the expression is evaluated. - -Example:: - - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; - - $expressionLanguage = new ExpressionLanguage(); - $expressionLanguage->register('lowercase', function ($str) { - return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); - }, function ($arguments, $str) { - if (!is_string($str)) { - return $str; - } - - return strtolower($str); - }); - - var_dump($expressionLanguage->evaluate('lowercase("HELLO")')); - // this will print: hello - -In addition to the custom function arguments, the **evaluator** is passed an -``arguments`` variable as its first argument, which is equal to the second -argument of ``evaluate()`` (e.g. the "values" when evaluating an expression). - -.. _components-expression-language-provider: - -Using Expression Providers --------------------------- - -When you use the ``ExpressionLanguage`` class in your library, you often want -to add custom functions. To do so, you can create a new expression provider by -creating a class that implements -:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface`. - -This interface requires one method: -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface::getFunctions`, -which returns an array of expression functions (instances of -:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction`) to -register:: - - use Symfony\Component\ExpressionLanguage\ExpressionFunction; - use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; - - class StringExpressionLanguageProvider implements ExpressionFunctionProviderInterface - { - public function getFunctions() - { - return [ - new ExpressionFunction('lowercase', function ($str) { - return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); - }, function ($arguments, $str) { - if (!is_string($str)) { - return $str; - } - - return strtolower($str); - }), - ]; - } - } - -.. tip:: - - To create an expression function from a PHP function with the - :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction::fromPhp` static method:: - - ExpressionFunction::fromPhp('strtoupper'); - - Namespaced functions are supported, but they require a second argument to - define the name of the expression:: - - ExpressionFunction::fromPhp('My\strtoupper', 'my_strtoupper'); - -You can register providers using -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::registerProvider` -or by using the second argument of the constructor:: - - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; - - // using the constructor - $expressionLanguage = new ExpressionLanguage(null, [ - new StringExpressionLanguageProvider(), - // ... - ]); - - // using registerProvider() - $expressionLanguage->registerProvider(new StringExpressionLanguageProvider()); - -.. tip:: - - It is recommended to create your own ``ExpressionLanguage`` class in your - library. Now you can add the extension by overriding the constructor:: - - use Psr\Cache\CacheItemPoolInterface; - use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; - - class ExpressionLanguage extends BaseExpressionLanguage - { - public function __construct(CacheItemPoolInterface $cache = null, array $providers = []) - { - // prepends the default provider to let users override it - array_unshift($providers, new StringExpressionLanguageProvider()); - - parent::__construct($cache, $providers); - } - } - diff --git a/components/expression_language/syntax.rst b/components/expression_language/syntax.rst deleted file mode 100644 index a4c17f81a27..00000000000 --- a/components/expression_language/syntax.rst +++ /dev/null @@ -1,320 +0,0 @@ -.. index:: - single: Syntax; ExpressionLanguage - -The Expression Syntax -===================== - -The ExpressionLanguage component uses a specific syntax which is based on the -expression syntax of Twig. In this document, you can find all supported -syntaxes. - -Supported Literals ------------------- - -The component supports: - -* **strings** - single and double quotes (e.g. ``'hello'``) -* **numbers** - e.g. ``103`` -* **arrays** - using JSON-like notation (e.g. ``[1, 2]``) -* **hashes** - using JSON-like notation (e.g. ``{ foo: 'bar' }``) -* **booleans** - ``true`` and ``false`` -* **null** - ``null`` -* **exponential** - also known as scientific (e.g. ``1.99E+3`` or ``1e-2``) - -.. caution:: - - A backslash (``\``) must be escaped by 4 backslashes (``\\\\``) in a string - and 8 backslashes (``\\\\\\\\``) in a regex:: - - echo $expressionLanguage->evaluate('"\\\\"'); // prints \ - $expressionLanguage->evaluate('"a\\\\b" matches "/^a\\\\\\\\b$/"'); // returns true - - Control characters (e.g. ``\n``) in expressions are replaced with - whitespace. To avoid this, escape the sequence with a single backslash - (e.g. ``\\n``). - -.. _component-expression-objects: - -Working with Objects --------------------- - -When passing objects into an expression, you can use different syntaxes to -access properties and call methods on the object. - -Accessing Public Properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Public properties on objects can be accessed by using the ``.`` syntax, similar -to JavaScript:: - - class Apple - { - public $variety; - } - - $apple = new Apple(); - $apple->variety = 'Honeycrisp'; - - var_dump($expressionLanguage->evaluate( - 'fruit.variety', - [ - 'fruit' => $apple, - ] - )); - -This will print out ``Honeycrisp``. - -Calling Methods -~~~~~~~~~~~~~~~ - -The ``.`` syntax can also be used to call methods on an object, similar to -JavaScript:: - - class Robot - { - public function sayHi($times) - { - $greetings = []; - for ($i = 0; $i < $times; $i++) { - $greetings[] = 'Hi'; - } - - return implode(' ', $greetings).'!'; - } - } - - $robot = new Robot(); - - var_dump($expressionLanguage->evaluate( - 'robot.sayHi(3)', - [ - 'robot' => $robot, - ] - )); - -This will print out ``Hi Hi Hi!``. - -.. _component-expression-functions: - -Working with Functions ----------------------- - -You can also use registered functions in the expression by using the same -syntax as PHP and JavaScript. The ExpressionLanguage component comes with one -function by default: ``constant()``, which will return the value of the PHP -constant:: - - define('DB_USER', 'root'); - - var_dump($expressionLanguage->evaluate( - 'constant("DB_USER")' - )); - -This will print out ``root``. - -.. tip:: - - To read how to register your own functions to use in an expression, see - ":doc:`/components/expression_language/extending`". - -.. _component-expression-arrays: - -Working with Arrays -------------------- - -If you pass an array into an expression, use the ``[]`` syntax to access -array keys, similar to JavaScript:: - - $data = ['life' => 10, 'universe' => 10, 'everything' => 22]; - - var_dump($expressionLanguage->evaluate( - 'data["life"] + data["universe"] + data["everything"]', - [ - 'data' => $data, - ] - )); - -This will print out ``42``. - -Supported Operators -------------------- - -The component comes with a lot of operators: - -Arithmetic Operators -~~~~~~~~~~~~~~~~~~~~ - -* ``+`` (addition) -* ``-`` (subtraction) -* ``*`` (multiplication) -* ``/`` (division) -* ``%`` (modulus) -* ``**`` (pow) - -For example:: - - var_dump($expressionLanguage->evaluate( - 'life + universe + everything', - [ - 'life' => 10, - 'universe' => 10, - 'everything' => 22, - ] - )); - -This will print out ``42``. - -Bitwise Operators -~~~~~~~~~~~~~~~~~ - -* ``&`` (and) -* ``|`` (or) -* ``^`` (xor) - -Comparison Operators -~~~~~~~~~~~~~~~~~~~~ - -* ``==`` (equal) -* ``===`` (identical) -* ``!=`` (not equal) -* ``!==`` (not identical) -* ``<`` (less than) -* ``>`` (greater than) -* ``<=`` (less than or equal to) -* ``>=`` (greater than or equal to) -* ``matches`` (regex match) - -.. tip:: - - To test if a string does *not* match a regex, use the logical ``not`` - operator in combination with the ``matches`` operator:: - - $expressionLanguage->evaluate('not ("foo" matches "/bar/")'); // returns true - - You must use parentheses because the unary operator ``not`` has precedence - over the binary operator ``matches``. - -Examples:: - - $ret1 = $expressionLanguage->evaluate( - 'life == everything', - [ - 'life' => 10, - 'everything' => 22, - ] - ); - - $ret2 = $expressionLanguage->evaluate( - 'life > everything', - [ - 'life' => 10, - 'everything' => 22, - ] - ); - -Both variables would be set to ``false``. - -Logical Operators -~~~~~~~~~~~~~~~~~ - -* ``not`` or ``!`` -* ``and`` or ``&&`` -* ``or`` or ``||`` - -For example:: - - $ret = $expressionLanguage->evaluate( - 'life < universe or life < everything', - [ - 'life' => 10, - 'universe' => 10, - 'everything' => 22, - ] - ); - -This ``$ret`` variable will be set to ``true``. - -String Operators -~~~~~~~~~~~~~~~~ - -* ``~`` (concatenation) - -For example:: - - var_dump($expressionLanguage->evaluate( - 'firstName~" "~lastName', - [ - 'firstName' => 'Arthur', - 'lastName' => 'Dent', - ] - )); - -This would print out ``Arthur Dent``. - -Array Operators -~~~~~~~~~~~~~~~ - -* ``in`` (contain) -* ``not in`` (does not contain) - -For example:: - - class User - { - public $group; - } - - $user = new User(); - $user->group = 'human_resources'; - - $inGroup = $expressionLanguage->evaluate( - 'user.group in ["human_resources", "marketing"]', - [ - 'user' => $user, - ] - ); - -The ``$inGroup`` would evaluate to ``true``. - -Numeric Operators -~~~~~~~~~~~~~~~~~ - -* ``..`` (range) - -For example:: - - class User - { - public $age; - } - - $user = new User(); - $user->age = 34; - - $expressionLanguage->evaluate( - 'user.age in 18..45', - [ - 'user' => $user, - ] - ); - -This will evaluate to ``true``, because ``user.age`` is in the range from -``18`` to ``45``. - -Ternary Operators -~~~~~~~~~~~~~~~~~ - -* ``foo ? 'yes' : 'no'`` -* ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``) -* ``foo ? 'yes'`` (equal to ``foo ? 'yes' : ''``) - -Built-in Objects and Variables ------------------------------- - -When using this component inside a Symfony application, certain objects and -variables are automatically injected by Symfony so you can use them in your -expressions (e.g. the request, the current user, etc.): - -* :doc:`Variables available in security expressions `; -* :doc:`Variables available in service container expressions `; -* :ref:`Variables available in routing expressions `. diff --git a/reference/constraints/Expression.rst b/reference/constraints/Expression.rst index 65a38efb415..3a75b98bb40 100644 --- a/reference/constraints/Expression.rst +++ b/reference/constraints/Expression.rst @@ -141,7 +141,7 @@ One way to accomplish this is with the Expression constraint: The :ref:`expression ` option is the expression that must return true in order for validation to pass. To learn more about the expression language syntax, see -:doc:`/components/expression_language/syntax`. +:ref:`expression-language-syntax`. .. sidebar:: Mapping the Error to a Specific Field @@ -265,7 +265,7 @@ The expression that will be evaluated. If the expression evaluates to a false value (using ``==``, not ``===``), validation will fail. To learn more about the expression language syntax, see -:doc:`/components/expression_language/syntax`. +:ref:`expression-language-syntax`. Inside of the expression, you have access to up to 2 variables: diff --git a/routing.rst b/routing.rst index e65e7dde27f..560d61040cb 100644 --- a/routing.rst +++ b/routing.rst @@ -414,9 +414,9 @@ arbitrary matching logic: ; }; -The value of the ``condition`` option is any valid -:doc:`ExpressionLanguage expression ` -and can use any of these variables created by Symfony: +The value of the ``condition`` option is any valid :ref:`ExpressionLanguage +expression ` and can use any of these variables +created by Symfony: ``context`` An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, diff --git a/security/expressions.rst b/security/expressions.rst index cad80f6365f..85767ff6a60 100644 --- a/security/expressions.rst +++ b/security/expressions.rst @@ -37,7 +37,7 @@ be granted (note: your User object may not have an ``isSuperAdmin()`` method, that method is invented for this example). This uses an expression and you can learn more about the expression language -syntax, see :doc:`/components/expression_language/syntax`. +syntax, see :ref:`expression-language-syntax`. .. _security-expression-variables: diff --git a/service_container/expression_language.rst b/service_container/expression_language.rst index f755057e240..b70e27ad0dc 100644 --- a/service_container/expression_language.rst +++ b/service_container/expression_language.rst @@ -71,7 +71,7 @@ to another service: ``App\Mailer``. One way to do this is with an expression: ->args([expr("service('App\\\\Mail\\\\MailerConfiguration').getMailerMethod()")]); }; -To learn more about the expression language syntax, see :doc:`/components/expression_language/syntax`. +To learn more about the expression language syntax, see :ref:`expression-language-syntax`. In this context, you have access to 2 functions: