Update to Drupal 8.0.5. For more information, see https://www.drupal.org/node/2679347

This commit is contained in:
Pantheon Automation 2016-03-02 12:40:24 -08:00 committed by Greg Anderson
parent 2a9f1f148d
commit fd3b12cf27
251 changed files with 5439 additions and 957 deletions

View file

@ -43,7 +43,7 @@ class InsertCommand implements CommandInterface, CommandWithAttachedAssetsInterf
protected $content;
/**
* A settings array to be passed to any any attached JavaScript behavior.
* A settings array to be passed to any attached JavaScript behavior.
*
* @var array
*/

View file

@ -11,8 +11,8 @@ namespace Drupal\Core\Ajax;
* AJAX command for calling the jQuery replace() method.
*
* The 'insert/replaceWith' command instructs the client to use jQuery's
* replaceWith() method to replace each element matched matched by the given
* selector with the given HTML.
* replaceWith() method to replace each element matched by the given selector
* with the given HTML.
*
* This command is implemented by Drupal.AjaxCommands.prototype.insert()
* defined in misc/ajax.js.

View file

@ -14,7 +14,7 @@ use Symfony\Component\HttpFoundation\Request;
*
* Some authentication methods should not be available throughout a whole site.
* For instance, there are good reasons to restrict insecure methods like HTTP
* basic authentication or an URL token authentication method to API-only
* basic authentication or a URL token authentication method to API-only
* routes.
*/
interface AuthenticationProviderFilterInterface {

View file

@ -375,8 +375,12 @@ class DbDumpCommand extends DbCommandBase {
* The template for the generated PHP script.
*/
protected function getTemplate() {
// The template contains an instruction for the file to be ignored by PHPCS.
// This is because the files can be huge and coding standards are
// irrelevant.
$script = <<<'ENDOFSCRIPT'
<?php
// @codingStandardsIgnoreFile
/**
* @file
* A database agnostic dump for testing purposes.

View file

@ -302,7 +302,7 @@ class ConfigManager implements ConfigManagerInterface {
$dependency_manager = $this->getConfigDependencyManager();
$dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager);
$original_dependencies = $dependents;
$update_uuids = [];
$delete_uuids = $update_uuids = [];
$return = [
'update' => [],
@ -311,26 +311,35 @@ class ConfigManager implements ConfigManagerInterface {
];
// Try to fix any dependencies and find out what will happen to the
// dependency graph.
foreach ($dependents as $dependent) {
// dependency graph. Entities are processed in the order of most dependent
// first. For example, this ensures that fields are removed before
// field storages.
while ($dependent = array_pop($dependents)) {
/** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent */
if ($dry_run) {
// Clone the entity so any changes do not change any static caches.
$dependent = clone $dependent;
}
$fixed = FALSE;
if ($this->callOnDependencyRemoval($dependent, $original_dependencies, $type, $names)) {
// Recalculate dependencies and update the dependency graph data.
$dependent->calculateDependencies();
$dependency_manager->updateData($dependent->getConfigDependencyName(), $dependent->getDependencies());
// Based on the updated data rebuild the list of dependents.
// Based on the updated data rebuild the list of dependents. This will
// remove entities that are no longer dependent after the recalculation.
$dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager);
// Remove any entities that we've already marked for deletion.
$dependents = array_filter($dependents, function ($dependent) use ($delete_uuids) {
return !in_array($dependent->uuid(), $delete_uuids);
});
// Ensure that the dependency has actually been fixed. It is possible
// that the dependent has multiple dependencies that cause it to be in
// the dependency chain.
$fixed = TRUE;
foreach ($dependents as $entity) {
foreach ($dependents as $key => $entity) {
if ($entity->uuid() == $dependent->uuid()) {
$fixed = FALSE;
unset($dependents[$key]);
break;
}
}
@ -339,15 +348,12 @@ class ConfigManager implements ConfigManagerInterface {
$update_uuids[] = $dependent->uuid();
}
}
// If the entity cannot be fixed then it has to be deleted.
if (!$fixed) {
$delete_uuids[] = $dependent->uuid();
$return['delete'][] = $dependent;
}
}
// Now that we've fixed all the possible dependencies the remaining need to
// be deleted. Reverse the deletes so that entities are removed in the
// correct order of dependence. For example, this ensures that fields are
// removed before field storages.
$return['delete'] = array_reverse($dependents);
$delete_uuids = array_map(function($dependent) {
return $dependent->uuid();
}, $return['delete']);
// Use the lists of UUIDs to filter the original list to work out which
// configuration entities are unchanged.
$return['unchanged'] = array_filter($original_dependencies, function ($dependent) use ($delete_uuids, $update_uuids) {

View file

@ -352,6 +352,32 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
}
}
/**
* {@inheritdoc}
*/
public function __sleep() {
$keys_to_unset = [];
if ($this instanceof EntityWithPluginCollectionInterface) {
$vars = get_object_vars($this);
foreach ($this->getPluginCollections() as $plugin_config_key => $plugin_collection) {
// Save any changes to the plugin configuration to the entity.
$this->set($plugin_config_key, $plugin_collection->getConfiguration());
// If the plugin collections are stored as properties on the entity,
// mark them to be unset.
$keys_to_unset += array_filter($vars, function ($value) use ($plugin_collection) {
return $plugin_collection === $value;
});
}
}
$vars = parent::__sleep();
if (!empty($keys_to_unset)) {
$vars = array_diff($vars, array_keys($keys_to_unset));
}
return $vars;
}
/**
* {@inheritdoc}
*/

View file

@ -207,8 +207,9 @@ class FileStorage implements StorageInterface {
$files = scandir($dir);
$names = array();
$pattern = '/^' . preg_quote($prefix, '/') . '.*' . preg_quote($extension, '/') . '$/';
foreach ($files as $file) {
if ($file[0] !== '.' && fnmatch($prefix . '*' . $extension, $file)) {
if ($file[0] !== '.' && preg_match($pattern, $file)) {
$names[] = basename($file, $extension);
}
}
@ -290,6 +291,7 @@ class FileStorage implements StorageInterface {
*/
protected function getAllCollectionNamesHelper($directory) {
$collections = array();
$pattern = '/\.' . preg_quote($this->getFileExtension(), '/') . '$/';
foreach (new \DirectoryIterator($directory) as $fileinfo) {
if ($fileinfo->isDir() && !$fileinfo->isDot()) {
$collection = $fileinfo->getFilename();
@ -309,7 +311,7 @@ class FileStorage implements StorageInterface {
// collection.
// @see \Drupal\Core\Config\FileStorage::listAll()
foreach (scandir($directory . '/' . $collection) as $file) {
if ($file[0] !== '.' && fnmatch('*.' . $this->getFileExtension(), $file)) {
if ($file[0] !== '.' && preg_match($pattern, $file)) {
$collections[] = $collection;
break;
}

View file

@ -190,6 +190,7 @@ class InstallStorage extends FileStorage {
*/
public function getComponentNames(array $list) {
$extension = '.' . $this->getFileExtension();
$pattern = '/' . preg_quote($extension, '/') . '$/';
$folders = array();
foreach ($list as $extension_object) {
// We don't have to use ExtensionDiscovery here because our list of
@ -203,7 +204,7 @@ class InstallStorage extends FileStorage {
$files = scandir($directory);
foreach ($files as $file) {
if ($file[0] !== '.' && fnmatch('*' . $extension, $file)) {
if ($file[0] !== '.' && preg_match($pattern, $file)) {
$folders[basename($file, $extension)] = $directory;
}
}
@ -220,6 +221,7 @@ class InstallStorage extends FileStorage {
*/
public function getCoreNames() {
$extension = '.' . $this->getFileExtension();
$pattern = '/' . preg_quote($extension, '/') . '$/';
$folders = array();
$directory = $this->getCoreFolder();
if (is_dir($directory)) {
@ -230,7 +232,7 @@ class InstallStorage extends FileStorage {
$files = scandir($directory);
foreach ($files as $file) {
if ($file[0] !== '.' && fnmatch('*' . $extension, $file)) {
if ($file[0] !== '.' && preg_match($pattern, $file)) {
$folders[basename($file, $extension)] = $directory;
}
}

View file

@ -138,7 +138,38 @@ class Connection extends DatabaseConnection {
}
}
return parent::query($query, $args, $options);
// We need to wrap queries with a savepoint if:
// - Currently in a transaction.
// - A 'mimic_implicit_commit' does not exist already.
// - The query is not a savepoint query.
$wrap_with_savepoint = $this->inTransaction() &&
!isset($this->transactionLayers['mimic_implicit_commit']) &&
!(is_string($query) && (
stripos($query, 'ROLLBACK TO SAVEPOINT ') === 0 ||
stripos($query, 'RELEASE SAVEPOINT ') === 0 ||
stripos($query, 'SAVEPOINT ') === 0
)
);
if ($wrap_with_savepoint) {
// Create a savepoint so we can rollback a failed query. This is so we can
// mimic MySQL and SQLite transactions which don't fail if a single query
// fails. This is important for tables that are created on demand. For
// example, \Drupal\Core\Cache\DatabaseBackend.
$this->addSavepoint();
try {
$return = parent::query($query, $args, $options);
$this->releaseSavepoint();
}
catch (\Exception $e) {
$this->rollbackSavepoint();
throw $e;
}
}
else {
$return = parent::query($query, $args, $options);
}
return $return;
}
public function prepareQuery($query) {

View file

@ -99,12 +99,25 @@ class Insert extends QueryInsert {
elseif ($options['return'] == Database::RETURN_INSERT_ID) {
$options['return'] = Database::RETURN_NULL;
}
// Only use the returned last_insert_id if it is not already set.
if (!empty($last_insert_id)) {
$this->connection->query($stmt, array(), $options);
// Create a savepoint so we can rollback a failed query. This is so we can
// mimic MySQL and SQLite transactions which don't fail if a single query
// fails. This is important for tables that are created on demand. For
// example, \Drupal\Core\Cache\DatabaseBackend.
$this->connection->addSavepoint();
try {
// Only use the returned last_insert_id if it is not already set.
if (!empty($last_insert_id)) {
$this->connection->query($stmt, array(), $options);
}
else {
$last_insert_id = $this->connection->query($stmt, array(), $options);
}
$this->connection->releaseSavepoint();
}
else {
$last_insert_id = $this->connection->query($stmt, array(), $options);
catch (\Exception $e) {
$this->connection->rollbackSavepoint();
throw $e;
}
// Re-initialize the values array so that we can re-use this query.

View file

@ -76,11 +76,23 @@ class NativeUpsert extends QueryUpsert {
$options['sequence_name'] = $table_information->sequences[0];
}
$this->connection->query($stmt, [], $options);
// Re-initialize the values array so that we can re-use this query.
$this->insertValues = [];
// Create a savepoint so we can rollback a failed query. This is so we can
// mimic MySQL and SQLite transactions which don't fail if a single query
// fails. This is important for tables that are created on demand. For
// example, \Drupal\Core\Cache\DatabaseBackend.
$this->connection->addSavepoint();
try {
$this->connection->query($stmt, [], $options);
$this->connection->releaseSavepoint();
}
catch (\Exception $e) {
$this->connection->rollbackSavepoint();
throw $e;
}
return TRUE;
}

View file

@ -765,7 +765,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
* The cache key used for the service container.
*/
protected function getContainerCacheKey() {
$parts = array('service_container', $this->environment, \Drupal::VERSION, Settings::get('deployment_identifier'));
$parts = array('service_container', $this->environment, \Drupal::VERSION, Settings::get('deployment_identifier'), serialize(Settings::get('container_yamls')));
return implode(':', $parts);
}
@ -866,7 +866,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
// If needs dumping flag was set, dump the container.
if ($this->containerNeedsDumping && !$this->cacheDrupalContainer($container_definition)) {
$this->container->get('logger.factory')->get('DrupalKernel')->notice('Container cannot be saved to cache.');
$this->container->get('logger.factory')->get('DrupalKernel')->error('Container cannot be saved to cache.');
}
return $this->container;

View file

@ -139,7 +139,7 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
$values[$this->langcodeKey] = $langcode;
$values[$this->getEntityType()->getKey('default_langcode')] = FALSE;
$this->initFieldValues($translation, $values, $field_names);
$this->invokeHook('translation_create', $entity);
$this->invokeHook('translation_create', $translation);
return $translation;
}

View file

@ -309,6 +309,9 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
* The entity storage object.
*
* @see \Drupal\Core\Field\FieldItemListInterface::preSave()
*
* @throws \Exception
* When there is a problem that should prevent saving the entity.
*/
public function preSave(EntityStorageInterface $storage);

View file

@ -177,25 +177,20 @@ class EntityTypeManager extends DefaultPluginManager implements EntityTypeManage
* {@inheritdoc}
*/
public function getFormObject($entity_type, $operation) {
if (!isset($this->handlers['form'][$operation][$entity_type])) {
if (!$class = $this->getDefinition($entity_type, TRUE)->getFormClass($operation)) {
throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a "%s" form class.', $entity_type, $operation));
}
$form_object = $this->classResolver->getInstanceFromDefinition($class);
$form_object
->setStringTranslation($this->stringTranslation)
->setModuleHandler($this->moduleHandler)
->setEntityTypeManager($this)
->setOperation($operation)
// The entity manager cannot be injected due to a circular dependency.
// @todo Remove this set call in https://www.drupal.org/node/2603542.
->setEntityManager(\Drupal::entityManager());
$this->handlers['form'][$operation][$entity_type] = $form_object;
if (!$class = $this->getDefinition($entity_type, TRUE)->getFormClass($operation)) {
throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a "%s" form class.', $entity_type, $operation));
}
return $this->handlers['form'][$operation][$entity_type];
$form_object = $this->classResolver->getInstanceFromDefinition($class);
return $form_object
->setStringTranslation($this->stringTranslation)
->setModuleHandler($this->moduleHandler)
->setEntityTypeManager($this)
->setOperation($operation)
// The entity manager cannot be injected due to a circular dependency.
// @todo Remove this set call in https://www.drupal.org/node/2603542.
->setEntityManager(\Drupal::entityManager());
}
/**

View file

@ -699,7 +699,6 @@ function hook_entity_type_alter(array &$entity_types) {
*
* @see \Drupal\Core\Entity\EntityManagerInterface::getAllViewModes()
* @see \Drupal\Core\Entity\EntityManagerInterface::getViewModes()
* @see hook_entity_view_mode_info()
*/
function hook_entity_view_mode_info_alter(&$view_modes) {
$view_modes['user']['full']['status'] = TRUE;

View file

@ -131,7 +131,7 @@ class TimestampFormatter extends FormatterBase implements ContainerFactoryPlugin
);
$elements['custom_date_format']['#states']['visible'][] = array(
':input[name="name="fields[' . $this->fieldDefinition->getName() . '][settings_edit_form][settings][date_format]"]' => array('value' => 'custom'),
':input[name="fields[' . $this->fieldDefinition->getName() . '][settings_edit_form][settings][date_format]"]' => array('value' => 'custom'),
);
$elements['timezone'] = array(

View file

@ -10,6 +10,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Email;
/**
* Plugin implementation of the 'email_default' widget.
@ -29,6 +30,7 @@ class EmailDefaultWidget extends WidgetBase {
*/
public static function defaultSettings() {
return array(
'size' => 60,
'placeholder' => '',
) + parent::defaultSettings();
}
@ -37,6 +39,13 @@ class EmailDefaultWidget extends WidgetBase {
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['size'] = array(
'#type' => 'number',
'#title' => $this->t('Textfield size'),
'#default_value' => $this->getSetting('size'),
'#required' => TRUE,
'#min' => 1,
);
$element['placeholder'] = array(
'#type' => 'textfield',
'#title' => t('Placeholder'),
@ -59,6 +68,7 @@ class EmailDefaultWidget extends WidgetBase {
else {
$summary[] = t('No placeholder');
}
$summary[] = t('Textfield size: @size', array('@size' => $this->getSetting('size')));
return $summary;
}
@ -71,6 +81,8 @@ class EmailDefaultWidget extends WidgetBase {
'#type' => 'email',
'#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL,
'#placeholder' => $this->getSetting('placeholder'),
'#size' => $this->getSetting('size'),
'#maxlength' => Email::EMAIL_MAX_LENGTH,
);
return $element;
}

View file

@ -81,6 +81,15 @@ class FormAjaxResponseBuilder implements FormAjaxResponseBuilderInterface {
$response = $result;
}
else {
// At this point we know callback returned a render element. If the
// element is part of the group (#group is set on it) it won't be rendered
// unless we remove #group from it. This is caused by
// \Drupal\Core\Render\Element\RenderElement::preRenderGroup(), which
// prevents all members of groups from being rendered directly.
if (!empty($result['#group'])) {
unset($result['#group']);
}
/** @var \Drupal\Core\Ajax\AjaxResponse $response */
$response = $this->ajaxRenderer->renderResponse($result, $request, $this->routeMatch);
}

View file

@ -29,7 +29,8 @@ use Drupal\Core\Language\LanguageInterface;
* - Any time UI text is displayed using PHP code, it should be passed through
* either the global t() function or a t() method on the class. If it
* involves plurals, it should be passed through either the global
* formatPlural() function or a formatPlural() method on the class. Use
* \Drupal\Core\StringTranslation\PluralTranslatableMarkup::createFromTranslatedString()
* or a formatPlural() method on the class. Use
* \Drupal\Core\StringTranslation\StringTranslationTrait to get these methods
* into a class.
* - Dates displayed in the UI should be passed through the 'date' service

View file

@ -14,11 +14,11 @@ namespace Drupal\Core\Lock;
*
* In most environments, multiple Drupal page requests (a.k.a. threads or
* processes) will execute in parallel. This leads to potential conflicts or
* race conditions when two requests execute the same code at the same time. A
* common example of this is a rebuild like menu_router_rebuild() where we
* invoke many hook implementations to get and process data from all active
* modules, and then delete the current data in the database to insert the new
* afterwards.
* race conditions when two requests execute the same code at the same time. For
* instance, some implementations of hook_cron() implicitly assume they are
* running only once, rather than having multiple calls in parallel. To prevent
* 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

View file

@ -9,6 +9,14 @@ namespace Drupal\Core\Menu;
/**
* Defines a contextual link plugin.
*
* Contextual links by default are in the module_name.links.contextual.yml
* file. These YAML files contain a list of contextual link plugin definitions,
* keyed by the plugin ID. Each definition must define a route_name and a group
* and might define title, options, and weight. See the getter methods on this
* interface for an explanation of each.
*
* @ingroup menu
*/
interface ContextualLinkInterface {

View file

@ -110,6 +110,10 @@ class MenuLinkDefaultForm implements MenuLinkFormInterface, ContainerInjectionIn
'#type' => 'item',
'#title' => $this->t('This link is provided by the @name module. The title and path cannot be edited.', array('@name' => $this->moduleHandler->getName($provider))),
);
$form['id'] = array(
'#type' => 'value',
'#value' => $this->menuLink->getPluginId(),
);
$link = array(
'#type' => 'link',
'#title' => $this->menuLink->getTitle(),
@ -158,7 +162,11 @@ class MenuLinkDefaultForm implements MenuLinkFormInterface, ContainerInjectionIn
* {@inheritdoc}
*/
public function extractFormValues(array &$form, FormStateInterface $form_state) {
$new_definition = array();
// Start from the complete, original, definition.
$new_definition = $this->menuLink->getPluginDefinition();
// Since the ID may not be present in the definition used to construct the
// plugin, add it here so it's available to any consumers of this method.
$new_definition['id'] = $form_state->getValue('id');
$new_definition['enabled'] = $form_state->getValue('enabled') ? 1 : 0;
$new_definition['weight'] = (int) $form_state->getValue('weight');
$new_definition['expanded'] = $form_state->getValue('expanded') ? 1 : 0;

View file

@ -38,7 +38,8 @@ interface MenuLinkFormInterface extends PluginFormInterface {
* The current state of the form.
*
* @return array
* The new plugin definition values taken from the form values.
* The new plugin definition values taken from the form values. The plugin
* ID must be returned as part of the definition.
*/
public function extractFormValues(array &$form, FormStateInterface $form_state);

View file

@ -13,7 +13,7 @@ namespace Drupal\Core\Path;
interface PathValidatorInterface {
/**
* Returns an URL object, if the path is valid and accessible.
* Returns a URL object, if the path is valid and accessible.
*
* @param string $path
* The path to check.
@ -24,7 +24,7 @@ interface PathValidatorInterface {
public function getUrlIfValid($path);
/**
* Returns an URL object, if the path is valid.
* Returns a URL object, if the path is valid.
*
* Unlike getUrlIfValid(), access check is not performed. Do not use this
* method if the $path is about to be presented to a user.

View file

@ -27,7 +27,7 @@ use Drupal\Core\Render\Element;
* @code
* $form['actions']['preview'] = array(
* '#type' => 'button',
* '#value => $this->t('Preview'),
* '#value' => $this->t('Preview'),
* );
* @endcode
*

View file

@ -16,13 +16,40 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
* Provides a form element for a table with radios or checkboxes in left column.
*
* Properties:
* - #header: Table headers used in the table.
* - #header: An array of table header labels.
* - #options: An associative array where each key is the value returned when
* a user selects the radio button or checkbox, and each value is the row of
* table data.
* - #empty: The message to display if table does not have any options.
* - #multiple: Set to FALSE to render the table with radios instead checkboxes.
* - #js_select: Set to FALSE if you don't want the select all checkbox added to
* the header.
*
* Other properties of the \Drupal\Core\Render\Element\Table element are also
* available.
*
* Usage example:
* See https://www.drupal.org/node/945102 for an example and full explanation.
* @code
* $header = [
* 'first_name' => t('First Name'),
* 'last_name' => t('Last Name'),
* ];
*
* $options = [
* 1 => ['first_name' => 'Indy', 'last_name' => 'Jones'],
* 2 => ['first_name' => 'Darth', 'last_name' => 'Vader'],
* 3 => ['first_name' => 'Super', 'last_name' => 'Man'],
* ];
*
* $form['table'] = array(
* '#type' => 'tableselect',
* '#header' => $header,
* '#options' => $options,
* '#empty' => t('No users found'),
* );
* @endcode
*
* See https://www.drupal.org/node/945102 for a full explanation.
*
* @see \Drupal\Core\Render\Element\Table
*

View file

@ -47,7 +47,7 @@ class ContentTypeHeaderMatcher implements RouteFilterInterface {
// We do not throw a
// \Symfony\Component\Routing\Exception\ResourceNotFoundException here
// because we don't want to return a 404 status code, but rather a 415.
throw new UnsupportedMediaTypeHttpException('No route found that matches the Content-Type header.');
throw new UnsupportedMediaTypeHttpException('No route found that matches "Content-Type: ' . $request->headers->get('Content-Type') . '"');
}
/**

View file

@ -34,7 +34,13 @@ interface RedirectDestinationInterface {
public function getAsArray();
/**
* Gets the destination as URL.
* Gets the destination as a path.
*
* To convert to a URL suitable for
* \Symfony\Component\HttpFoundation\RedirectResponse::__construct() use
* @code
* \Drupal\Core\Url::fromUserInput(\Drupal::destination()->get())->setAbsolute()->toString()
* @endcode
*
* @return string
*/

View file

@ -305,14 +305,18 @@ class UrlGenerator implements UrlGeneratorInterface {
return $collect_bubbleable_metadata ? $generated_url->setGeneratedUrl($url) : $url;
}
$options += array('prefix' => '');
$options += $route->getOption('default_url_options') ?: [];
$options += array('prefix' => '', 'path_processing' => TRUE);
$name = $this->getRouteDebugMessage($name);
$this->processRoute($name, $route, $parameters, $generated_url);
$path = $this->getInternalPathFromRoute($name, $route, $parameters, $query_params);
// Outbound path processors might need the route object for the path, e.g.
// to get the path pattern.
$options['route'] = $route;
$path = $this->processPath($path, $options, $generated_url);
if ($options['path_processing']) {
$path = $this->processPath($path, $options, $generated_url);
}
if (!empty($options['prefix'])) {
$path = ltrim($path, '/');

View file

@ -55,10 +55,15 @@ class StaticTranslation implements TranslatorInterface {
}
/**
* Add translations for new language.
* Retrieves translations for a given language.
*
* @param string $langcode
* The langcode of the language.
*
* @return array
* A multidimensional array of translations, indexed by the context the
* source string belongs to. The second level is using original strings as
* keys. An empty array will be returned when no translations are available.
*/
protected function getLanguage($langcode) {
// This class is usually a base class but we do not declare as abstract

View file

@ -254,7 +254,7 @@ class TwigExtension extends \Twig_Extension {
}
/**
* Gets a rendered link from an url object.
* Gets a rendered link from a url object.
*
* @param string $text
* The link text for the anchor tag as a translated string.
@ -313,7 +313,7 @@ class TwigExtension extends \Twig_Extension {
* Saves the unneeded automatic escaping for performance reasons.
*
* The URL generation process percent encodes non-alphanumeric characters.
* Thus, the only character within an URL that must be escaped in HTML is the
* Thus, the only character within a URL that must be escaped in HTML is the
* ampersand ("&") which separates query params. Thus we cannot mark
* the generated URL as always safe, but only when we are sure there won't be
* multiple query params. This is the case when there are none or only one
@ -402,6 +402,10 @@ class TwigExtension extends \Twig_Extension {
* @return string|null
* The escaped, rendered output, or NULL if there is no valid output.
*
* @throws \Exception
* When $arg is passed as an object which does not implement __toString(),
* RenderableInterface or toString().
*
* @todo Refactor this to keep it in sync with theme_render_and_autoescape()
* in https://www.drupal.org/node/2575065
*/
@ -433,7 +437,7 @@ class TwigExtension extends \Twig_Extension {
elseif (method_exists($arg, '__toString')) {
$return = (string) $arg;
}
// You can't throw exceptions in the magic PHP __toString methods, see
// You can't throw exceptions in the magic PHP __toString() methods, see
// http://php.net/manual/en/language.oop5.magic.php#object.tostring so
// we also support a toString method.
elseif (method_exists($arg, 'toString')) {
@ -471,10 +475,11 @@ class TwigExtension extends \Twig_Extension {
/**
* Wrapper around render() for twig printed output.
*
* If an object is passed that has no __toString method an exception is thrown;
* other objects are casted to string. However in the case that the object is an
* instance of a Twig_Markup object it is returned directly to support auto
* escaping.
* If an object is passed which does not implement __toString(),
* RenderableInterface or toString() then an exception is thrown;
* Other objects are casted to string. However in the case that the
* object is an instance of a Twig_Markup object it is returned directly
* to support auto escaping.
*
* If an array is passed it is rendered via render() and scalar values are
* returned directly.
@ -485,6 +490,10 @@ class TwigExtension extends \Twig_Extension {
* @return mixed
* The rendered output or an Twig_Markup object.
*
* @throws \Exception
* When $arg is passed as an object which does not implement __toString(),
* RenderableInterface or toString().
*
* @see render
* @see TwigNodeVisitor
*/
@ -511,7 +520,7 @@ class TwigExtension extends \Twig_Extension {
elseif (method_exists($arg, '__toString')) {
return (string) $arg;
}
// You can't throw exceptions in the magic PHP __toString methods, see
// You can't throw exceptions in the magic PHP __toString() methods, see
// http://php.net/manual/en/language.oop5.magic.php#object.tostring so
// we also support a toString method.
elseif (method_exists($arg, 'toString')) {

View file

@ -40,7 +40,8 @@ class Registry implements DestructableInterface {
* The complete theme registry.
*
* @var array
* An associative array keyed by theme hook names, whose values are
* An array of theme registries, keyed by the theme name. Each registry is
* an associative array keyed by theme hook names, whose values are
* associative arrays containing the aggregated hook definition:
* - type: The type of the extension the original theme hook originates
* from; e.g., 'module' for theme hook 'node' of Node module.
@ -79,7 +80,7 @@ class Registry implements DestructableInterface {
* - process: An array of theme variable process callbacks to invoke
* before invoking the actual theme function or template.
*/
protected $registry;
protected $registry = [];
/**
* The cache backend to use for the complete theme registry data.
@ -96,11 +97,11 @@ class Registry implements DestructableInterface {
protected $moduleHandler;
/**
* The incomplete, runtime theme registry.
* An array of incomplete, runtime theme registries, keyed by theme name.
*
* @var \Drupal\Core\Utility\ThemeRegistry
* @var \Drupal\Core\Utility\ThemeRegistry[]
*/
protected $runtimeRegistry;
protected $runtimeRegistry = [];
/**
* Stores whether the registry was already initialized.
@ -209,20 +210,20 @@ class Registry implements DestructableInterface {
*/
public function get() {
$this->init($this->themeName);
if (isset($this->registry)) {
return $this->registry;
if (isset($this->registry[$this->theme->getName()])) {
return $this->registry[$this->theme->getName()];
}
if ($cache = $this->cache->get('theme_registry:' . $this->theme->getName())) {
$this->registry = $cache->data;
$this->registry[$this->theme->getName()] = $cache->data;
}
else {
$this->registry = $this->build();
$this->build();
// Only persist it if all modules are loaded to ensure it is complete.
if ($this->moduleHandler->isLoaded()) {
$this->setCache();
}
}
return $this->registry;
return $this->registry[$this->theme->getName()];
}
/**
@ -235,17 +236,17 @@ class Registry implements DestructableInterface {
*/
public function getRuntime() {
$this->init($this->themeName);
if (!isset($this->runtimeRegistry)) {
$this->runtimeRegistry = new ThemeRegistry('theme_registry:runtime:' . $this->theme->getName(), $this->cache, $this->lock, array('theme_registry'), $this->moduleHandler->isLoaded());
if (!isset($this->runtimeRegistry[$this->theme->getName()])) {
$this->runtimeRegistry[$this->theme->getName()] = new ThemeRegistry('theme_registry:runtime:' . $this->theme->getName(), $this->cache, $this->lock, array('theme_registry'), $this->moduleHandler->isLoaded());
}
return $this->runtimeRegistry;
return $this->runtimeRegistry[$this->theme->getName()];
}
/**
* Persists the theme registry in the cache backend.
*/
protected function setCache() {
$this->cache->set('theme_registry:' . $this->theme->getName(), $this->registry, Cache::PERMANENT, array('theme_registry'));
$this->cache->set('theme_registry:' . $this->theme->getName(), $this->registry[$this->theme->getName()], Cache::PERMANENT, array('theme_registry'));
}
/**
@ -359,9 +360,9 @@ class Registry implements DestructableInterface {
unset($cache[$hook]['preprocess functions']);
}
}
$this->registry = $cache;
$this->registry[$this->theme->getName()] = $cache;
return $this->registry;
return $this->registry[$this->theme->getName()];
}
/**
@ -719,12 +720,12 @@ class Registry implements DestructableInterface {
*/
public function reset() {
// Reset the runtime registry.
if (isset($this->runtimeRegistry) && $this->runtimeRegistry instanceof ThemeRegistry) {
$this->runtimeRegistry->clear();
foreach ($this->runtimeRegistry as $runtime_registry) {
$runtime_registry->clear();
}
$this->runtimeRegistry = NULL;
$this->runtimeRegistry = [];
$this->registry = NULL;
$this->registry = [];
Cache::invalidateTags(array('theme_registry'));
return $this;
}
@ -733,8 +734,8 @@ class Registry implements DestructableInterface {
* {@inheritdoc}
*/
public function destruct() {
if (isset($this->runtimeRegistry)) {
$this->runtimeRegistry->destruct();
foreach ($this->runtimeRegistry as $runtime_registry) {
$runtime_registry->destruct();
}
}

View file

@ -28,11 +28,9 @@ abstract class ComplexDataDefinitionBase extends DataDefinition implements Compl
* {@inheritdoc}
*/
public function getPropertyDefinition($name) {
if (!isset($this->propertyDefinitions)) {
$this->getPropertyDefinitions();
}
if (isset($this->propertyDefinitions[$name])) {
return $this->propertyDefinitions[$name];
$definitions = $this->getPropertyDefinitions();
if (isset($definitions[$name])) {
return $definitions[$name];
}
}

View file

@ -199,4 +199,23 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
public function getParent() {
return $this->parent;
}
/**
* {@inheritdoc}
*/
public function __sleep() {
$vars = get_object_vars($this);
// Prevent services from being serialized. static::getStringTranslation()
// and static::getTypedDataManager() lazy-load them after $this has been
// unserialized.
// @todo Replace this with
// \Drupal\Core\DependencyInjection\DependencySerializationTrait before
// Drupal 9.0.0. We cannot use that now, because child classes already use
// it and PHP 5 would consider that conflicts.
unset($vars['stringTranslation']);
unset($vars['typedDataManager']);
return array_keys($vars);
}
}

View file

@ -76,6 +76,7 @@ class Error {
'%line' => $caller['line'],
'severity_level' => static::ERROR,
'backtrace' => $backtrace,
'backtrace_string' => $exception->getTraceAsString(),
);
}
@ -179,7 +180,12 @@ class Error {
}
}
$return .= $call['function'] . '(' . implode(', ', $call['args']) . ")\n";
$line = '';
if (isset($trace['line'])) {
$line = " (Line: {$trace['line']})";
}
$return .= $call['function'] . '(' . implode(', ', $call['args']) . ")$line\n";
}
return $return;