В Symfony 2.4 появится новый компонент — ExpressionLanguage. Компонент является движком для компиляции и исполнения «выражений».
Этот язык является урезанной версией твига. Выражения укладываются в одну строку и обычно возвращают булево значения, но не ограничиваются этим.
В отличии от твига, ExpressionLanguage работает в двух режимах:
- Компиляция: выражение компилируется в PHP код для последующего исполнения (код не зависит от среды выполнения)
- Исполнение: выражение исполняется без предварительной компиляции
Чтобы было возможно компилировать выражения в PHP код, не нуждающийся в модификации во время выполнения, оператор .
должен быть явным и означать лишь одно возможное поведение: foo.bar
— для свойств объекта, foo['bar']
для доступа к массиву, foo.getBar()
для вызова методов.
Использование компонента просто на сколько это возможно:
use SymfonyComponentExpressionLanguageExpressionLanguage;
$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 SymfonyComponentValidatorConstraints as Assert;
/**
* @AssertExpression("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, но прошу учесть что я переводчик, статьи, а не создатель этого компонента и пока сам не разобрался нравится он мне или нет (особенно поразило зачем выражения встроили в твиг).
Все замечания и пожелания пожалуйста в личку
Автор: hell0w0rd