]> git.immae.eu Git - github/wallabag/wallabag.git/blob - vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngine.php
twig implementation
[github/wallabag/wallabag.git] / vendor / symfony / twig-bridge / Symfony / Bridge / Twig / Form / TwigRendererEngine.php
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Bridge\Twig\Form;
13
14 use Symfony\Component\Form\AbstractRendererEngine;
15 use Symfony\Component\Form\FormView;
16
17 /**
18 * @author Bernhard Schussek <bschussek@gmail.com>
19 */
20 class TwigRendererEngine extends AbstractRendererEngine implements TwigRendererEngineInterface
21 {
22 /**
23 * @var \Twig_Environment
24 */
25 private $environment;
26
27 /**
28 * @var \Twig_Template
29 */
30 private $template;
31
32 /**
33 * {@inheritdoc}
34 */
35 public function setEnvironment(\Twig_Environment $environment)
36 {
37 $this->environment = $environment;
38 }
39
40 /**
41 * {@inheritdoc}
42 */
43 public function renderBlock(FormView $view, $resource, $blockName, array $variables = array())
44 {
45 $cacheKey = $view->vars[self::CACHE_KEY_VAR];
46
47 $context = $this->environment->mergeGlobals($variables);
48
49 ob_start();
50
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.
55
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]);
59
60 return ob_get_clean();
61 }
62
63 /**
64 * Loads the cache with the resource for a given block name.
65 *
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.
70 *
71 * @see getResourceForBlock()
72 *
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.
76 *
77 * @return Boolean True if the resource could be loaded, false otherwise.
78 */
79 protected function loadResourceForBlockName($cacheKey, FormView $view, $blockName)
80 {
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;
90
91 return false;
92 }
93
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.
98
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)
104 }
105 }
106
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)
112 }
113 }
114
115 // Proceed with the themes of the parent view
116 if ($view->parent) {
117 $parentCacheKey = $view->parent->vars[self::CACHE_KEY_VAR];
118
119 if (!isset($this->resources[$parentCacheKey])) {
120 $this->loadResourceForBlockName($parentCacheKey, $view->parent, $blockName);
121 }
122
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;
127 }
128 }
129 }
130
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;
136 }
137
138 return false !== $this->resources[$cacheKey][$blockName];
139 }
140
141 /**
142 * Loads the resources for all blocks in a theme.
143 *
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.
150 */
151 protected function loadResourcesFromTheme($cacheKey, &$theme)
152 {
153 if (!$theme instanceof \Twig_Template) {
154 /* @var \Twig_Template $theme */
155 $theme = $this->environment->loadTemplate($theme);
156 }
157
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
162 // anyway.
163 $this->template = $theme;
164 }
165
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;
169
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.
173 do {
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;
179 }
180 }
181 } while (false !== $currentTheme = $currentTheme->getParent(array()));
182 }
183 }