Update to drupal 8.0.0-rc1. For more information, see https://www.drupal.org/node/2582663

This commit is contained in:
Greg Anderson 2015-10-08 11:40:12 -07:00
parent eb34d130a8
commit f32e58e4b1
8476 changed files with 211648 additions and 170042 deletions

View file

@ -7,7 +7,9 @@
namespace Drupal\Core\Template;
use Drupal\Component\Utility\SafeStringInterface;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\MarkupInterface;
/**
* Collects, sanitizes, and renders HTML attributes.
@ -39,12 +41,34 @@ use Drupal\Component\Utility\SafeStringInterface;
* {# Produces <cat class="cat black-cat white-cat black-white-cat my-custom-class" id="socks"> #}
* @endcode
*
* The attribute keys and values are automatically sanitized for output with
* Html::escape() and the entire attribute string is marked safe for output.
* The attribute keys and values are automatically escaped for output with
* Html::escape(). No protocol filtering is applied, so when using user-entered
* input as a value for an attribute that expects an URI (href, src, ...),
* UrlHelper::stripDangerousProtocols() should be used to ensure dangerous
* protocols (such as 'javascript:') are removed. For example:
* @code
* $path = 'javascript:alert("xss");';
* $path = UrlHelper::stripDangerousProtocols($path);
* $attributes = new Attribute(array('href' => $path));
* echo '<a' . $attributes . '>';
* // Produces <a href="alert(&quot;xss&quot;);">
* @endcode
*
* The attribute values are considered plain text and are treated as such. If a
* safe HTML string is detected, it is converted to plain text with
* PlainTextOutput::renderFromHtml() before being escaped. For example:
* @code
* $value = t('Highlight the @tag tag', ['@tag' => '<em>']);
* $attributes = new Attribute(['value' => $value]);
* echo '<input' . $attributes . '>';
* // Produces <input value="Highlight the &lt;em&gt; tag">
* @endcode
*
* @see \Drupal\Component\Utility\Html::escape()
* @see \Drupal\Component\Render\PlainTextOutput::renderFromHtml()
* @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
*/
class Attribute implements \ArrayAccess, \IteratorAggregate, SafeStringInterface {
class Attribute implements \ArrayAccess, \IteratorAggregate, MarkupInterface {
/**
* Stores the attribute data.
@ -100,15 +124,26 @@ class Attribute implements \ArrayAccess, \IteratorAggregate, SafeStringInterface
}
// An array value or 'class' attribute name are forced to always be an
// AttributeArray value for consistency.
if (is_array($value) || $name == 'class') {
if ($name == 'class' && !is_array($value)) {
// Cast the value to string in case it implements MarkupInterface.
$value = [(string) $value];
}
if (is_array($value)) {
// Cast the value to an array if the value was passed in as a string.
// @todo Decide to fix all the broken instances of class as a string
// in core or cast them.
$value = new AttributeArray($name, (array) $value);
$value = new AttributeArray($name, $value);
}
elseif (is_bool($value)) {
$value = new AttributeBoolean($name, $value);
}
// As a development aid, we allow the value to be a safe string object.
elseif (SafeMarkup::isSafe($value)) {
// Attributes are not supposed to display HTML markup, so we just convert
// the value to plain text.
$value = PlainTextOutput::renderFromHtml($value);
$value = new AttributeString($name, $value);
}
elseif (!is_object($value)) {
$value = new AttributeString($name, $value);
}

View file

@ -25,7 +25,7 @@ namespace Drupal\Core\Template\Loader;
* @see \Drupal\Core\Render\Element\InlineTemplate
* @see twig_render_template()
*/
class StringLoader extends \Twig_Loader_String {
class StringLoader implements \Twig_LoaderInterface, \Twig_ExistsLoaderInterface {
/**
* {@inheritdoc}
@ -39,4 +39,25 @@ class StringLoader extends \Twig_Loader_String {
}
}
/**
* {@inheritdoc}
*/
public function getSource($name) {
return $name;
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name) {
return $name;
}
/**
* {@inheritdoc}
*/
public function isFresh($name, $time) {
return true;
}
}

View file

@ -9,8 +9,7 @@ namespace Drupal\Core\Template;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Render\SafeString;
use Drupal\Core\Render\Markup;
/**
* A class that defines a Twig environment for Drupal.
@ -22,27 +21,6 @@ use Drupal\Core\Render\SafeString;
*/
class TwigEnvironment extends \Twig_Environment {
/**
* The cache object used for auto-refresh via mtime.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache_object = NULL;
/**
* The PhpStorage object used for storing the templates.
*
* @var \Drupal\Core\PhpStorage\PhpStorageFactory
*/
protected $storage = NULL;
/**
* The template cache filename prefix.
*
* @var string
*/
protected $templateCacheFilenamePrefix;
/**
* Static cache of template classes.
*
@ -66,9 +44,6 @@ class TwigEnvironment extends \Twig_Environment {
* The options for the Twig environment.
*/
public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, \Twig_LoaderInterface $loader = NULL, $options = array()) {
$this->cache_object = $cache;
$this->templateCacheFilenamePrefix = $twig_extension_hash;
// Ensure that twig.engine is loaded, given that it is needed to render a
// template because functions like TwigExtension::escapeFilter() are called.
require_once $root . '/core/themes/engines/twig/twig.engine';
@ -82,122 +57,16 @@ class TwigEnvironment extends \Twig_Environment {
'auto_reload' => NULL,
);
// Ensure autoescaping is always on.
$options['autoescape'] = TRUE;
$options['autoescape'] = 'html';
if ($options['cache'] === TRUE) {
$options['cache'] = new TwigPhpStorageCache($cache, $twig_extension_hash);
}
$this->loader = $loader;
parent::__construct($this->loader, $options);
}
/**
* Checks if the compiled template needs an update.
*/
protected function isFresh($cache_filename, $name) {
$cid = 'twig:' . $cache_filename;
$obj = $this->cache_object->get($cid);
$mtime = isset($obj->data) ? $obj->data : FALSE;
return $mtime === FALSE || $this->isTemplateFresh($name, $mtime);
}
/**
* Compile the source and write the compiled template to disk.
*/
public function updateCompiledTemplate($cache_filename, $name) {
$source = $this->loader->getSource($name);
$compiled_source = $this->compileSource($source, $name);
$this->storage()->save($cache_filename, $compiled_source);
// Save the last modification time
$cid = 'twig:' . $cache_filename;
$this->cache_object->set($cid, REQUEST_TIME);
}
/**
* {@inheritdoc}
*/
public function getCacheFilename($name) {
// We override the cache filename in order to avoid issues with not using
// shared filesystems. The Twig templates for example rely on available Twig
// extensions, so we use the twig extension hash which varies by extensions
// and their mtime.
// @see \Drupal\Core\DependencyInjection\Compiler\TwigExtensionPass
if (!$this->cache) {
return FALSE;
}
$class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix));
// The first part is what is invalidated.
return $this->templateCacheFilenamePrefix . '_' . basename($name) . '_' . $class;
}
/**
* Implements Twig_Environment::loadTemplate().
*
* We need to overwrite this function to integrate with drupal_php_storage().
*
* This is a straight copy from loadTemplate() changed to use
* drupal_php_storage().
*
* @param string $name
* The template name or the string which should be rendered as template.
* @param int $index
* The index if it is an embedded template.
*
* @return \Twig_TemplateInterface
* A template instance representing the given template name.
*
* @throws \Twig_Error_Loader
* When the template cannot be found.
* @throws \Twig_Error_Syntax
* When an error occurred during compilation.
*/
public function loadTemplate($name, $index = NULL) {
$cls = $this->getTemplateClass($name, $index);
if (isset($this->loadedTemplates[$cls])) {
return $this->loadedTemplates[$cls];
}
if (!class_exists($cls, FALSE)) {
$cache_filename = $this->getCacheFilename($name);
if ($cache_filename !== FALSE) {
// If autoreload is on, check that the template has not been
// modified since the last compilation.
if ($this->isAutoReload() && !$this->isFresh($cache_filename, $name)) {
$this->updateCompiledTemplate($cache_filename, $name);
}
if (!$this->storage()->load($cache_filename)) {
$this->updateCompiledTemplate($cache_filename, $name);
$this->storage()->load($cache_filename);
}
}
if (!class_exists($cls, FALSE)) {
$compiled_source = $this->compileSource($this->loader->getSource($name), $name);
eval('?' . '>' . $compiled_source);
}
}
if (!$this->runtimeInitialized) {
$this->initRuntime();
}
return $this->loadedTemplates[$cls] = new $cls($this);
}
/**
* Gets the PHP code storage object to use for the compiled Twig files.
*
* @return \Drupal\Component\PhpStorage\PhpStorageInterface
*/
protected function storage() {
if (!isset($this->storage)) {
$this->storage = PhpStorageFactory::get('twig');
}
return $this->storage;
}
/**
* Gets the template class associated with the given string.
*
@ -238,15 +107,15 @@ class TwigEnvironment extends \Twig_Environment {
* @param array $context
* An array of parameters to pass to the template.
*
* @return \Drupal\Component\Utility\SafeStringInterface|string
* The rendered inline template as a SafeString object.
* @return \Drupal\Component\Render\MarkupInterface|string
* The rendered inline template as a Markup object.
*
* @see \Drupal\Core\Template\Loader\StringLoader::exists()
*/
public function renderInline($template_string, array $context = array()) {
// Prefix all inline templates with a special comment.
$template_string = '{# inline_template_start #}' . $template_string;
return SafeString::create($this->loadTemplate($template_string, NULL)->render($context));
return Markup::create($this->loadTemplate($template_string, NULL)->render($context));
}
}

