PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/symfony2/vendor/symfony/src/Symfony/Bridge/Twig/Extension/FormExtension.php

http://github.com/eryx/php-framework-benchmark
PHP | 330 lines | 164 code | 41 blank | 125 comment | 16 complexity | 73aca5ccd5c9610a001a09997031b74d MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Bridge\Twig\Extension;
  11. use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
  12. use Symfony\Component\Form\FormView;
  13. use Symfony\Component\Form\Exception\FormException;
  14. use Symfony\Component\Form\Util\FormUtil;
  15. /**
  16. * FormExtension extends Twig with form capabilities.
  17. *
  18. * @author Fabien Potencier <fabien@symfony.com>
  19. * @author Bernhard Schussek <bernhard.schussek@symfony.com>
  20. */
  21. class FormExtension extends \Twig_Extension
  22. {
  23. protected $resources;
  24. protected $blocks;
  25. protected $environment;
  26. protected $themes;
  27. protected $varStack;
  28. protected $template;
  29. public function __construct(array $resources = array())
  30. {
  31. $this->themes = new \SplObjectStorage();
  32. $this->varStack = array();
  33. $this->blocks = new \SplObjectStorage();
  34. $this->resources = $resources;
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function initRuntime(\Twig_Environment $environment)
  40. {
  41. $this->environment = $environment;
  42. }
  43. /**
  44. * Sets a theme for a given view.
  45. *
  46. * @param FormView $view A FormView instance
  47. * @param array $resources An array of resources
  48. */
  49. public function setTheme(FormView $view, array $resources)
  50. {
  51. $this->themes->attach($view, $resources);
  52. $this->blocks = new \SplObjectStorage();
  53. }
  54. /**
  55. * Returns the token parser instance to add to the existing list.
  56. *
  57. * @return array An array of Twig_TokenParser instances
  58. */
  59. public function getTokenParsers()
  60. {
  61. return array(
  62. // {% form_theme form "SomeBundle::widgets.twig" %}
  63. new FormThemeTokenParser(),
  64. );
  65. }
  66. public function getFunctions()
  67. {
  68. return array(
  69. 'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
  70. 'form_widget' => new \Twig_Function_Method($this, 'renderWidget', array('is_safe' => array('html'))),
  71. 'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
  72. 'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
  73. 'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
  74. 'form_rest' => new \Twig_Function_Method($this, 'renderRest', array('is_safe' => array('html'))),
  75. '_form_is_choice_group' => new \Twig_Function_Method($this, 'isChoiceGroup', array('is_safe' => array('html'))),
  76. '_form_is_choice_selected' => new \Twig_Function_Method($this, 'isChoiceSelected', array('is_safe' => array('html'))),
  77. );
  78. }
  79. public function isChoiceGroup($label)
  80. {
  81. return FormUtil::isChoiceGroup($label);
  82. }
  83. public function isChoiceSelected(FormView $view, $choice)
  84. {
  85. return FormUtil::isChoiceSelected($choice, $view->get('value'));
  86. }
  87. /**
  88. * Renders the HTML enctype in the form tag, if necessary
  89. *
  90. * Example usage in Twig templates:
  91. *
  92. * <form action="..." method="post" {{ form_enctype(form) }}>
  93. *
  94. * @param FormView $view The view for which to render the encoding type
  95. *
  96. * @return string The html markup
  97. */
  98. public function renderEnctype(FormView $view)
  99. {
  100. return $this->render($view, 'enctype');
  101. }
  102. /**
  103. * Renders a row for the view.
  104. *
  105. * @param FormView $view The view to render as a row
  106. * @param array $variables An array of variables
  107. *
  108. * @return string The html markup
  109. */
  110. public function renderRow(FormView $view, array $variables = array())
  111. {
  112. return $this->render($view, 'row', $variables);
  113. }
  114. /**
  115. * Renders views which have not already been rendered.
  116. *
  117. * @param FormView $view The parent view
  118. * @param array $variables An array of variables
  119. *
  120. * @return string The html markup
  121. */
  122. public function renderRest(FormView $view, array $variables = array())
  123. {
  124. return $this->render($view, 'rest', $variables);
  125. }
  126. /**
  127. * Renders the HTML for a given view
  128. *
  129. * Example usage in Twig:
  130. *
  131. * {{ form_widget(view) }}
  132. *
  133. * You can pass options during the call:
  134. *
  135. * {{ form_widget(view, {'attr': {'class': 'foo'}}) }}
  136. *
  137. * {{ form_widget(view, {'separator': '+++++'}) }}
  138. *
  139. * @param FormView $view The view to render
  140. * @param array $variables Additional variables passed to the template
  141. *
  142. * @return string The html markup
  143. */
  144. public function renderWidget(FormView $view, array $variables = array())
  145. {
  146. return $this->render($view, 'widget', $variables);
  147. }
  148. /**
  149. * Renders the errors of the given view
  150. *
  151. * @param FormView $view The view to render the errors for
  152. *
  153. * @return string The html markup
  154. */
  155. public function renderErrors(FormView $view)
  156. {
  157. return $this->render($view, 'errors');
  158. }
  159. /**
  160. * Renders the label of the given view
  161. *
  162. * @param FormView $view The view to render the label for
  163. * @param string $label Label name
  164. * @param array $variables Additional variables passed to the template
  165. *
  166. * @return string The html markup
  167. */
  168. public function renderLabel(FormView $view, $label = null, array $variables = array())
  169. {
  170. if ($label !== null) {
  171. $variables += array('label' => $label);
  172. }
  173. return $this->render($view, 'label', $variables);
  174. }
  175. /**
  176. * Renders a template.
  177. *
  178. * 1. This function first looks for a block named "_<view id>_<section>",
  179. * 2. if such a block is not found the function will look for a block named
  180. * "<type name>_<section>",
  181. * 3. the type name is recursively replaced by the parent type name until a
  182. * corresponding block is found
  183. *
  184. * @param FormView $view The form view
  185. * @param string $section The section to render (i.e. 'row', 'widget', 'label', ...)
  186. * @param array $variables Additional variables
  187. *
  188. * @return string The html markup
  189. *
  190. * @throws FormException if no template block exists to render the given section of the view
  191. */
  192. protected function render(FormView $view, $section, array $variables = array())
  193. {
  194. $mainTemplate = in_array($section, array('widget', 'row'));
  195. if ($mainTemplate && $view->isRendered()) {
  196. return '';
  197. }
  198. if (null === $this->template) {
  199. $this->template = reset($this->resources);
  200. if (!$this->template instanceof \Twig_Template) {
  201. $this->template = $this->environment->loadTemplate($this->template);
  202. }
  203. }
  204. $custom = '_'.$view->get('id');
  205. $rendering = $custom.$section;
  206. $blocks = $this->getBlocks($view);
  207. if (isset($this->varStack[$rendering])) {
  208. $typeIndex = $this->varStack[$rendering]['typeIndex'] - 1;
  209. $types = $this->varStack[$rendering]['types'];
  210. $this->varStack[$rendering]['variables'] = array_replace_recursive($this->varStack[$rendering]['variables'], $variables);
  211. } else {
  212. $types = $view->get('types');
  213. $types[] = $custom;
  214. $typeIndex = count($types) - 1;
  215. $this->varStack[$rendering] = array (
  216. 'variables' => array_replace_recursive($view->all(), $variables),
  217. 'types' => $types,
  218. );
  219. }
  220. do {
  221. $types[$typeIndex] .= '_'.$section;
  222. if (isset($blocks[$types[$typeIndex]])) {
  223. $this->varStack[$rendering]['typeIndex'] = $typeIndex;
  224. // we do not call renderBlock here to avoid too many nested level calls (XDebug limits the level to 100 by default)
  225. ob_start();
  226. $this->template->displayBlock($types[$typeIndex], $this->varStack[$rendering]['variables'], $blocks);
  227. $html = ob_get_clean();
  228. if ($mainTemplate) {
  229. $view->setRendered();
  230. }
  231. unset($this->varStack[$rendering]);
  232. return $html;
  233. }
  234. } while (--$typeIndex >= 0);
  235. throw new FormException(sprintf(
  236. 'Unable to render the form as none of the following blocks exist: "%s".',
  237. implode('", "', array_reverse($types))
  238. ));
  239. }
  240. /**
  241. * Returns the name of the extension.
  242. *
  243. * @return string The extension name
  244. */
  245. public function getName()
  246. {
  247. return 'form';
  248. }
  249. /**
  250. * Returns the blocks used to render the view.
  251. *
  252. * Templates are looked for in the resources in the following order:
  253. * * resources from the themes (and its parents)
  254. * * resources from the themes of parent views (up to the root view)
  255. * * default resources
  256. *
  257. * @param FormView $view The view
  258. *
  259. * @return array An array of Twig_TemplateInterface instances
  260. */
  261. protected function getBlocks(FormView $view)
  262. {
  263. if (!$this->blocks->contains($view)) {
  264. $rootView = !$view->hasParent();
  265. $templates = $rootView ? $this->resources : array();
  266. if (isset($this->themes[$view])) {
  267. $templates = array_merge($templates, $this->themes[$view]);
  268. }
  269. $blocks = array();
  270. foreach ($templates as $template) {
  271. if (!$template instanceof \Twig_Template) {
  272. $template = $this->environment->loadTemplate($template);
  273. }
  274. $templateBlocks = array();
  275. do {
  276. $templateBlocks = array_merge($template->getBlocks(), $templateBlocks);
  277. } while (false !== $template = $template->getParent(array()));
  278. $blocks = array_merge($blocks, $templateBlocks);
  279. }
  280. if (!$rootView) {
  281. $blocks = array_merge($this->getBlocks($view->getParent()), $blocks);
  282. }
  283. $this->blocks->attach($view, $blocks);
  284. } else {
  285. $blocks = $this->blocks[$view];
  286. }
  287. return $blocks;
  288. }
  289. }