4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Bridge\Twig\Form
;
14 use Symfony\Component\Form\AbstractRendererEngine
;
15 use Symfony\Component\Form\FormView
;
18 * @author Bernhard Schussek <bschussek@gmail.com>
20 class TwigRendererEngine
extends AbstractRendererEngine
implements TwigRendererEngineInterface
23 * @var \Twig_Environment
35 public function setEnvironment(\Twig_Environment
$environment)
37 $this->environment
= $environment;
43 public function renderBlock(FormView
$view, $resource, $blockName, array $variables = array())
45 $cacheKey = $view->vars
[self
::CACHE_KEY_VAR
];
47 $context = $this->environment
->mergeGlobals($variables);
51 // By contract,This method can only be called after getting the resource
52 // (which is passed to the method). Getting a resource for the first time
53 // (with an empty cache) is guaranteed to invoke loadResourcesFromTheme(),
54 // where the property $template is initialized.
56 // We do not call renderBlock here to avoid too many nested level calls
57 // (XDebug limits the level to 100 by default)
58 $this->template
->displayBlock($blockName, $context, $this->resources
[$cacheKey]);
60 return ob_get_clean();
64 * Loads the cache with the resource for a given block name.
66 * This implementation eagerly loads all blocks of the themes assigned to the given view
67 * and all of its ancestors views. This is necessary, because Twig receives the
68 * list of blocks later. At that point, all blocks must already be loaded, for the
69 * case that the function "block()" is used in the Twig template.
71 * @see getResourceForBlock()
73 * @param string $cacheKey The cache key of the form view.
74 * @param FormView $view The form view for finding the applying themes.
75 * @param string $blockName The name of the block to load.
77 * @return Boolean True if the resource could be loaded, false otherwise.
79 protected function loadResourceForBlockName($cacheKey, FormView
$view, $blockName)
81 // The caller guarantees that $this->resources[$cacheKey][$block] is
82 // not set, but it doesn't have to check whether $this->resources[$cacheKey]
83 // is set. If $this->resources[$cacheKey] is set, all themes for this
84 // $cacheKey are already loaded (due to the eager population, see doc comment).
85 if (isset($this->resources
[$cacheKey])) {
86 // As said in the previous, the caller guarantees that
87 // $this->resources[$cacheKey][$block] is not set. Since the themes are
88 // already loaded, it can only be a non-existing block.
89 $this->resources
[$cacheKey][$blockName] = false;
94 // Recursively try to find the block in the themes assigned to $view,
95 // then of its parent view, then of the parent view of the parent and so on.
96 // When the root view is reached in this recursion, also the default
97 // themes are taken into account.
99 // Check each theme whether it contains the searched block
100 if (isset($this->themes
[$cacheKey])) {
101 for ($i = count($this->themes
[$cacheKey]) - 1; $i >= 0; --$i) {
102 $this->loadResourcesFromTheme($cacheKey, $this->themes
[$cacheKey][$i]);
103 // CONTINUE LOADING (see doc comment)
107 // Check the default themes once we reach the root view without success
108 if (!$view->parent
) {
109 for ($i = count($this->defaultThemes
) - 1; $i >= 0; --$i) {
110 $this->loadResourcesFromTheme($cacheKey, $this->defaultThemes
[$i]);
111 // CONTINUE LOADING (see doc comment)
115 // Proceed with the themes of the parent view
117 $parentCacheKey = $view->parent
->vars
[self
::CACHE_KEY_VAR
];
119 if (!isset($this->resources
[$parentCacheKey])) {
120 $this->loadResourceForBlockName($parentCacheKey, $view->parent
, $blockName);
123 // EAGER CACHE POPULATION (see doc comment)
124 foreach ($this->resources
[$parentCacheKey] as $nestedBlockName => $resource) {
125 if (!isset($this->resources
[$cacheKey][$nestedBlockName])) {
126 $this->resources
[$cacheKey][$nestedBlockName] = $resource;
131 // Even though we loaded the themes, it can happen that none of them
132 // contains the searched block
133 if (!isset($this->resources
[$cacheKey][$blockName])) {
134 // Cache that we didn't find anything to speed up further accesses
135 $this->resources
[$cacheKey][$blockName] = false;
138 return false !== $this->resources
[$cacheKey][$blockName];
142 * Loads the resources for all blocks in a theme.
144 * @param string $cacheKey The cache key for storing the resource.
145 * @param mixed $theme The theme to load the block from. This parameter
146 * is passed by reference, because it might be necessary
147 * to initialize the theme first. Any changes made to
148 * this variable will be kept and be available upon
149 * further calls to this method using the same theme.
151 protected function loadResourcesFromTheme($cacheKey, &$theme)
153 if (!$theme instanceof \Twig_Template
) {
154 /* @var \Twig_Template $theme */
155 $theme = $this->environment
->loadTemplate($theme);
158 if (null === $this->template
) {
159 // Store the first \Twig_Template instance that we find so that
160 // we can call displayBlock() later on. It doesn't matter *which*
161 // template we use for that, since we pass the used blocks manually
163 $this->template
= $theme;
166 // Use a separate variable for the inheritance traversal, because
167 // theme is a reference and we don't want to change it.
168 $currentTheme = $theme;
170 // The do loop takes care of template inheritance.
171 // Add blocks from all templates in the inheritance tree, but avoid
172 // overriding blocks already set.
174 foreach ($currentTheme->getBlocks() as $block => $blockData) {
175 if (!isset($this->resources
[$cacheKey][$block])) {
176 // The resource given back is the key to the bucket that
177 // contains this block.
178 $this->resources
[$cacheKey][$block] = $blockData;
181 } while (false !== $currentTheme = $currentTheme->getParent(array()));