Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -342,9 +342,12 @@ class Registry implements DestructableInterface {
$this->processExtension($cache, $this->theme->getEngine(), 'theme_engine', $this->theme->getName(), $this->theme->getPath());
}
// Finally, hooks provided by the theme itself.
// Hooks provided by the theme itself.
$this->processExtension($cache, $this->theme->getName(), 'theme', $this->theme->getName(), $this->theme->getPath());
// Discover and add all preprocess functions for theme hook suggestions.
$this->postProcessExtension($cache, $this->theme);
// Let modules and themes alter the registry.
$this->moduleHandler->alter('theme_registry', $cache);
$this->themeManager->alterForTheme($this->theme, 'theme_registry', $cache);
@ -420,7 +423,7 @@ class Registry implements DestructableInterface {
'base hook' => TRUE,
);
$module_list = array_keys((array) $this->moduleHandler->getModuleList());
$module_list = array_keys($this->moduleHandler->getModuleList());
// Invoke the hook_theme() implementation, preprocess what is returned, and
// merge it into $cache.
@ -438,10 +441,23 @@ class Registry implements DestructableInterface {
$result[$hook]['type'] = $type;
$result[$hook]['theme path'] = $path;
// If a theme hook has a base hook, mark its preprocess functions always
// incomplete in order to inherit the base hook's preprocess functions.
if (!empty($result[$hook]['base hook'])) {
$result[$hook]['incomplete preprocess functions'] = TRUE;
}
if (isset($cache[$hook]['includes'])) {
$result[$hook]['includes'] = $cache[$hook]['includes'];
}
// Load the includes, as they may contain preprocess functions.
if (isset($info['includes'])) {
foreach ($info['includes'] as $include_file) {
include_once $this->root . '/' . $include_file;
}
}
// If the theme implementation defines a file, then also use the path
// that it defined. Otherwise use the default path. This allows
// system.module to declare theme functions on behalf of core .include
@ -556,13 +572,142 @@ class Registry implements DestructableInterface {
$cache[$hook]['preprocess functions'][] = $name . '_preprocess_' . $hook;
$cache[$hook]['theme path'] = $path;
}
// Ensure uniqueness.
$cache[$hook]['preprocess functions'] = array_unique($cache[$hook]['preprocess functions']);
}
}
}
}
/**
* Completes the definition of the requested suggestion hook.
*
* @param string $hook
* The name of the suggestion hook to complete.
* @param array $cache
* The theme registry, as documented in
* \Drupal\Core\Theme\Registry::processExtension().
*/
protected function completeSuggestion($hook, array &$cache) {
$previous_hook = $hook;
$incomplete_previous_hook = array();
while ((!isset($cache[$previous_hook]) || isset($cache[$previous_hook]['incomplete preprocess functions']))
&& $pos = strrpos($previous_hook, '__')) {
if (isset($cache[$previous_hook]) && !$incomplete_previous_hook && isset($cache[$previous_hook]['incomplete preprocess functions'])) {
$incomplete_previous_hook = $cache[$previous_hook];
unset($incomplete_previous_hook['incomplete preprocess functions']);
}
$previous_hook = substr($previous_hook, 0, $pos);
// If base hook exists clone of it for the preprocess function
// without a template.
// @see https://www.drupal.org/node/2457295
if (isset($cache[$previous_hook]) && !isset($cache[$previous_hook]['incomplete preprocess functions'])) {
$cache[$hook] = $incomplete_previous_hook + $cache[$previous_hook];
if (isset($incomplete_previous_hook['preprocess functions'])) {
$diff = array_diff($incomplete_previous_hook['preprocess functions'], $cache[$previous_hook]['preprocess functions']);
$cache[$hook]['preprocess functions'] = array_merge($cache[$previous_hook]['preprocess functions'], $diff);
}
// If a base hook isn't set, this is the actual base hook.
if (!isset($cache[$previous_hook]['base hook'])) {
$cache[$hook]['base hook'] = $previous_hook;
}
}
}
}
/**
* Completes the theme registry adding discovered functions and hooks.
*
* @param array $cache
* The theme registry as documented in
* \Drupal\Core\Theme\Registry::processExtension().
* @param \Drupal\Core\Theme\ActiveTheme $theme
* Current active theme.
*
* @see ::processExtension()
*/
protected function postProcessExtension(array &$cache, ActiveTheme $theme) {
$grouped_functions = $this->getPrefixGroupedUserFunctions();
// Gather prefixes. This will be used to limit the found functions to the
// expected naming conventions.
$prefixes = array_keys((array) $this->moduleHandler->getModuleList());
foreach (array_reverse($theme->getBaseThemes()) as $base) {
$prefixes[] = $base->getName();
}
if ($theme->getEngine()) {
$prefixes[] = $theme->getEngine() . '_engine';
}
$prefixes[] = $theme->getName();
// Collect all variable preprocess functions in the correct order.
$suggestion_level = [];
$matches = [];
// Look for functions named according to the pattern and add them if they
// have matching hooks in the registry.
foreach ($prefixes as $prefix) {
// Grep only the functions which are within the prefix group.
list($first_prefix,) = explode('_', $prefix, 2);
if (!isset($grouped_functions[$first_prefix])) {
continue;
}
// Add the function and the name of the associated theme hook to the list
// of preprocess functions grouped by suggestion specificity if a matching
// base hook is found.
foreach ($grouped_functions[$first_prefix] as $candidate) {
if (preg_match("/^{$prefix}_preprocess_(((?:[^_]++|_(?!_))+)__.*)/", $candidate, $matches)) {
if (isset($cache[$matches[2]])) {
$level = substr_count($matches[1], '__');
$suggestion_level[$level][$candidate] = $matches[1];
}
}
}
}
// Add missing variable preprocessors. This is needed for modules that do
// not explicitly register the hook. For example, when a theme contains a
// variable preprocess function but it does not implement a template, it
// will go missing. This will add the expected function. It also allows
// modules or themes to have a variable process function based on a pattern
// even if the hook does not exist.
ksort($suggestion_level);
foreach ($suggestion_level as $level => $item) {
foreach ($item as $preprocessor => $hook) {
if (isset($cache[$hook]['preprocess functions']) && !in_array($hook, $cache[$hook]['preprocess functions'])) {
// Add missing preprocessor to existing hook.
$cache[$hook]['preprocess functions'][] = $preprocessor;
}
elseif (!isset($cache[$hook]) && strpos($hook, '__')) {
// Process non-existing hook and register it.
// Look for a previously defined hook that is either a less specific
// suggestion hook or the base hook.
$this->completeSuggestion($hook, $cache);
$cache[$hook]['preprocess functions'][] = $preprocessor;
}
}
}
// Inherit all base hook variable preprocess functions into suggestion
// hooks. This ensures that derivative hooks have a complete set of variable
// preprocess functions.
foreach ($cache as $hook => $info) {
// The 'base hook' is only applied to derivative hooks already registered
// from a pattern. This is typically set from
// drupal_find_theme_functions() and drupal_find_theme_templates().
if (isset($info['incomplete preprocess functions'])) {
$this->completeSuggestion($hook, $cache);
unset($cache[$hook]['incomplete preprocess functions']);
}
// Optimize the registry.
if (isset($cache[$hook]['preprocess functions']) && empty($cache[$hook]['preprocess functions'])) {
unset($cache[$hook]['preprocess functions']);
}
// Ensure uniqueness.
if (isset($cache[$hook]['preprocess functions'])) {
$cache[$hook]['preprocess functions'] = array_unique($cache[$hook]['preprocess functions']);
}
}
}
/**
* Invalidates theme registry caches.
*
@ -589,6 +734,25 @@ class Registry implements DestructableInterface {
}
}
/**
* Gets all user functions grouped by the word before the first underscore.
*
* @return array
* Functions grouped by the first prefix.
*/
public function getPrefixGroupedUserFunctions() {
$functions = get_defined_functions();
$grouped_functions = [];
// Splitting user defined functions into groups by the first prefix.
foreach ($functions['user'] as $function) {
list($first_prefix,) = explode('_', $function, 2);
$grouped_functions[$first_prefix][] = $function;
}
return $grouped_functions;
}
/**
* Wraps drupal_get_path().
*

View file

@ -7,12 +7,13 @@
namespace Drupal\Core\Theme;
use Drupal\Component\Utility\SafeStringInterface;
use Drupal\Core\Render\SafeString;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\StackedRouteMatchInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Template\Attribute;
use Drupal\Component\Utility\SafeMarkup;
/**
* Provides the default implementation of a theme manager.
@ -279,12 +280,10 @@ class ThemeManager implements ThemeManagerInterface {
include_once $this->root . '/' . $include_file;
}
}
// Replace the preprocess functions with those from the base hook.
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;
$info['preprocess functions'] = $base_hook_info['preprocess functions'];
}
}
if (isset($info['preprocess functions'])) {
@ -317,7 +316,10 @@ class ThemeManager implements ThemeManagerInterface {
$output = '';
if (isset($info['function'])) {
if (function_exists($info['function'])) {
$output = SafeMarkup::set($info['function']($variables));
// 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 = SafeString::create($info['function']($variables));
}
}
else {
@ -387,7 +389,7 @@ class ThemeManager implements ThemeManagerInterface {
$output = $render_function($template_file, $variables);
}
return (string) $output;
return ($output instanceof SafeStringInterface) ? $output : (string) $output;
}
/**

View file

@ -26,8 +26,8 @@ interface ThemeManagerInterface {
* @param array $variables
* An associative array of theme variables.
*
* @return string
* The rendered output.
* @return string|\Drupal\Component\Utility\SafeStringInterface
* The rendered output, or a SafeString object.
*/
public function render($hook, array $variables);

View file

@ -14,9 +14,6 @@ use Drupal\Core\Routing\RouteMatchInterface;
*
* It therefore uses ThemeNegotiatorInterface objects which are passed in
* using the 'theme_negotiator' tag.
*
* @see \Drupal\Core\Theme\ThemeNegotiatorPass
* @see \Drupal\Core\Theme\ThemeNegotiatorInterface
*/
class ThemeNegotiator implements ThemeNegotiatorInterface {