Пользователь
0,0
рейтинг
13 ноября 2013 в 23:34

Разработка → Новое в Symfony 2.4: компонент ExpressionLanguage перевод

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

Проголосовало 80 человек. Воздержалось 24 человека.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Перевод: Fabien Potencier
Никита Гусаков @hell0w0rd
карма
25,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (11)

  • 0
    Не так давно писали чтото-похожее для YAML-конфигов, в целом очень полезный функционал в умелых руках.
    • 0
      С одной стороны этот функционал способен породить кучу говнокода в неумелых руках, но с другой стороны неумелые руки вроде не часто осиливают symfony (по-крайней мере вторую версию).
      • 0
        Чем больше неумелых рук осваивают симфони, тем меньше неумелых рук осваивают симфони.
  • 0
    Удобно строить бизнес-правила, можно даже в сочетание написать билдер на JS
  • 0
    Если бы еще аннотации были частью языка, а не в виде коментариев.
  • +1
    Вот это велосипед! Лямбды и eval оказались недостаточно клёвыми.
    • 0
      eval совсем не клево, а лямбды — как вы в конфигах опишите лямбды?
      • +1
        Какой же это конфиг, если в нём код? А если код, то почему не php?
        • 0
          Ну не стоит лукавить. Конфиги в крупных фреймворках влияют на логику работы приложения. Это не просто информация о том как подключать кешер, соединяться с базой, на каком порту приложение работает и так далее. Вы описываете роутинг, схемы моделей. Так что это просто расширение возможностей конфигов с логикой.
          • 0
            Почему всё-таки не php? Всего-то и делов:
            • 0
              Нет, можно на php конфиги писать, я не спорю. Но всетаки такой способ — создание некой песочницы.
              В общем все это спорно и то и другое можно использовать не по назначению, на вкус и цвет в общем

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.