Core and composer updates

This commit is contained in:
Rob Davies 2017-07-03 16:47:07 +01:00
parent a82634bb98
commit 62cac30480
1118 changed files with 21770 additions and 6306 deletions

View file

@ -3,7 +3,9 @@
namespace Drupal\Component\Plugin\Context;
/**
* Interface for context definitions.
* Interface used to define definition objects found in ContextInterface.
*
* @see \Drupal\Component\Plugin\Context\ContextInterface
*
* @todo WARNING: This interface is going to receive some additions as part of
* https://www.drupal.org/node/2346999.

View file

@ -3,7 +3,14 @@
namespace Drupal\Component\Plugin\Context;
/**
* A generic context interface for wrapping data a plugin needs to operate.
* Provides data and definitions for plugins during runtime and administration.
*
* Plugin contexts are satisfied by ContextInterface implementing objects.
* These objects always contain a definition of what data they will provide
* during runtime. During run time, ContextInterface implementing objects must
* also provide the corresponding data value.
*
* @see \Drupal\Component\Plugin\Context\ContextDefinitionInterface
*/
interface ContextInterface {

View file

@ -27,6 +27,12 @@ class YamlPecl implements SerializationInterface {
* {@inheritdoc}
*/
public static function decode($raw) {
static $init;
if (!isset($init)) {
// We never want to unserialize !php/object.
ini_set('yaml.decode_php', 0);
$init = TRUE;
}
// yaml_parse() will error with an empty value.
if (!trim($raw)) {
return NULL;

View file

@ -61,13 +61,15 @@ class Html {
* Do not pass one string containing multiple classes as they will be
* incorrectly concatenated with dashes, i.e. "one two" will become "one-two".
*
* @param string $class
* The class name to clean.
* @param mixed $class
* The class name to clean. It can be a string or anything that can be cast
* to string.
*
* @return string
* The cleaned class name.
*/
public static function getClass($class) {
$class = (string) $class;
if (!isset(static::$classes[$class])) {
static::$classes[$class] = static::cleanCssIdentifier(Unicode::strtolower($class));
}

View file

@ -46,7 +46,7 @@ class UrlHelper {
$params = [];
foreach ($query as $key => $value) {
$key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key));
$key = ($parent ? $parent . rawurlencode('[' . $key . ']') : rawurlencode($key));
// Recurse into children.
if (is_array($value)) {
@ -142,7 +142,12 @@ class UrlHelper {
// External URLs: not using parse_url() here, so we do not have to rebuild
// the scheme, host, and path without having any use for it.
if (strpos($url, '://') !== FALSE) {
// The URL is considered external if it contains the '://' delimiter. Since
// a URL can also be passed as a query argument, we check if this delimiter
// appears in front of the '?' query argument delimiter.
$scheme_delimiter_position = strpos($url, '://');
$query_delimiter_position = strpos($url, '?');
if ($scheme_delimiter_position !== FALSE && ($query_delimiter_position === FALSE || $scheme_delimiter_position < $query_delimiter_position)) {
// Split off everything before the query string into 'path'.
$parts = explode('?', $url);

View file

@ -18,8 +18,6 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
* "node" = @ContextDefinition("entity:node")
* }
* @endcode
* Remove spaces after @ in your actual plugin - these are put into this sample
* code so that it is not recognized as an annotation.
*
* To add a label to a context definition use the "label" key:
* @code

View file

@ -6,6 +6,8 @@ use Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
* Defines the chained fast cache backend factory.
*
* @see \Drupal\Core\Cache\ChainedFastBackend
*/
class ChainedFastBackendFactory implements CacheFactoryInterface {

View file

@ -6,7 +6,6 @@ use Drupal\Component\PhpStorage\FileStorage;
use Composer\Script\Event;
use Composer\Installer\PackageEvent;
use Composer\Semver\Constraint\Constraint;
use PHP_CodeSniffer;
/**
* Provides static functions for composer script events.
@ -137,28 +136,6 @@ EOT;
}
}
/**
* Configures phpcs if present.
*
* @param \Composer\Script\Event $event
*/
public static function configurePhpcs(Event $event) {
// Grab the local repo which tells us what's been installed.
$local_repository = $event->getComposer()
->getRepositoryManager()
->getLocalRepository();
// Make sure both phpcs and coder are installed.
$phpcs_package = $local_repository->findPackage('squizlabs/php_codesniffer', '*');
$coder_package = $local_repository->findPackage('drupal/coder', '*');
if (!empty($phpcs_package) && !empty($coder_package)) {
$config = $event->getComposer()->getConfig();
$vendor_dir = $config->get('vendor-dir');
// Set phpcs' installed_paths config to point to our coder_sniffer
// directory.
PHP_CodeSniffer::setConfigData('installed_paths', $vendor_dir . '/drupal/coder/coder_sniffer');
}
}
/**
* Remove possibly problematic test files from vendored projects.
*

View file

@ -466,7 +466,7 @@ class ConfigInstaller implements ConfigInstallerInterface {
// Check the dependencies of configuration provided by the module.
list($invalid_default_config, $missing_dependencies) = $this->findDefaultConfigWithUnmetDependencies($storage, $enabled_extensions, $profile_storages);
if (!empty($invalid_default_config)) {
throw UnmetDependenciesException::create($name, array_unique($missing_dependencies));
throw UnmetDependenciesException::create($name, array_unique($missing_dependencies, SORT_REGULAR));
}
// Install profiles can not have config clashes. Configuration that

View file

@ -19,8 +19,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* difficult to unit test. Therefore this base class should only be used by
* controller classes that contain only trivial glue code. Controllers that
* contain sufficiently complex logic that it's worth testing should not use
* this base class but use ContainerInjectionInterface instead, or even better be
* refactored to be trivial glue code.
* this base class but use ContainerInjectionInterface instead, or even
* better be refactored to be trivial glue code.
*
* The services exposed here are those that it is reasonable for a well-behaved
* controller to leverage. A controller that needs other services may
@ -70,7 +70,7 @@ abstract class ControllerBase implements ContainerInjectionInterface {
/**
* The configuration factory.
*
* @var \Drupal\Core\Config\Config
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;

View file

@ -10,9 +10,9 @@ use Symfony\Component\DependencyInjection\Reference;
* Provides a compiler pass for stacked HTTP kernels.
*
* Builds the HTTP kernel by collecting all services tagged 'http_middleware'
* and assembling them into a StackedKernel. The middleware with the lowest
* priority ends up as the outermost while the highest priority middleware
* wraps the actual HTTP kernel defined by the http_kernel.basic service.
* and assembling them into a StackedKernel. The middleware with the highest
* priority ends up as the outermost while the lowest priority middleware wraps
* the actual HTTP kernel defined by the http_kernel.basic service.
*
* The 'http_middleware' service tag additionally accepts a 'responder'
* parameter. It should be set to TRUE if many or most requests will be handled

View file

@ -870,6 +870,16 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
// If there is no container and no cached container definition, build a new
// one from scratch.
if (!isset($container) && !isset($container_definition)) {
// Building the container creates 1000s of objects. Garbage collection of
// these objects is expensive. This appears to be causing random
// segmentation faults in PHP 5.6 due to
// https://bugs.php.net/bug.php?id=72286. Once the container is rebuilt,
// garbage collection is re-enabled.
$disable_gc = version_compare(PHP_VERSION, '7', '<') && gc_enabled();
if ($disable_gc) {
gc_collect_cycles();
gc_disable();
}
$container = $this->compileContainer();
// Only dump the container if dumping is allowed. This is useful for
@ -879,6 +889,11 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
$dumper = new $this->phpArrayDumperClass($container);
$container_definition = $dumper->getArray();
}
// If garbage collection was disabled prior to rebuilding container,
// re-enable it.
if ($disable_gc) {
gc_enable();
}
}
// The container was rebuilt successfully.

View file

@ -830,6 +830,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$translation->translationInitialize = FALSE;
$translation->typedData = NULL;
$translation->loadedRevisionId = &$this->loadedRevisionId;
$translation->isDefaultRevision = &$this->isDefaultRevision;
return $translation;
}
@ -1095,7 +1096,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// Ensure that the following properties are actually cloned by
// overwriting the original references with ones pointing to copies of
// them: enforceIsNew, newRevision, loadedRevisionId, fields, entityKeys,
// translatableEntityKeys and values.
// translatableEntityKeys, values and isDefaultRevision.
$enforce_is_new = $this->enforceIsNew;
$this->enforceIsNew = &$enforce_is_new;
@ -1117,6 +1118,9 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$values = $this->values;
$this->values = &$values;
$default_revision = $this->isDefaultRevision;
$this->isDefaultRevision = &$default_revision;
foreach ($this->fields as $name => $fields_by_langcode) {
$this->fields[$name] = [];
// Untranslatable fields may have multiple references for the same field

View file

@ -318,8 +318,14 @@ class EntityAutocomplete extends Textfield {
* A string of entity labels separated by commas.
*/
public static function getEntityLabels(array $entities) {
/** @var \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository */
$entity_repository = \Drupal::service('entity.repository');
$entity_labels = [];
foreach ($entities as $entity) {
// Set the entity in the correct language for display.
$entity = $entity_repository->getTranslationFromContext($entity);
// Use the special view label, since some entities allow the label to be
// viewed, even if the entity is not allowed to be viewed.
$label = ($entity->access('view label')) ? $entity->label() : t('- Restricted access -');

View file

@ -110,6 +110,7 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
* Use \Drupal\Core\Entity\EntityInterface::toUrl() instead.
*
* @see https://www.drupal.org/node/2614344
* @see \Drupal\Core\Entity\EntityInterface::toUrl
*/
public function urlInfo($rel = 'canonical', array $options = []);
@ -167,6 +168,7 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
* Please use toUrl() instead.
*
* @see https://www.drupal.org/node/2614344
* @see \Drupal\Core\Entity\EntityInterface::toUrl
*/
public function url($rel = 'canonical', $options = []);
@ -189,6 +191,7 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
* Please use toLink() instead.
*
* @see https://www.drupal.org/node/2614344
* @see \Drupal\Core\Entity\EntityInterface::toLink
*/
public function link($text = NULL, $rel = 'canonical', array $options = []);

View file

@ -22,7 +22,9 @@ interface EntityListBuilderInterface {
* sorting the loaded entities.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* An array of entities implementing \Drupal\Core\Entity\EntityInterface.
* An array of entities implementing \Drupal\Core\Entity\EntityInterface
* indexed by their IDs. Returns an empty array if no matching entities are
* found.
*/
public function load();

View file

@ -603,6 +603,13 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
$this->database->delete($this->revisionTable)
->condition($this->revisionKey, $revision->getRevisionId())
->execute();
if ($this->revisionDataTable) {
$this->database->delete($this->revisionDataTable)
->condition($this->revisionKey, $revision->getRevisionId())
->execute();
}
$this->deleteRevisionFromDedicatedTables($revision);
}

View file

@ -0,0 +1,23 @@
<?php
namespace Drupal\Core\Field;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Provides en entity access control handler for base field override entity.
*/
class BaseFieldOverrideAccessControlHandler extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
$access = parent::checkAccess($entity, $operation, $account);
return $access->orIf(AccessResult::allowedIfHasPermission($account, 'administer ' . $entity->getTargetEntityTypeId() . ' fields'));
}
}

View file

@ -16,7 +16,8 @@ use Drupal\Core\Field\FieldException;
* id = "base_field_override",
* label = @Translation("Base field override"),
* handlers = {
* "storage" = "Drupal\Core\Field\BaseFieldOverrideStorage"
* "storage" = "Drupal\Core\Field\BaseFieldOverrideStorage",
* "access" = "Drupal\Core\Field\BaseFieldOverrideAccessControlHandler",
* },
* config_prefix = "base_field_override",
* entity_keys = {

View file

@ -104,7 +104,8 @@ abstract class EntityReferenceFormatterBase extends FormatterBase {
// tags on which the access results depend, to ensure users that cannot view
// this field at the moment will gain access once any of those cache tags
// are invalidated.
$field_level_access_cacheability->applyTo($elements);
$field_level_access_cacheability->merge(CacheableMetadata::createFromRenderArray($elements))
->applyTo($elements);
return $elements;
}

View file

@ -0,0 +1,54 @@
<?php
namespace Drupal\Core\Field\Plugin\migrate\field;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
/**
* @MigrateField(
* id = "email",
* core = {6,7},
* type_map = {
* "email" = "email"
* }
* )
*/
class Email extends FieldPluginBase {
/**
* {@inheritdoc}
*/
public function getFieldWidgetMap() {
return [
'email_textfield' => 'email_default',
];
}
/**
* {@inheritdoc}
*/
public function getFieldFormatterMap() {
return [
'email_formatter_default' => 'basic_string',
'email_formatter_contact' => 'basic_string',
'email_formatter_plain' => 'basic_string',
'email_formatter_spamspan' => 'basic_string',
];
}
/**
* {@inheritdoc}
*/
public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
$process = [
'plugin' => 'iterator',
'source' => $field_name,
'process' => [
'value' => 'email',
],
];
$migration->setProcessOfProperty($field_name, $process);
}
}

View file

@ -5,7 +5,7 @@ namespace Drupal\Core\Lock;
/**
* @defgroup lock Locking mechanisms
* @{
* Functions to coordinate long-running operations across requests.
* Functions to coordinate long operations across requests.
*
* In most environments, multiple Drupal page requests (a.k.a. threads or
* processes) will execute in parallel. This leads to potential conflicts or
@ -15,15 +15,14 @@ namespace Drupal\Core\Lock;
* problems with such code, the cron system uses a locking process to ensure
* that cron is not started again if it is already running.
*
* This is a cooperative, advisory lock system. Any long-running operation
* that could potentially be attempted in parallel by multiple requests should
* try to acquire a lock before proceeding. By obtaining a lock, one request
* notifies any other requests that a specific operation is in progress which
* must not be executed in parallel.
* To avoid these types of conflicts, Drupal has a cooperative, advisory lock
* system. Any long-running operation that could potentially be attempted in
* parallel by multiple requests should try to acquire a lock before
* proceeding. By obtaining a lock, one request notifies any other requests that
* a specific operation is in progress which must not be executed in parallel.
*
* To use this API, pick a unique name for the lock. A sensible choice is the
* name of the function performing the operation. A very simple example use of
* this API:
* name of the function performing the operation. Here is a simple example:
* @code
* function mymodule_long_operation() {
* $lock = \Drupal::lock();
@ -53,6 +52,26 @@ namespace Drupal\Core\Lock;
* $lock->acquire() and $lock->wait() will automatically break (delete) a lock
* whose duration has exceeded the timeout specified when it was acquired.
*
* The following limitations in this implementation should be carefully noted:
* - Time: Timestamps are derived from the local system clock of the environment
* the code is executing in. The orderly progression of time from this
* viewpoint can be disrupted by external events such as NTP synchronization
* and operator intervention. Where multiple web servers are involved in
* serving the site, they will have their own independent clocks, introducing
* another source of error in the time keeping process. Timeout values applied
* to locks must therefore be considered approximate, and should not be relied
* upon.
* - Uniqueness: Uniqueness of lock names is not enforced. The impact of the
* use of a common lock name will depend on what processes and resources the
* lock is being used to manage.
* - Sharing: There is limited support for resources shared across sites.
* The locks are stored as rows in the semaphore table and, as such, they
* have the same visibility as the table. If resources managed by a lock are
* shared across sites then the semaphore table must be shared across sites
* as well. This is a binary situation: either all resources are shared and
* the semaphore table is shared or no resources are shared and the semaphore
* table is not shared. Mixed mode operation is not supported.
*
* @} End of "defgroup lock".
*/

View file

@ -53,7 +53,7 @@ interface LocalTaskManagerInterface extends PluginManagerInterface {
public function getTasksBuild($current_route_name, RefinableCacheableDependencyInterface &$cacheability);
/**
* Collects the local tasks (tabs) for the current route.
* Renders the local tasks (tabs) for the given route.
*
* @param string $route_name
* The route for which to make renderable local tasks.

View file

@ -21,6 +21,13 @@ class MenuLinkTree implements MenuLinkTreeInterface {
*/
protected $treeStorage;
/**
* The menu link plugin manager.
*
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
*/
protected $menuLinkManager;
/**
* The route provider to load routes by name.
*

View file

@ -282,27 +282,44 @@ class MenuTreeStorage implements MenuTreeStorageInterface {
* depth.
*/
protected function doSave(array $link) {
$original = $this->loadFull($link['id']);
// @todo Should we just return here if the link values match the original
// values completely?
// https://www.drupal.org/node/2302137
$affected_menus = [];
// Get the existing definition if it exists. This does not use
// self::loadFull() to avoid the unserialization of fields with 'serialize'
// equal to TRUE as defined in self::schemaDefinition(). The makes $original
// easier to compare with the return value of self::preSave().
$query = $this->connection->select($this->table, $this->options);
$query->fields($this->table);
$query->condition('id', $link['id']);
$original = $this->safeExecuteSelect($query)->fetchAssoc();
if ($original) {
$link['mlid'] = $original['mlid'];
$link['has_children'] = $original['has_children'];
$affected_menus[$original['menu_name']] = $original['menu_name'];
$fields = $this->preSave($link, $original);
// If $link matches the $original data then exit early as there are no
// changes to make. Use array_diff_assoc() to check if they match because:
// - Some of the data types of the values are not the same. The values
// in $original are all strings because they have come from database but
// $fields contains typed values.
// - MenuTreeStorage::preSave() removes the 'mlid' from $fields.
// - The order of the keys in $original and $fields is different.
if (array_diff_assoc($fields, $original) == [] && array_diff_assoc($original, $fields) == ['mlid' => $link['mlid']]) {
return $affected_menus;
}
}
$transaction = $this->connection->startTransaction();
try {
if ($original) {
$link['mlid'] = $original['mlid'];
$link['has_children'] = $original['has_children'];
$affected_menus[$original['menu_name']] = $original['menu_name'];
}
else {
if (!$original) {
// Generate a new mlid.
$options = ['return' => Database::RETURN_INSERT_ID] + $this->options;
$link['mlid'] = $this->connection->insert($this->table, $options)
->fields(['id' => $link['id'], 'menu_name' => $link['menu_name']])
->execute();
$fields = $this->preSave($link, []);
}
$fields = $this->preSave($link, $original);
// We may be moving the link to a new menu.
$affected_menus[$fields['menu_name']] = $fields['menu_name'];
$query = $this->connection->update($this->table, $this->options);

View file

@ -5,7 +5,10 @@ namespace Drupal\Core\Plugin\Context;
use Drupal\Component\Plugin\Context\ContextDefinitionInterface as ComponentContextDefinitionInterface;
/**
* Interface for context definitions.
* Interface to define definition objects in ContextInterface via TypedData.
*
* @see \Drupal\Component\Plugin\Context\ContextDefinitionInterface
* @see \Drupal\Core\Plugin\Context\ContextInterface
*/
interface ContextDefinitionInterface extends ComponentContextDefinitionInterface {

View file

@ -6,7 +6,10 @@ use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterfac
use Drupal\Core\Cache\CacheableDependencyInterface;
/**
* Interface for context.
* Context data and definitions for plugins supporting caching and return docs.
*
* @see \Drupal\Component\Plugin\Context\ContextInterface
* @see \Drupal\Core\Plugin\Context\ContextDefinitionInterface
*/
interface ContextInterface extends ComponentContextInterface, CacheableDependencyInterface {

View file

@ -361,7 +361,7 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return CACHE::PERMANENT;
return Cache::PERMANENT;
}
}

View file

@ -15,7 +15,7 @@ namespace Drupal\Core\Plugin;
interface ObjectWithPluginCollectionInterface {
/**
* Gets the plugin collections used by this entity.
* Gets the plugin collections used by this object.
*
* @return \Drupal\Component\Plugin\LazyPluginCollection[]
* An array of plugin collections, keyed by the property name they use to

View file

@ -73,22 +73,17 @@ class StatusMessages extends RenderElement {
* @see drupal_get_messages()
*/
public static function renderMessages($type) {
$render = [];
$messages = drupal_get_messages($type);
if ($messages) {
// Render the messages.
$render = [
'#theme' => 'status_messages',
// @todo Improve when https://www.drupal.org/node/2278383 lands.
'#message_list' => $messages,
'#status_headings' => [
'status' => t('Status message'),
'error' => t('Error message'),
'warning' => t('Warning message'),
],
];
}
return $render;
// Render the messages.
return [
'#theme' => 'status_messages',
// @todo Improve when https://www.drupal.org/node/2278383 lands.
'#message_list' => drupal_get_messages($type),
'#status_headings' => [
'status' => t('Status message'),
'error' => t('Error message'),
'warning' => t('Warning message'),
],
];
}
}

View file

@ -225,12 +225,7 @@
* For further information on the Theme and Render APIs, see:
* - https://www.drupal.org/docs/8/theming
* - https://www.drupal.org/developing/api/8/render
* - https://www.drupal.org/node/722174
* - https://www.drupal.org/node/933976
* - https://www.drupal.org/node/930760
*
* @todo Check these links. Some are for Drupal 7, and might need updates for
* Drupal 8.
* - @link themeable Theme system overview @endlink.
*
* @section arrays Render arrays
* The core structure of the Render API is the render array, which is a

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\Core\Test;
/**
* Object to test that security issues around serialization.
*/
class ObjectSerialization {
/**
* ObjectSerialization constructor.
*/
public function __construct() {
throw new \Exception('This object should never be constructed');
}
/**
* ObjectSerialization deconstructor.
*/
public function __destruct() {
throw new \Exception('This object should never be destructed');
}
}