* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Twig\Extension; use Symfony\Bridge\Twig\Form\TwigRendererInterface; use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; use Symfony\Component\Form\Extension\Core\View\ChoiceView; use Symfony\Component\Form\FormView; use Twig\Environment; use Twig\Extension\AbstractExtension; use Twig\Extension\InitRuntimeInterface; use Twig\TwigFilter; use Twig\TwigFunction; use Twig\TwigTest; /** * FormExtension extends Twig with form capabilities. * * @author Fabien Potencier * @author Bernhard Schussek */ class FormExtension extends AbstractExtension implements InitRuntimeInterface { /** * This property is public so that it can be accessed directly from compiled * templates without having to call a getter, which slightly decreases performance. * * @var TwigRendererInterface */ public $renderer; public function __construct(TwigRendererInterface $renderer) { $this->renderer = $renderer; } /** * {@inheritdoc} */ public function initRuntime(Environment $environment) { $this->renderer->setEnvironment($environment); } /** * {@inheritdoc} */ public function getTokenParsers() { return array( // {% form_theme form "SomeBundle::widgets.twig" %} new FormThemeTokenParser(), ); } /** * {@inheritdoc} */ public function getFunctions() { return array( new TwigFunction('form_enctype', null, array('node_class' => 'Symfony\Bridge\Twig\Node\FormEnctypeNode', 'is_safe' => array('html'), 'deprecated' => true, 'alternative' => 'form_start')), new TwigFunction('form_widget', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), new TwigFunction('form_errors', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), new TwigFunction('form_label', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), new TwigFunction('form_row', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), new TwigFunction('form_rest', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), new TwigFunction('form', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), new TwigFunction('form_start', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), new TwigFunction('form_end', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), new TwigFunction('csrf_token', array($this, 'renderCsrfToken')), ); } /** * {@inheritdoc} */ public function getFilters() { return array( new TwigFilter('humanize', array($this, 'humanize')), new TwigFilter('form_encode_currency', array($this, 'encodeCurrency'), array('is_safe' => array('html'), 'needs_environment' => true)), ); } /** * {@inheritdoc} */ public function getTests() { return array( new TwigTest('selectedchoice', array($this, 'isSelectedChoice')), new TwigTest('rootform', array($this, 'isRootForm')), ); } /** * Renders a CSRF token. * * @param string $intention The intention of the protected action * * @return string A CSRF token */ public function renderCsrfToken($intention) { return $this->renderer->renderCsrfToken($intention); } /** * Makes a technical name human readable. * * @param string $text The text to humanize * * @return string The humanized text */ public function humanize($text) { return $this->renderer->humanize($text); } /** * Returns whether a choice is selected for a given form value. * * Unfortunately Twig does not support an efficient way to execute the * "is_selected" closure passed to the template by ChoiceType. It is faster * to implement the logic here (around 65ms for a specific form). * * Directly implementing the logic here is also faster than doing so in * ChoiceView (around 30ms). * * The worst option tested so far is to implement the logic in ChoiceView * and access the ChoiceView method directly in the template. Doing so is * around 220ms slower than doing the method call here in the filter. Twig * seems to be much more efficient at executing filters than at executing * methods of an object. * * @param ChoiceView $choice The choice to check * @param string|array $selectedValue The selected value to compare * * @return bool Whether the choice is selected * * @see ChoiceView::isSelected() */ public function isSelectedChoice(ChoiceView $choice, $selectedValue) { if (\is_array($selectedValue)) { return \in_array($choice->value, $selectedValue, true); } return $choice->value === $selectedValue; } /** * @internal */ public function isRootForm(FormView $formView) { return null === $formView->parent; } /** * @internal */ public function encodeCurrency(Environment $environment, $text, $widget = '') { if ('UTF-8' === $charset = $environment->getCharset()) { $text = htmlspecialchars($text, ENT_QUOTES | (\defined('ENT_SUBSTITUTE') ? ENT_SUBSTITUTE : 0), 'UTF-8'); } else { $text = htmlentities($text, ENT_QUOTES | (\defined('ENT_SUBSTITUTE') ? ENT_SUBSTITUTE : 0), 'UTF-8'); $text = iconv('UTF-8', $charset, $text); $widget = iconv('UTF-8', $charset, $widget); } return str_replace('{{ widget }}', $widget, $text); } /** * {@inheritdoc} */ public function getName() { return 'form'; } }