public ThemeManager::render($hook, array $variables)
Generates themed output.
See the Default theme implementations topic for details.
Parameters
string $hook: The name of the theme hook to call.
array $variables: An associative array of theme variables.
Return value
string|\Drupal\Component\Render\MarkupInterface The rendered output, or a Markup object.
Overrides ThemeManagerInterface::render
File
- core/lib/Drupal/Core/Theme/ThemeManager.php, line 130
Class
- ThemeManager
- Provides the default implementation of a theme manager.
Namespace
Drupal\Core\Theme
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | public function render( $hook , array $variables ) { static $default_attributes ; $active_theme = $this ->getActiveTheme(); // If called before all modules are loaded, we do not necessarily have a // full theme registry to work with, and therefore cannot process the theme // request properly. See also \Drupal\Core\Theme\Registry::get(). if (! $this ->moduleHandler->isLoaded() && !defined( 'MAINTENANCE_MODE' )) { throw new \Exception( 'The theme implementations may not be rendered until all modules are loaded.' ); } $theme_registry = $this ->themeRegistry->getRuntime(); // If an array of hook candidates were passed, use the first one that has an // implementation. if ( is_array ( $hook )) { foreach ( $hook as $candidate ) { if ( $theme_registry ->has( $candidate )) { break ; } } $hook = $candidate ; } // Save the original theme hook, so it can be supplied to theme variable // preprocess callbacks. $original_hook = $hook ; // If there's no implementation, check for more generic fallbacks. // If there's still no implementation, log an error and return an empty // string. if (! $theme_registry ->has( $hook )) { // Iteratively strip everything after the last '__' delimiter, until an // implementation is found. while ( $pos = strrpos ( $hook , '__' )) { $hook = substr ( $hook , 0, $pos ); if ( $theme_registry ->has( $hook )) { break ; } } if (! $theme_registry ->has( $hook )) { // Only log a message when not trying theme suggestions ($hook being an // array). if (!isset( $candidate )) { \Drupal::logger( 'theme' )->warning( 'Theme hook %hook not found.' , array ( '%hook' => $hook )); } // There is no theme implementation for the hook passed. Return FALSE so // the function calling // \Drupal\Core\Theme\ThemeManagerInterface::render() can differentiate // between a hook that exists and renders an empty string, and a hook // that is not implemented. return FALSE; } } $info = $theme_registry ->get( $hook ); // If a renderable array is passed as $variables, then set $variables to // the arguments expected by the theme function. if (isset( $variables [ '#theme' ]) || isset( $variables [ '#theme_wrappers' ])) { $element = $variables ; $variables = array (); if (isset( $info [ 'variables' ])) { foreach ( array_keys ( $info [ 'variables' ]) as $name ) { if (isset( $element [ "#$name" ]) || array_key_exists ( "#$name" , $element )) { $variables [ $name ] = $element [ "#$name" ]; } } } else { $variables [ $info [ 'render element' ]] = $element ; // Give a hint to render engines to prevent infinite recursion. $variables [ $info [ 'render element' ]][ '#render_children' ] = TRUE; } } // Merge in argument defaults. if (! empty ( $info [ 'variables' ])) { $variables += $info [ 'variables' ]; } elseif (! empty ( $info [ 'render element' ])) { $variables += array ( $info [ 'render element' ] => array ()); } // Supply original caller info. $variables += array ( 'theme_hook_original' => $original_hook , ); // Set base hook for later use. For example if '#theme' => 'node__article' // is called, we run hook_theme_suggestions_node_alter() rather than // hook_theme_suggestions_node__article_alter(), and also pass in the base // hook as the last parameter to the suggestions alter hooks. if (isset( $info [ 'base hook' ])) { $base_theme_hook = $info [ 'base hook' ]; } else { $base_theme_hook = $hook ; } // Invoke hook_theme_suggestions_HOOK(). $suggestions = $this ->moduleHandler->invokeAll( 'theme_suggestions_' . $base_theme_hook , array ( $variables )); // If the theme implementation was invoked with a direct theme suggestion // like '#theme' => 'node__article', add it to the suggestions array before // invoking suggestion alter hooks. if (isset( $info [ 'base hook' ])) { $suggestions [] = $hook ; } // Invoke hook_theme_suggestions_alter() and // hook_theme_suggestions_HOOK_alter(). $hooks = array ( 'theme_suggestions' , 'theme_suggestions_' . $base_theme_hook , ); $this ->moduleHandler->alter( $hooks , $suggestions , $variables , $base_theme_hook ); $this ->alter( $hooks , $suggestions , $variables , $base_theme_hook ); // Check if each suggestion exists in the theme registry, and if so, // use it instead of the base hook. For example, a function may use // '#theme' => 'node', but a module can add 'node__article' as a suggestion // via hook_theme_suggestions_HOOK_alter(), enabling a theme to have // an alternate template file for article nodes. foreach ( array_reverse ( $suggestions ) as $suggestion ) { if ( $theme_registry ->has( $suggestion )) { $info = $theme_registry ->get( $suggestion ); break ; } } // Include a file if the theme function or variable preprocessor is held // elsewhere. if (! empty ( $info [ 'includes' ])) { foreach ( $info [ 'includes' ] as $include_file ) { include_once $this ->root . '/' . $include_file ; } } // Invoke the variable preprocessors, if any. if (isset( $info [ 'base hook' ])) { $base_hook = $info [ 'base hook' ]; $base_hook_info = $theme_registry ->get( $base_hook ); // Include files required by the base hook, since its variable // preprocessors might reside there. if (! empty ( $base_hook_info [ 'includes' ])) { foreach ( $base_hook_info [ 'includes' ] as $include_file ) { include_once $this ->root . '/' . $include_file ; } } if (isset( $base_hook_info [ 'preprocess functions' ])) { // Set a variable for the 'theme_hook_suggestion'. This is used to // maintain backwards compatibility with template engines. $theme_hook_suggestion = $hook ; } } if (isset( $info [ 'preprocess functions' ])) { foreach ( $info [ 'preprocess functions' ] as $preprocessor_function ) { if (function_exists( $preprocessor_function )) { $preprocessor_function ( $variables , $hook , $info ); } } // Allow theme preprocess functions to set $variables['#attached'] and // $variables['#cache'] and use them like the corresponding element // properties on render arrays. In Drupal 8, this is the (only) officially // supported method of attaching bubbleable metadata from preprocess // functions. Assets attached here should be associated with the template // that we are preprocessing variables for. $preprocess_bubbleable = []; foreach ([ '#attached' , '#cache' ] as $key ) { if (isset( $variables [ $key ])) { $preprocess_bubbleable [ $key ] = $variables [ $key ]; } } // We do not allow preprocess functions to define cacheable elements. unset( $preprocess_bubbleable [ '#cache' ][ 'keys' ]); if ( $preprocess_bubbleable ) { // @todo Inject the Renderer in https://www.drupal.org/node/2529438. drupal_render( $preprocess_bubbleable ); } } // Generate the output using either a function or a template. $output = '' ; if (isset( $info [ 'function' ])) { if (function_exists( $info [ 'function' ])) { // Theme functions do not render via the theme engine, so the output is // not autoescaped. However, we can only presume that the theme function // has been written correctly and that the markup is safe. $output = Markup::create( $info [ 'function' ]( $variables )); } } else { $render_function = 'twig_render_template' ; $extension = '.html.twig' ; // The theme engine may use a different extension and a different // renderer. $theme_engine = $active_theme ->getEngine(); if (isset( $theme_engine )) { if ( $info [ 'type' ] != 'module' ) { if (function_exists( $theme_engine . '_render_template' )) { $render_function = $theme_engine . '_render_template' ; } $extension_function = $theme_engine . '_extension' ; if (function_exists( $extension_function )) { $extension = $extension_function (); } } } // In some cases, a template implementation may not have had // template_preprocess() run (for example, if the default implementation // is a function, but a template overrides that default implementation). // In these cases, a template should still be able to expect to have // access to the variables provided by template_preprocess(), so we add // them here if they don't already exist. We don't want the overhead of // running template_preprocess() twice, so we use the 'directory' variable // to determine if it has already run, which while not completely // intuitive, is reasonably safe, and allows us to save on the overhead of // adding some new variable to track that. if (!isset( $variables [ 'directory' ])) { $default_template_variables = array (); template_preprocess( $default_template_variables , $hook , $info ); $variables += $default_template_variables ; } if (!isset( $default_attributes )) { $default_attributes = new Attribute(); } foreach ( array ( 'attributes' , 'title_attributes' , 'content_attributes' ) as $key ) { if (isset( $variables [ $key ]) && !( $variables [ $key ] instanceof Attribute)) { if ( $variables [ $key ]) { $variables [ $key ] = new Attribute( $variables [ $key ]); } else { // Create empty attributes. $variables [ $key ] = clone $default_attributes ; } } } // Render the output using the template file. $template_file = $info [ 'template' ] . $extension ; if (isset( $info [ 'path' ])) { $template_file = $info [ 'path' ] . '/' . $template_file ; } // Add the theme suggestions to the variables array just before rendering // the template for backwards compatibility with template engines. $variables [ 'theme_hook_suggestions' ] = $suggestions ; // For backwards compatibility, pass 'theme_hook_suggestion' on to the // template engine. This is only set when calling a direct suggestion like // '#theme' => 'menu__shortcut_default' when the template exists in the // current theme. if (isset( $theme_hook_suggestion )) { $variables [ 'theme_hook_suggestion' ] = $theme_hook_suggestion ; } $output = $render_function ( $template_file , $variables ); } return ( $output instanceof MarkupInterface) ? $output : (string) $output ; } |
Please login to continue.