Pull to refresh

Новое в Symfony 2.4: компонент ExpressionLanguage

Reading time 5 min
Views 5.9K
Original author: Fabien Potencier
В Symfony 2.4 появится новый компонент — ExpressionLanguage. Компонент является движком для компиляции и исполнения «выражений».
Этот язык является урезанной версией твига. Выражения укладываются в одну строку и обычно возвращают булево значения, но не ограничиваются этим.
В отличии от твига, ExpressionLanguage работает в двух режимах:
  • Компиляция: выражение компилируется в PHP код для последующего исполнения (код не зависит от среды выполнения)
  • Исполнение: выражение исполняется без предварительной компиляции

Чтобы было возможно компилировать выражения в PHP код, не нуждающийся в модификации во время выполнения, оператор . должен быть явным и означать лишь одно возможное поведение: foo.bar — для свойств объекта, foo['bar'] для доступа к массиву, foo.getBar() для вызова методов.
Использование компонента просто на сколько это возможно:
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

$language = new ExpressionLanguage();

echo $language->evaluate('1 + 1');
// echo 2

echo $language->compile('1 + 2');
// echo "(1 + 2)"

Язык выражений поддерживает все то же что и твиг: математические операторы, строки, числа, массивы, хеши, булевы переменные… Выражения могут рассматриваться как очень ограниченная PHP-песочница, в которой невозможны внешние воздействия, все переменные должны быть объявлены заранее до компиляции или исполнения выражения.
$language->evaluate('a.b', array('a' => new stdClass()));

$language->compile('a.b', array('a'));

Последнее, но не по значению — вы можете легко расширить язык с помощью функций. Они работают точно также как их аналоги в твиге (для подробного ознакомления посмотрите метод register())
Как на счет примеров использования? Мы встроили компонент во множество других компонентов, используемых в Symfony.


Контейнер сервисов (Service Container)


Вы можете использовать выражения в любом месте, где можно передать аргумент в контейнер:
$c->register('foo', 'Foo')->addArgument(new Expression('bar.getvalue()'));

В контейнере выражения дополняются двумя функциями: service(), чтобы получить сервис, и parameter, чтобы получить значение параметра:
service("bar").getValue(parameter("value"))

В XML:
<service id="foo" class="Foo">
    <argument type="expression">service('bar').getvalue(parameter('value'))</argument>
</service>

Здесь нет никакого оверхеда во время исполнения, так как PHP-дампер компилирует выражения. Предыдущий пример скомпилируется в следующий PHP код:
$this->get("bar")->getvalue($this->getParameter("value"))


Правила доступа (Access Control Rules)


Настройка правил доступа может ввести в заблуждения, что может привести к незащищенным приложения
Новая директива allow_if упрощает настройку правил доступа в вашем приложении:
access_control:
    - { path: ^/_internal/secure, allow_if: "'127.0.0.1' == request.getClientIp() or has_role('ROLE_ADMIN')" }

Это правило ограничивает пути, начинающиеся с /_internal/secure для пользователей зашедших не с localhost или не имеющих права администратора.
request, token и user — переменные, к которым у вас есть доступ, is_anonymous(), is_authenticated(), is_fully_authenticated(), is_rememberme(), and has_role() — функции доступные в выражениях при настройке правил доступа.

Twig


Вы также можете использовать выражения в ваших шаблонах, с помощью функции expression
{% if is_granted(expression('has_role("FOO")')) %}
   ...
{% endif %}


Если вы используете SensioFrameworkExtraBundle, у вас также есть возможность обезопасить контроллеры, с аннотацией @ Security
/**
 * @Route("/post/{id}")
 * @Security("has_role('ROLE_ADMIN')")
 */
public function showAction(Post $post)
{
}

Примечание: Аннотация @ Security будет частью 3 версии бандла, который выйдет перед Symfony 2.4

Кеширование


В третьей версии SensioFrameworkExtraBundle также будет доступна аннотация @Cache, которая дает доступ к HTTP кешированию. Вместо написания шаблонного кода снова и снова в простых ситуациях:
/**
 * @Route("/post/{id}")
 * @Cache(smaxage="15")
 */
public function showAction(Request $request, Post $post)
{
    $response = new Response();
    $response->setLastModified($post->getUpdated());
    if ($response->isNotModified($request)) {
        return $response;
    }

    // ...
}

Вы можете настроить все в аннотации (это также работает для ETag):
/**
 * @Route("/post/{id}")
 * @Cache(smaxage="15", lastModified="post.getUpdatedAt()")
 */
public function showAction(Post $post)
{
    // ...
}

Маршрутизация (Routing)


Из коробки Symfony может выбрать роут по предопределенным переменным (таким как info, method, sheme), но некоторым нужна более сложная логика, базирующаяся на информации из запроса (объект Request)
Чтобы покрыть эти специальные случаи, вы можете использовать дериктиву condition, которая позволяет добавить любое выражение использующее переменные request и routing context:
hello:
    path: /hello/{name}
    condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') =~ '/firefox/i'"

И опять таки, используя PHP дампер правил маршрутизации (URL matcher), нет никакого оверхеда, так как все выражения компилируются в PHP код:
// hello
if (0 === strpos($pathinfo, '/hello') && preg_match('#^/hello/(?P<name>[^/]++)$#s', $pathinfo, $matches)
    && (in_array($context->getMethod(), array(0 => "GET", 1 => "HEAD"))
    && preg_match("/firefox/i", $request->headers->get("User-Agent")))
) {
    return $this->mergeDefaults(array_replace($matches, array('_route' => 'hello')), array ());
}

Имейте ввиду, что эти условия не будут никак использоваться при генерации URL

Validation


Новое условие Expression позволяет использовать выражения для валидации:
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Assert\Expression("this.getFoo() == 'fo'", message="Not good!")
 */
class Obj
{
    public function getFoo()
    {
        return 'foo';
    }
}

В выражениях валидатора this ссылается на текущий объект валидации.

Движок бизнес-правил (Business Rule Engine)


Кроме того используя компонент в самом фреймворке язык выражений отличный кандидат для создания движка бизнесс-правил. Идея в том, что вебмастер (администратор) сайта может гибко настроить сайт, без использования PHP и без посвещения себя в проблемы безопасности:
# 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


Вот и последний пост, в котором я рассматриваю новые возможности Symfony 2.4. В течении нескольких дней будет доступна первая пред-релизная версия (release candidate).

Чувствую что не всем приглянется подобное использование PHP, но прошу учесть что я переводчик, статьи, а не создатель этого компонента и пока сам не разобрался нравится он мне или нет (особенно поразило зачем выражения встроили в твиг).
Все замечания и пожелания пожалуйста в личку
Only registered users can participate in poll. Log in, please.
Как вы относитесь к компоненту
18.07% Однозначно буду использовать 15
22.89% Буду использовать осмотрительно 19
26.51% Буду использовать, но только в очень крайних случаях 22
22.89% Не буду использовать 19
9.64% Категорически не буду использовать и всячески против чтобы это использовали мои коллеги 8
83 users voted. 26 users abstained.
Tags:
Hubs:
+6
Comments 11
Comments Comments 11

Articles