Skip to content

[#3044] Furthering @WouterJ's work on the ExpressionLanguage component #3191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 28, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 65 additions & 10 deletions components/expression_language/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,57 @@ The ExpressionLanguage Component
(mostly, but not limited to, Booleans).

.. versionadded:: 2.4
The ExpressionLanguage component was new in Symfony 2.4.
The ExpressionLanguage component was introduced in Symfony 2.4.

Installation
------------

You can install the component in 2 different ways:

* Use the official Git repository (https://github.com/symfony/expression-language);
* :doc:`Install it via Composer </components/using_components>` (``symfony/expression-language`` on `Packagist`_).
* :doc:`Install it via Composer </components/using_components>` (``symfony/expression-language`` on `Packagist`_);
* Use the official Git repository (https://github.com/symfony/expression-language).

How can the Expression Engine Help Me?
--------------------------------------

The purpose of the component is to allow users to use expressions inside
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using the word 'expressions' here seems a bit vague, most people do not know what we mean by 'expressions'. I think we should provide 2 examples of the component in action:

  1. Complex logic to determine a boolean value (for instance, access rules in security or route matching)
  2. Complex logic to get a specific value (for instance, in the service container);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reworded this paragraph and my initial thought (once we have the framework docs) is to make security, validation rules and route matching links to the framework docs where you can see the examples. Then we can keep this paragraph short, which is something I want.

configuration for more complex logic. For some examples, the Symfony2 Framework
uses expressions in security, for validation rules and in route matching.

Besides using the component in the framework itself, the ExpressionLanguage
component is a perfect candidate for the foundation of a *business rule engine*.
The idea is to let the webmaster of a website configure things in a dynamic
way without using PHP and without introducing security problems:

.. code-block:: text

# Get the special price if
user.getGroup() in ['good_customers', 'collaborator']

# Promote article to the homepage when
article.commentCount > 100 and article.category not in ["misc"]

# Send an alert when
product.stock < 15

Expressions can be seen as a very restricted PHP sandbox and are immune to
external injections as you must explicitly declare which variables are available
in an expression.

Usage
-----

The ExpressionLanguage component can compile and evaluate expressions.
Expressions are one-liners which most of the time return a boolean, you can
compare them to the expression in an ``if`` statement. A simple example of an
expression is ``1 + 2``. You can also use more complicated expressions, such
as ``someArray[3].someMethod('bar')``.
Expressions are one-liners that often return a Boolean, which can be used
by the code executing the expression in an ``if`` statement. A simple example
of an expression is ``1 + 2``. You can also use more complicated expressions,
such as ``someArray[3].someMethod('bar')``.

The component provides 2 ways to work with expressions:

* **evaluation**: the expression is evaluated without being compiled to PHP;
* **compile**: the expression is compiled to PHP, so it can be cached and
evaluated;
* **evaluation**: the expression is evaluated without being compiled to PHP.
evaluated.

The main class of the component is
:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage`::
Expand All @@ -49,7 +76,35 @@ The main class of the component is
Expression Syntax
-----------------

See ":doc:`/components/expression_language/syntax`" to learn the syntax of the
See :doc:`/components/expression_language/syntax` to learn the syntax of the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the current form. I don't think that we need to highlight the section's name with quotes as this is already achieved by the link.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - sometimes I add commas for more emphasis when something is hidden inside a paragraph.

ExpressionLanguage component.

Passing in Variables
--------------------

You can also pass variables into the expression, which can be of any valid
PHP type (including objects)::

use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

$language = new ExpressionLanguage();

class Apple
{
public $variety;
}

$apple = new Apple();
$apple->variety = 'Honeycrisp';

echo $language->evaluate(
'fruit.variety',
array(
'fruit' => $apple,
)
);

This will print "Honeycrisp". For more information, see the :doc:`/components/expression_language/syntax`
entry, especially :ref:`component-expression-objects` and :ref:`component-expression-arrays`.

.. _Packagist: https://packagist.org/packages/symfony/expression-language
185 changes: 172 additions & 13 deletions components/expression_language/syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,104 @@ 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 twig notation (e.g. ``[1, 2]``)
* **hashes** - using twig notation (e.g. ``{ foo: 'bar' }``)
* **arrays** - using JSON-like notation (e.g. ``[1, 2]``)
* **hashes** - using JSON-like notation (e.g. ``{ foo: 'bar' }``)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does key foo here require quotes (e.g. "foo") here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, not in the expression syntax.

* **booleans** - ``true`` and ``false``
* **null** - ``null``

.. _component-expression-objects:

Working with Objects
--------------------

When passing objects into an expression, you can use different syntaxes to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those into's should be changed to just to's

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find into slightly more descriptive. It's also used in the Dependency Injection wiki article - so I'm going to keep it unless others agree with you.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for into

access properties and call methods on the object.

Accessing Public Methods
~~~~~~~~~~~~~~~~~~~~~~~~

Public properties on objects can be accessed by using the ``.`` syntax, similar
to JavaScript::

class Apple
{
public $variety;
}

$apple = new Apple();
$apple->variety = 'Honeycrisp';

echo $language->evaluate(
'fruit.variety',
array(
'fruit' => $apple,
)
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing sample output as in the examples below.

This will print ``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 = array();
for ($i = 0; $i < $times; $i++) {
$greetings[] = 'Hi';
}

return implode(' ', $greetings).'!';
}
}

$robot = new Robot();

echo $language->evaluate(
'robot.sayHi(3)',
array(
'robot' => $robot,
)
);

This will print ``Hi Hi Hi!``.

.. _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 = array('life' => 10, 'universe' => 10, 'everything' => 22);

echo $language->evaluate(
'data["life"] + data["universe"] + data["everything"]',
array(
'data' => $data,
)
);

This will print ``42``.

Supported Operators
~~~~~~~~~~~~~~~~~~~
-------------------

The component comes with a lot of operators:

Arithmetic Operators
....................
~~~~~~~~~~~~~~~~~~~~

* ``+`` (addition)
* ``-`` (subtraction)
Expand All @@ -35,20 +115,33 @@ Arithmetic Operators
* ``%`` (modulus)
* ``**`` (pow)

For example::

echo $language->evaluate(
'life + universe + everything',
array(
'life' => 10,
'universe' => 10,
'everything' => 22,
)
);

This will print out ``42``.

Assignment Operators
....................
~~~~~~~~~~~~~~~~~~~~

* ``=``

Bitwise Operators
.................
~~~~~~~~~~~~~~~~~

* ``&`` (and)
* ``|`` (or)
* ``^`` (xor)

Comparison Operators
....................
~~~~~~~~~~~~~~~~~~~~

* ``==`` (equal)
* ``===`` (identical)
Expand All @@ -67,31 +160,97 @@ Comparison Operators

$language->evaluate('not "foo" matches "/bar/"'); // returns true

Examples::

$ret1 = $language->evaluate(
'life == everything',
array(
'life' => 10,
'universe' => 10,
'everything' => 22,
)
);

$ret2 = $language->evaluate(
'life > everything',
array(
'life' => 10,
'universe' => 10,
'everything' => 22,
)
);

Both variables would be set to ``false``.

Logical Operators
.................
~~~~~~~~~~~~~~~~~

* ``not`` or ``!``
* ``and`` or ``&&``
* ``or`` or ``||``

For example::

$ret = $language->evaluate(
'life < universe or life < everything',
array(
'life' => 10,
'universe' => 10,
'everything' => 22,
)
);

This ``$ret`` variable will be set to ``true``.

String Operators
................
~~~~~~~~~~~~~~~~

* ``~`` (concatenation)

For example::

echo $language->evaluate(
'firstName~" "~lastName',
array(
'firstName' => 'Arthur',
'lastName' => 'Dent',
)
);

This would print out ``Arthur Dent``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

being really strict, it would not print anything

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true - but the other examples evaluating to a string are echo'd in the end. So I think it is all about consistency, i.e. all examples should follow the same structure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only one example prints something, the other examples say 'it returns ...'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see five descriptions (including the introduction page) where it "prints" something (even without echo).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed this to echo and killed the variable - it was more of some copy-and-paste from a file where I was trying these out :).


Array Operators
...............
~~~~~~~~~~~~~~~

* ``in`` (contain)
* ``not in`` (does not contain)

For example::

class User
{
public $group;
}

$user = new User();
$user->group = 'human_resources';

$inGroup = $language->evaluate(
'user.group in ["human_resources", "marketing"]',
array(
'user' => $user
)
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing description of return value as in the examples before and maybe $ret5 can be renamed to $ret.

The ``$inGroup`` would evaluate to ``true``.

Numeric Operators
.................
~~~~~~~~~~~~~~~~~

* ``..`` (range)

Ternary Operators
.................
~~~~~~~~~~~~~~~~~

* ``foo ? 'yes' : 'no'``
* ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``)
Expand Down