View file

@ -14,7 +14,9 @@ namespace Drupal\Core\Template;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\SafeStringInterface;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Render\RenderableInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
@ -50,6 +52,13 @@ class TwigExtension extends \Twig_Extension {
*/
protected $themeManager;
/**
* The date formatter.
*
* @var \Drupal\Core\Datetime\DateFormatter
*/
protected $dateFormatter;
/**
* Constructs \Drupal\Core\Template\TwigExtension.
*
@ -101,6 +110,19 @@ class TwigExtension extends \Twig_Extension {
return $this;
}
/**
* Sets the date formatter.
*
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* The date formatter.
*
* @return $this
*/
public function setDateFormatter(DateFormatter $date_formatter) {
$this->dateFormatter = $date_formatter;
return $this;
}
/**
* {@inheritdoc}
*/
@ -115,6 +137,7 @@ class TwigExtension extends \Twig_Extension {
new \Twig_SimpleFunction('link', array($this, 'getLink')),
new \Twig_SimpleFunction('file_url', 'file_create_url'),
new \Twig_SimpleFunction('attach_library', [$this, 'attachLibrary']),
new \Twig_SimpleFunction('active_theme_path', [$this, 'getActiveThemePath']),
new \Twig_SimpleFunction('active_theme', [$this, 'getActiveTheme']),
];
}
@ -132,7 +155,6 @@ class TwigExtension extends \Twig_Extension {
// "raw" filter and give it identifiable names. These filters should only
// be used in "trans" tags.
// @see TwigNodeTrans::compileString()
new \Twig_SimpleFilter('passthrough', 'twig_raw_filter', array('is_safe' => array('html'))),
new \Twig_SimpleFilter('placeholder', [$this, 'escapePlaceholder'], array('is_safe' => array('html'), 'needs_environment' => TRUE)),
// Replace twig's escape filter with our own.
@ -151,6 +173,7 @@ class TwigExtension extends \Twig_Extension {
new \Twig_SimpleFilter('clean_id', '\Drupal\Component\Utility\Html::getId'),
// This filter will render a renderable array to use the string results.
new \Twig_SimpleFilter('render', array($this, 'renderVar')),
new \Twig_SimpleFilter('format_date', array($this->dateFormatter, 'format')),
);
}
@ -272,6 +295,16 @@ class TwigExtension extends \Twig_Extension {
return $this->themeManager->getActiveTheme()->getName();
}
/**
* Gets the path of the active theme.
*
* @return string
* The path to the active theme.
*/
public function getActiveThemePath() {
return $this->themeManager->getActiveTheme()->getPath();
}
/**
* Determines at compile time whether the generated URL will be safe.
*
@ -349,6 +382,9 @@ class TwigExtension extends \Twig_Extension {
*
* Replacement function for Twig's escape filter.
*
* Note: This function should be kept in sync with
* theme_render_and_autoescape().
*
* @param \Twig_Environment $env
* A Twig_Environment instance.
* @param mixed $arg
@ -363,6 +399,9 @@ class TwigExtension extends \Twig_Extension {
*
* @return string|null
* The escaped, rendered output, or NULL if there is no valid output.
*
* @todo Refactor this to keep it in sync with theme_render_and_autoescape()
* in https://www.drupal.org/node/2575065
*/
public function escapeFilter(\Twig_Environment $env, $arg, $strategy = 'html', $charset = NULL, $autoescape = FALSE) {
// Check for a numeric zero int or float.
@ -376,7 +415,7 @@ class TwigExtension extends \Twig_Extension {
}
// Keep Twig_Markup objects intact to support autoescaping.
if ($autoescape && ($arg instanceOf \Twig_Markup || $arg instanceOf SafeStringInterface)) {
if ($autoescape && ($arg instanceOf \Twig_Markup || $arg instanceOf MarkupInterface)) {
return $arg;
}
@ -386,7 +425,10 @@ class TwigExtension extends \Twig_Extension {
$return = (string) $arg;
}
elseif (is_object($arg)) {
if (method_exists($arg, '__toString')) {
if ($arg instanceof RenderableInterface) {
$arg = $arg->toRenderable();
}
elseif (method_exists($arg, '__toString')) {
$return = (string) $arg;
}
// You can't throw exceptions in the magic PHP __toString methods, see
@ -461,7 +503,10 @@ class TwigExtension extends \Twig_Extension {
}
if (is_object($arg)) {
if (method_exists($arg, '__toString')) {
if ($arg instanceof RenderableInterface) {
$arg = $arg->toRenderable();
}
elseif (method_exists($arg, '__toString')) {
return (string) $arg;
}
// You can't throw exceptions in the magic PHP __toString methods, see

View file

@ -136,9 +136,6 @@ class TwigNodeTrans extends \Twig_Node {
$argPrefix = '@';
while ($args instanceof \Twig_Node_Expression_Filter) {
switch ($args->getNode('filter')->getAttribute('value')) {
case 'passthrough':
$argPrefix = '!';
break;
case 'placeholder':
$argPrefix = '%';
break;

View file

@ -0,0 +1,121 @@
<?php
/**
* @file
* Contains \Drupal\Core\Template\TwigPhpStorageCache.
*/
namespace Drupal\Core\Template;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
/**
* Provides an alternate cache storage for Twig using PhpStorage.
*
* This class is designed to work on setups with multiple webheads using a local
* filesystem for the twig cache. When generating the cache key, a hash value
* depending on the enabled extensions is included. This prevents stale
* templates from being reused when twig extensions are enabled or disabled.
*
* @see \Drupal\Core\DependencyInjection\Compiler\TwigExtensionPass
*/
class TwigPhpStorageCache implements \Twig_CacheInterface {
/**
* The cache object used for auto-refresh via mtime.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The PhpStorage object used for storing the templates.
*
* @var \Drupal\Component\PhpStorage\PhpStorageInterface
*/
protected $storage;
/**
* The template cache filename prefix.
*
* @var string
*/
protected $templateCacheFilenamePrefix;
/**
* Store cache backend and other information internally.
*
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache bin.
* @param string $twig_extension_hash
* The Twig extension hash.
*/
public function __construct(CacheBackendInterface $cache, $twig_extension_hash) {
$this->cache = $cache;
$this->templateCacheFilenamePrefix = $twig_extension_hash;
}
/**
* Gets the PHP code storage object to use for the compiled Twig files.
*
* @return \Drupal\Component\PhpStorage\PhpStorageInterface
*/
protected function storage() {
if (!isset($this->storage)) {
$this->storage = PhpStorageFactory::get('twig');
}
return $this->storage;
}
/**
* {@inheritdoc}
*/
public function generateKey($name, $className) {
$hash = hash('sha256', $className);
if (strpos($name, '{# inline_template_start #}') === 0) {
// $name is an inline template, and can have characters that are not valid
// for a filename. $hash is unique for each inline template so we just use
// the generic name 'inline-template' here.
$name = 'inline-template';
}
else {
$name = basename($name);
}
// The first part is what is invalidated.
return $this->templateCacheFilenamePrefix . '_' . $name . '_' . $hash;
}
/**
* {@inheritdoc}
*/
public function load($key) {
$this->storage()->load($key);
}
/**
* {@inheritdoc}
*/
public function write($key, $content) {
$this->storage()->save($key, $content);
// Save the last mtime.
$cid = 'twig:' . $key;
$this->cache->set($cid, REQUEST_TIME);
}
/**
* {@inheritdoc}
*/
public function getTimestamp($key) {
$cid = 'twig:' . $key;
if ($cache = $this->cache->get($cid)) {
return $cache->data;
}
else {
return 0;
}
}
}