PageRenderTime 25ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/symfony/src/Symfony/Component/Form/FormRenderer.php

https://gitlab.com/cuza/Clinic_Recods
PHP | 309 lines | 128 code | 55 blank | 126 comment | 24 complexity | 7aa95ed9101933139f6353f47f4079c1 MD5 | raw file
  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\Component\Form;
  11. use Symfony\Component\Form\Exception\LogicException;
  12. use Symfony\Component\Form\Exception\BadMethodCallException;
  13. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  14. /**
  15. * Renders a form into HTML using a rendering engine.
  16. *
  17. * @author Bernhard Schussek <bschussek@gmail.com>
  18. */
  19. class FormRenderer implements FormRendererInterface
  20. {
  21. const CACHE_KEY_VAR = 'unique_block_prefix';
  22. /**
  23. * @var FormRendererEngineInterface
  24. */
  25. private $engine;
  26. /**
  27. * @var CsrfTokenManagerInterface
  28. */
  29. private $csrfTokenManager;
  30. /**
  31. * @var array
  32. */
  33. private $blockNameHierarchyMap = array();
  34. /**
  35. * @var array
  36. */
  37. private $hierarchyLevelMap = array();
  38. /**
  39. * @var array
  40. */
  41. private $variableStack = array();
  42. /**
  43. * Constructor.
  44. *
  45. * @param FormRendererEngineInterface $engine
  46. * @param CsrfTokenManagerInterface|null $csrfTokenManager
  47. */
  48. public function __construct(FormRendererEngineInterface $engine, CsrfTokenManagerInterface $csrfTokenManager = null)
  49. {
  50. $this->engine = $engine;
  51. $this->csrfTokenManager = $csrfTokenManager;
  52. }
  53. /**
  54. * {@inheritdoc}
  55. */
  56. public function getEngine()
  57. {
  58. return $this->engine;
  59. }
  60. /**
  61. * {@inheritdoc}
  62. */
  63. public function setTheme(FormView $view, $themes)
  64. {
  65. $this->engine->setTheme($view, $themes);
  66. }
  67. /**
  68. * {@inheritdoc}
  69. */
  70. public function renderCsrfToken($tokenId)
  71. {
  72. if (null === $this->csrfTokenManager) {
  73. throw new BadMethodCallException('CSRF tokens can only be generated if a CsrfTokenManagerInterface is injected in FormRenderer::__construct().');
  74. }
  75. return $this->csrfTokenManager->getToken($tokenId)->getValue();
  76. }
  77. /**
  78. * {@inheritdoc}
  79. */
  80. public function renderBlock(FormView $view, $blockName, array $variables = array())
  81. {
  82. $resource = $this->engine->getResourceForBlockName($view, $blockName);
  83. if (!$resource) {
  84. throw new LogicException(sprintf('No block "%s" found while rendering the form.', $blockName));
  85. }
  86. $viewCacheKey = $view->vars[self::CACHE_KEY_VAR];
  87. // The variables are cached globally for a view (instead of for the
  88. // current suffix)
  89. if (!isset($this->variableStack[$viewCacheKey])) {
  90. $this->variableStack[$viewCacheKey] = array();
  91. // The default variable scope contains all view variables, merged with
  92. // the variables passed explicitly to the helper
  93. $scopeVariables = $view->vars;
  94. $varInit = true;
  95. } else {
  96. // Reuse the current scope and merge it with the explicitly passed variables
  97. $scopeVariables = end($this->variableStack[$viewCacheKey]);
  98. $varInit = false;
  99. }
  100. // Merge the passed with the existing attributes
  101. if (isset($variables['attr']) && isset($scopeVariables['attr'])) {
  102. $variables['attr'] = array_replace($scopeVariables['attr'], $variables['attr']);
  103. }
  104. // Merge the passed with the exist *label* attributes
  105. if (isset($variables['label_attr']) && isset($scopeVariables['label_attr'])) {
  106. $variables['label_attr'] = array_replace($scopeVariables['label_attr'], $variables['label_attr']);
  107. }
  108. // Do not use array_replace_recursive(), otherwise array variables
  109. // cannot be overwritten
  110. $variables = array_replace($scopeVariables, $variables);
  111. $this->variableStack[$viewCacheKey][] = $variables;
  112. // Do the rendering
  113. $html = $this->engine->renderBlock($view, $resource, $blockName, $variables);
  114. // Clear the stack
  115. array_pop($this->variableStack[$viewCacheKey]);
  116. if ($varInit) {
  117. unset($this->variableStack[$viewCacheKey]);
  118. }
  119. return $html;
  120. }
  121. /**
  122. * {@inheritdoc}
  123. */
  124. public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = array())
  125. {
  126. $renderOnlyOnce = 'row' === $blockNameSuffix || 'widget' === $blockNameSuffix;
  127. if ($renderOnlyOnce && $view->isRendered()) {
  128. return '';
  129. }
  130. // The cache key for storing the variables and types
  131. $viewCacheKey = $view->vars[self::CACHE_KEY_VAR];
  132. $viewAndSuffixCacheKey = $viewCacheKey.$blockNameSuffix;
  133. // In templates, we have to deal with two kinds of block hierarchies:
  134. //
  135. // +---------+ +---------+
  136. // | Theme B | -------> | Theme A |
  137. // +---------+ +---------+
  138. //
  139. // form_widget -------> form_widget
  140. // ^
  141. // |
  142. // choice_widget -----> choice_widget
  143. //
  144. // The first kind of hierarchy is the theme hierarchy. This allows to
  145. // override the block "choice_widget" from Theme A in the extending
  146. // Theme B. This kind of inheritance needs to be supported by the
  147. // template engine and, for example, offers "parent()" or similar
  148. // functions to fall back from the custom to the parent implementation.
  149. //
  150. // The second kind of hierarchy is the form type hierarchy. This allows
  151. // to implement a custom "choice_widget" block (no matter in which theme),
  152. // or to fallback to the block of the parent type, which would be
  153. // "form_widget" in this example (again, no matter in which theme).
  154. // If the designer wants to explicitly fallback to "form_widget" in his
  155. // custom "choice_widget", for example because he only wants to wrap
  156. // a <div> around the original implementation, he can simply call the
  157. // widget() function again to render the block for the parent type.
  158. //
  159. // The second kind is implemented in the following blocks.
  160. if (!isset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey])) {
  161. // INITIAL CALL
  162. // Calculate the hierarchy of template blocks and start on
  163. // the bottom level of the hierarchy (= "_<id>_<section>" block)
  164. $blockNameHierarchy = array();
  165. foreach ($view->vars['block_prefixes'] as $blockNamePrefix) {
  166. $blockNameHierarchy[] = $blockNamePrefix.'_'.$blockNameSuffix;
  167. }
  168. $hierarchyLevel = count($blockNameHierarchy) - 1;
  169. $hierarchyInit = true;
  170. } else {
  171. // RECURSIVE CALL
  172. // If a block recursively calls searchAndRenderBlock() again, resume rendering
  173. // using the parent type in the hierarchy.
  174. $blockNameHierarchy = $this->blockNameHierarchyMap[$viewAndSuffixCacheKey];
  175. $hierarchyLevel = $this->hierarchyLevelMap[$viewAndSuffixCacheKey] - 1;
  176. $hierarchyInit = false;
  177. }
  178. // The variables are cached globally for a view (instead of for the
  179. // current suffix)
  180. if (!isset($this->variableStack[$viewCacheKey])) {
  181. $this->variableStack[$viewCacheKey] = array();
  182. // The default variable scope contains all view variables, merged with
  183. // the variables passed explicitly to the helper
  184. $scopeVariables = $view->vars;
  185. $varInit = true;
  186. } else {
  187. // Reuse the current scope and merge it with the explicitly passed variables
  188. $scopeVariables = end($this->variableStack[$viewCacheKey]);
  189. $varInit = false;
  190. }
  191. // Load the resource where this block can be found
  192. $resource = $this->engine->getResourceForBlockNameHierarchy($view, $blockNameHierarchy, $hierarchyLevel);
  193. // Update the current hierarchy level to the one at which the resource was
  194. // found. For example, if looking for "choice_widget", but only a resource
  195. // is found for its parent "form_widget", then the level is updated here
  196. // to the parent level.
  197. $hierarchyLevel = $this->engine->getResourceHierarchyLevel($view, $blockNameHierarchy, $hierarchyLevel);
  198. // The actually existing block name in $resource
  199. $blockName = $blockNameHierarchy[$hierarchyLevel];
  200. // Escape if no resource exists for this block
  201. if (!$resource) {
  202. throw new LogicException(sprintf(
  203. 'Unable to render the form as none of the following blocks exist: "%s".',
  204. implode('", "', array_reverse($blockNameHierarchy))
  205. ));
  206. }
  207. // Merge the passed with the existing attributes
  208. if (isset($variables['attr']) && isset($scopeVariables['attr'])) {
  209. $variables['attr'] = array_replace($scopeVariables['attr'], $variables['attr']);
  210. }
  211. // Merge the passed with the exist *label* attributes
  212. if (isset($variables['label_attr']) && isset($scopeVariables['label_attr'])) {
  213. $variables['label_attr'] = array_replace($scopeVariables['label_attr'], $variables['label_attr']);
  214. }
  215. // Do not use array_replace_recursive(), otherwise array variables
  216. // cannot be overwritten
  217. $variables = array_replace($scopeVariables, $variables);
  218. // In order to make recursive calls possible, we need to store the block hierarchy,
  219. // the current level of the hierarchy and the variables so that this method can
  220. // resume rendering one level higher of the hierarchy when it is called recursively.
  221. //
  222. // We need to store these values in maps (associative arrays) because within a
  223. // call to widget() another call to widget() can be made, but for a different view
  224. // object. These nested calls should not override each other.
  225. $this->blockNameHierarchyMap[$viewAndSuffixCacheKey] = $blockNameHierarchy;
  226. $this->hierarchyLevelMap[$viewAndSuffixCacheKey] = $hierarchyLevel;
  227. // We also need to store the variables for the view so that we can render other
  228. // blocks for the same view using the same variables as in the outer block.
  229. $this->variableStack[$viewCacheKey][] = $variables;
  230. // Do the rendering
  231. $html = $this->engine->renderBlock($view, $resource, $blockName, $variables);
  232. // Clear the stack
  233. array_pop($this->variableStack[$viewCacheKey]);
  234. // Clear the caches if they were filled for the first time within
  235. // this function call
  236. if ($hierarchyInit) {
  237. unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey], $this->hierarchyLevelMap[$viewAndSuffixCacheKey]);
  238. }
  239. if ($varInit) {
  240. unset($this->variableStack[$viewCacheKey]);
  241. }
  242. if ($renderOnlyOnce) {
  243. $view->setRendered();
  244. }
  245. return $html;
  246. }
  247. /**
  248. * {@inheritdoc}
  249. */
  250. public function humanize($text)
  251. {
  252. return ucfirst(trim(strtolower(preg_replace(array('/([A-Z])/', '/[_\s]+/'), array('_$1', ' '), $text))));
  253. }
  254. }