Этот язык является урезанной версией твига. Выражения укладываются в одну строку и обычно возвращают булево значения, но не ограничиваются этим.
В отличии от твига, 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, но прошу учесть что я переводчик, статьи, а не создатель этого компонента и пока сам не разобрался нравится он мне или нет (особенно поразило зачем выражения встроили в твиг).
Все замечания и пожелания пожалуйста в личку
ссылка на оригинал статьи http://habrahabr.ru/post/202058/
Добавить комментарий