Update to Drupal 8.0.0 beta 14. For more information, see https://drupal.org/node/2544542
This commit is contained in:
parent
3b2511d96d
commit
81ccda77eb
2155 changed files with 54307 additions and 46870 deletions
|
@ -25,6 +25,9 @@ use Drupal\Core\Session\AccountInterface;
|
|||
*
|
||||
* When using ::orIf() and ::andIf(), cacheability metadata will be merged
|
||||
* accordingly as well.
|
||||
*
|
||||
* @todo Use RefinableCacheableDependencyInterface and the corresponding trait in
|
||||
* https://www.drupal.org/node/2526326.
|
||||
*/
|
||||
abstract class AccessResult implements AccessResultInterface, CacheableDependencyInterface {
|
||||
|
||||
|
@ -316,10 +319,12 @@ abstract class AccessResult implements AccessResultInterface, CacheableDependenc
|
|||
* The entity whose cache tag to set on the access result.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. Use
|
||||
* ::addCacheableDependency() instead.
|
||||
*/
|
||||
public function cacheUntilEntityChanges(EntityInterface $entity) {
|
||||
$this->addCacheTags($entity->getCacheTags());
|
||||
return $this;
|
||||
return $this->addCacheableDependency($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -329,9 +334,33 @@ abstract class AccessResult implements AccessResultInterface, CacheableDependenc
|
|||
* The configuration object whose cache tag to set on the access result.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. Use
|
||||
* ::addCacheableDependency() instead.
|
||||
*/
|
||||
public function cacheUntilConfigurationChanges(ConfigBase $configuration) {
|
||||
$this->addCacheTags($configuration->getCacheTags());
|
||||
return $this->addCacheableDependency($configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency on an object: merges its cacheability metadata.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheableDependencyInterface|object $other_object
|
||||
* The dependency. If the object implements CacheableDependencyInterface,
|
||||
* then its cacheability metadata will be used. Otherwise, the passed in
|
||||
* object must be assumed to be uncacheable, so max-age 0 is set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCacheableDependency($other_object) {
|
||||
if ($other_object instanceof CacheableDependencyInterface) {
|
||||
$this->contexts = Cache::mergeContexts($this->contexts, $other_object->getCacheContexts());
|
||||
$this->tags = Cache::mergeTags($this->tags, $other_object->getCacheTags());
|
||||
$this->maxAge = Cache::mergeMaxAges($this->maxAge, $other_object->getCacheMaxAge());
|
||||
}
|
||||
else {
|
||||
$this->maxAge = 0;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Access;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
|
@ -36,7 +36,7 @@ class RouteProcessorCsrf implements OutboundRouteProcessorInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) {
|
||||
public function processOutbound($route_name, Route $route, array &$parameters, BubbleableMetadata $bubbleable_metadata = NULL) {
|
||||
if ($route->hasRequirement('_csrf_token')) {
|
||||
$path = ltrim($route->getPath(), '/');
|
||||
// Replace the path parameters with values from the parameters array.
|
||||
|
@ -45,13 +45,44 @@ class RouteProcessorCsrf implements OutboundRouteProcessorInterface {
|
|||
}
|
||||
// Adding this to the parameters means it will get merged into the query
|
||||
// string when the route is compiled.
|
||||
$parameters['token'] = $this->csrfToken->get($path);
|
||||
if ($cacheable_metadata) {
|
||||
// Tokens are per user and per session, so not cacheable.
|
||||
// @todo Improve in https://www.drupal.org/node/2351015.
|
||||
$cacheable_metadata->setCacheMaxAge(0);
|
||||
if (!$bubbleable_metadata) {
|
||||
$parameters['token'] = $this->csrfToken->get($path);
|
||||
}
|
||||
else {
|
||||
// Generate a placeholder and a render array to replace it.
|
||||
$placeholder = hash('sha1', $path);
|
||||
$placeholder_render_array = [
|
||||
'#lazy_builder' => ['route_processor_csrf:renderPlaceholderCsrfToken', [$path]],
|
||||
];
|
||||
|
||||
// Instead of setting an actual CSRF token as the query string, we set
|
||||
// the placeholder, which will be replaced at the very last moment. This
|
||||
// ensures links with CSRF tokens don't break cacheability.
|
||||
$parameters['token'] = $placeholder;
|
||||
$bubbleable_metadata->addAttachments(['placeholders' => [$placeholder => $placeholder_render_array]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; gets a CSRF token for the given path.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to get a CSRF token for.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array representing the CSRF token.
|
||||
*/
|
||||
public function renderPlaceholderCsrfToken($path) {
|
||||
return [
|
||||
'#markup' => $this->csrfToken->get($path),
|
||||
// Tokens are per session.
|
||||
'#cache' => [
|
||||
'contexts' => [
|
||||
'session',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -164,15 +164,15 @@ class AjaxResponseAttachmentsProcessor implements AttachmentsResponseProcessorIn
|
|||
$resource_commands = array();
|
||||
if ($css_assets) {
|
||||
$css_render_array = $this->cssCollectionRenderer->render($css_assets);
|
||||
$resource_commands[] = new AddCssCommand($this->renderer->renderPlain($css_render_array));
|
||||
$resource_commands[] = new AddCssCommand((string) $this->renderer->renderPlain($css_render_array));
|
||||
}
|
||||
if ($js_assets_header) {
|
||||
$js_header_render_array = $this->jsCollectionRenderer->render($js_assets_header);
|
||||
$resource_commands[] = new PrependCommand('head', $this->renderer->renderPlain($js_header_render_array));
|
||||
$resource_commands[] = new PrependCommand('head', (string) $this->renderer->renderPlain($js_header_render_array));
|
||||
}
|
||||
if ($js_assets_footer) {
|
||||
$js_footer_render_array = $this->jsCollectionRenderer->render($js_assets_footer);
|
||||
$resource_commands[] = new AppendCommand('body', $this->renderer->renderPlain($js_footer_render_array));
|
||||
$resource_commands[] = new AppendCommand('body', (string) $this->renderer->renderPlain($js_footer_render_array));
|
||||
}
|
||||
foreach (array_reverse($resource_commands) as $resource_command) {
|
||||
$response->addCommand($resource_command, TRUE);
|
||||
|
|
|
@ -37,10 +37,10 @@ trait CommandWithAttachedAssetsTrait {
|
|||
if (is_array($this->content)) {
|
||||
$html = \Drupal::service('renderer')->renderRoot($this->content);
|
||||
$this->attachedAssets = AttachedAssets::createFromRenderArray($this->content);
|
||||
return $html;
|
||||
return (string) $html;
|
||||
}
|
||||
else {
|
||||
return $this->content;
|
||||
return (string) $this->content;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Authentication\AuthenticationCollector.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Authentication;
|
||||
|
||||
/**
|
||||
* A collector class for authentication providers.
|
||||
*/
|
||||
class AuthenticationCollector implements AuthenticationCollectorInterface {
|
||||
|
||||
/**
|
||||
* Array of all registered authentication providers, keyed by ID.
|
||||
*
|
||||
* @var \Drupal\Core\Authentication\AuthenticationProviderInterface[]
|
||||
*/
|
||||
protected $providers;
|
||||
|
||||
/**
|
||||
* Array of all providers and their priority.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $providerOrders = [];
|
||||
|
||||
/**
|
||||
* Sorted list of registered providers.
|
||||
*
|
||||
* @var \Drupal\Core\Authentication\AuthenticationProviderInterface[]
|
||||
*/
|
||||
protected $sortedProviders;
|
||||
|
||||
/**
|
||||
* List of providers which are allowed on routes with no _auth option.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $globalProviders;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addProvider(AuthenticationProviderInterface $provider, $provider_id, $priority = 0, $global = FALSE) {
|
||||
$this->providers[$provider_id] = $provider;
|
||||
$this->providerOrders[$priority][$provider_id] = $provider;
|
||||
// Force the providers to be re-sorted.
|
||||
$this->sortedProviders = NULL;
|
||||
|
||||
if ($global) {
|
||||
$this->globalProviders[$provider_id] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isGlobal($provider_id) {
|
||||
return isset($this->globalProviders[$provider_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProvider($provider_id) {
|
||||
return isset($this->providers[$provider_id]) ? $this->providers[$provider_id] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSortedProviders() {
|
||||
if (!isset($this->sortedProviders)) {
|
||||
// Sort the providers according to priority.
|
||||
krsort($this->providerOrders);
|
||||
|
||||
// Merge nested providers from $this->providers into $this->sortedProviders.
|
||||
$this->sortedProviders = [];
|
||||
foreach ($this->providerOrders as $providers) {
|
||||
$this->sortedProviders = array_merge($this->sortedProviders, $providers);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->sortedProviders;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Authentication\AuthenticationCollectorInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Authentication;
|
||||
|
||||
/**
|
||||
* Interface for collectors of registered authentication providers.
|
||||
*/
|
||||
interface AuthenticationCollectorInterface {
|
||||
|
||||
/**
|
||||
* Adds a provider to the array of registered providers.
|
||||
*
|
||||
* @param \Drupal\Core\Authentication\AuthenticationProviderInterface $provider
|
||||
* The provider object.
|
||||
* @param string $provider_id
|
||||
* Identifier of the provider.
|
||||
* @param int $priority
|
||||
* (optional) The provider's priority.
|
||||
* @param bool $global
|
||||
* (optional) TRUE if the provider is to be applied globally on all routes.
|
||||
* Defaults to FALSE.
|
||||
*/
|
||||
public function addProvider(AuthenticationProviderInterface $provider, $provider_id, $priority = 0, $global = FALSE);
|
||||
|
||||
/**
|
||||
* Returns whether a provider is considered global.
|
||||
*
|
||||
* @param string $provider_id
|
||||
* The provider ID.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the provider is global, FALSE otherwise.
|
||||
*
|
||||
* @see \Drupal\Core\Authentication\AuthenticationCollectorInterface::addProvider
|
||||
*/
|
||||
public function isGlobal($provider_id);
|
||||
|
||||
/**
|
||||
* Returns an authentication provider.
|
||||
*
|
||||
* @param string $provider_id
|
||||
* The provider ID.
|
||||
*
|
||||
* @return \Drupal\Core\Authentication\AuthenticationProviderInterface|NULL
|
||||
* The authentication provider which matches the ID.
|
||||
*/
|
||||
public function getProvider($provider_id);
|
||||
|
||||
/**
|
||||
* Returns the sorted array of authentication providers.
|
||||
*
|
||||
* @return \Drupal\Core\Authentication\AuthenticationProviderInterface[]
|
||||
* An array of authentication provider objects.
|
||||
*/
|
||||
public function getSortedProviders();
|
||||
|
||||
}
|
|
@ -20,69 +20,23 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
*
|
||||
* If no provider set an active user then the user is set to anonymous.
|
||||
*/
|
||||
class AuthenticationManager implements AuthenticationProviderInterface, AuthenticationProviderFilterInterface, AuthenticationProviderChallengeInterface, AuthenticationManagerInterface {
|
||||
class AuthenticationManager implements AuthenticationProviderInterface, AuthenticationProviderFilterInterface, AuthenticationProviderChallengeInterface {
|
||||
|
||||
/**
|
||||
* Array of all registered authentication providers, keyed by ID.
|
||||
* The authentication provider collector.
|
||||
*
|
||||
* @var \Drupal\Core\Authentication\AuthenticationProviderInterface[]
|
||||
* @var \Drupal\Core\Authentication\AuthenticationCollectorInterface
|
||||
*/
|
||||
protected $providers;
|
||||
protected $authCollector;
|
||||
|
||||
/**
|
||||
* Array of all providers and their priority.
|
||||
* Creates a new authentication manager instance.
|
||||
*
|
||||
* @var array
|
||||
* @param \Drupal\Core\Authentication\AuthenticationCollectorInterface $auth_collector
|
||||
* The authentication provider collector.
|
||||
*/
|
||||
protected $providerOrders = array();
|
||||
|
||||
/**
|
||||
* Sorted list of registered providers.
|
||||
*
|
||||
* @var \Drupal\Core\Authentication\AuthenticationProviderInterface[]
|
||||
*/
|
||||
protected $sortedProviders;
|
||||
|
||||
/**
|
||||
* List of providers which implement the filter interface.
|
||||
*
|
||||
* @var \Drupal\Core\Authentication\AuthenticationProviderFilterInterface[]
|
||||
*/
|
||||
protected $filters;
|
||||
|
||||
/**
|
||||
* List of providers which implement the challenge interface.
|
||||
*
|
||||
* @var \Drupal\Core\Authentication\AuthenticationProviderChallengeInterface[]
|
||||
*/
|
||||
protected $challengers;
|
||||
|
||||
/**
|
||||
* List of providers which are allowed on routes with no _auth option.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $globalProviders;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addProvider(AuthenticationProviderInterface $provider, $provider_id, $priority = 0, $global = FALSE) {
|
||||
$this->providers[$provider_id] = $provider;
|
||||
$this->providerOrders[$priority][$provider_id] = $provider;
|
||||
// Force the builders to be re-sorted.
|
||||
$this->sortedProviders = NULL;
|
||||
|
||||
if ($provider instanceof AuthenticationProviderFilterInterface) {
|
||||
$this->filters[$provider_id] = $provider;
|
||||
}
|
||||
if ($provider instanceof AuthenticationProviderChallengeInterface) {
|
||||
$this->challengers[$provider_id] = $provider;
|
||||
}
|
||||
|
||||
if ($global) {
|
||||
$this->globalProviders[$provider_id] = TRUE;
|
||||
}
|
||||
public function __construct(AuthenticationCollectorInterface $auth_collector) {
|
||||
$this->authCollector = $auth_collector;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,7 +51,13 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
|
|||
*/
|
||||
public function authenticate(Request $request) {
|
||||
$provider_id = $this->getProvider($request);
|
||||
return $this->providers[$provider_id]->authenticate($request);
|
||||
$provider = $this->authCollector->getProvider($provider_id);
|
||||
|
||||
if ($provider) {
|
||||
return $provider->authenticate($request);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,7 +70,7 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
|
|||
$result = $this->applyFilter($request, $authenticated, $this->getProvider($request));
|
||||
}
|
||||
else {
|
||||
foreach ($this->getSortedProviders() as $provider_id => $provider) {
|
||||
foreach ($this->authCollector->getSortedProviders() as $provider_id => $provider) {
|
||||
if ($this->applyFilter($request, $authenticated, $provider_id)) {
|
||||
$result = TRUE;
|
||||
break;
|
||||
|
@ -126,8 +86,10 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
|
|||
*/
|
||||
public function challengeException(Request $request, \Exception $previous) {
|
||||
$provider_id = $this->getChallenger($request);
|
||||
|
||||
if ($provider_id) {
|
||||
return $this->challengers[$provider_id]->challengeException($request, $previous);
|
||||
$provider = $this->authCollector->getProvider($provider_id);
|
||||
return $provider->challengeException($request, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,7 +104,7 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
|
|||
* If no application detects appropriate credentials, then NULL is returned.
|
||||
*/
|
||||
protected function getProvider(Request $request) {
|
||||
foreach ($this->getSortedProviders() as $provider_id => $provider) {
|
||||
foreach ($this->authCollector->getSortedProviders() as $provider_id => $provider) {
|
||||
if ($provider->applies($request)) {
|
||||
return $provider_id;
|
||||
}
|
||||
|
@ -150,21 +112,19 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the challenge provider for a request.
|
||||
* Returns the ID of the challenge provider for a request.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The incoming request.
|
||||
*
|
||||
* @return string|NULL
|
||||
* The id of the first authentication provider which applies to the request.
|
||||
* The ID of the first authentication provider which applies to the request.
|
||||
* If no application detects appropriate credentials, then NULL is returned.
|
||||
*/
|
||||
protected function getChallenger(Request $request) {
|
||||
if (!empty($this->challengers)) {
|
||||
foreach ($this->getSortedProviders($request, FALSE) as $provider_id => $provider) {
|
||||
if (isset($this->challengers[$provider_id]) && !$provider->applies($request) && $this->applyFilter($request, FALSE, $provider_id)) {
|
||||
return $provider_id;
|
||||
}
|
||||
foreach ($this->authCollector->getSortedProviders() as $provider_id => $provider) {
|
||||
if (($provider instanceof AuthenticationProviderChallengeInterface) && !$provider->applies($request) && $this->applyFilter($request, FALSE, $provider_id)) {
|
||||
return $provider_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,8 +146,10 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
|
|||
* TRUE if provider is allowed, FALSE otherwise.
|
||||
*/
|
||||
protected function applyFilter(Request $request, $authenticated, $provider_id) {
|
||||
if (isset($this->filters[$provider_id])) {
|
||||
$result = $this->filters[$provider_id]->appliesToRoutedRequest($request, $authenticated);
|
||||
$provider = $this->authCollector->getProvider($provider_id);
|
||||
|
||||
if ($provider && ($provider instanceof AuthenticationProviderFilterInterface)) {
|
||||
$result = $provider->appliesToRoutedRequest($request, $authenticated);
|
||||
}
|
||||
else {
|
||||
$result = $this->defaultFilter($request, $provider_id);
|
||||
|
@ -222,27 +184,8 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
|
|||
return in_array($provider_id, $route->getOption('_auth'));
|
||||
}
|
||||
else {
|
||||
return isset($this->globalProviders[$provider_id]);
|
||||
return $this->authCollector->isGlobal($provider_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sorted array of authentication providers.
|
||||
*
|
||||
* @return \Drupal\Core\Authentication\AuthenticationProviderInterface[]
|
||||
* An array of authentication provider objects.
|
||||
*/
|
||||
protected function getSortedProviders() {
|
||||
if (!isset($this->sortedProviders)) {
|
||||
// Sort the builders according to priority.
|
||||
krsort($this->providerOrders);
|
||||
// Merge nested providers from $this->providers into $this->sortedProviders.
|
||||
$this->sortedProviders = array();
|
||||
foreach ($this->providerOrders as $providers) {
|
||||
$this->sortedProviders = array_merge($this->sortedProviders, $providers);
|
||||
}
|
||||
}
|
||||
return $this->sortedProviders;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Authentication\AuthenticationManagerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Authentication;
|
||||
|
||||
/**
|
||||
* Defines an interface for authentication managers.
|
||||
*/
|
||||
interface AuthenticationManagerInterface {
|
||||
|
||||
/**
|
||||
* Adds a provider to the array of registered providers.
|
||||
*
|
||||
* @param \Drupal\Core\Authentication\AuthenticationProviderInterface $provider
|
||||
* The provider object.
|
||||
* @param string $provider_id
|
||||
* Identifier of the provider.
|
||||
* @param int $priority
|
||||
* (optional) The provider's priority.
|
||||
* @param bool $global
|
||||
* (optional) TRUE if the provider is to be applied globally on all routes.
|
||||
* Defaults to FALSE.
|
||||
*/
|
||||
public function addProvider(AuthenticationProviderInterface $provider, $provider_id, $priority = 0, $global = FALSE);
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ namespace Drupal\Core\Block;
|
|||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginBase;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
|
@ -265,18 +266,23 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn
|
|||
// \Drupal\system\MachineNameController::transliterate(), so it might make
|
||||
// sense to provide a common service for the two.
|
||||
$transliterated = $this->transliteration()->transliterate($admin_label, LanguageInterface::LANGCODE_DEFAULT, '_');
|
||||
|
||||
$replace_pattern = '[^a-z0-9_.]+';
|
||||
|
||||
$transliterated = Unicode::strtolower($transliterated);
|
||||
|
||||
if (isset($replace_pattern)) {
|
||||
$transliterated = preg_replace('@' . $replace_pattern . '@', '', $transliterated);
|
||||
}
|
||||
$transliterated = preg_replace('@[^a-z0-9_.]+@', '', $transliterated);
|
||||
|
||||
return $transliterated;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
$max_age = parent::getCacheMaxAge();
|
||||
// @todo Configurability of this will be removed in
|
||||
// https://www.drupal.org/node/2458763.
|
||||
return Cache::mergeMaxAges($max_age, (int) $this->configuration['cache']['max_age']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the transliteration service.
|
||||
*
|
||||
|
@ -299,25 +305,4 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn
|
|||
$this->transliteration = $transliteration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return (int)$this->configuration['cache']['max_age'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Breadcrumb;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
|
@ -95,7 +94,7 @@ class BreadcrumbManager implements ChainBreadcrumbBuilderInterface {
|
|||
break;
|
||||
}
|
||||
else {
|
||||
throw new \UnexpectedValueException(SafeMarkup::format('Invalid breadcrumb returned by !class::build().', array('!class' => get_class($builder))));
|
||||
throw new \UnexpectedValueException('Invalid breadcrumb returned by ' . get_class($builder) . '::build().');
|
||||
}
|
||||
}
|
||||
// Allow modules to alter the breadcrumb.
|
||||
|
|
|
@ -24,19 +24,16 @@ class Cache {
|
|||
/**
|
||||
* Merges arrays of cache contexts and removes duplicates.
|
||||
*
|
||||
* @param string[] …
|
||||
* Arrays of cache contexts to merge.
|
||||
* @param array $a
|
||||
* Cache contexts array to merge.
|
||||
* @param array $b
|
||||
* Cache contexts array to merge.
|
||||
*
|
||||
* @return string[]
|
||||
* The merged array of cache contexts.
|
||||
*/
|
||||
public static function mergeContexts() {
|
||||
$cache_context_arrays = func_get_args();
|
||||
$cache_contexts = [];
|
||||
foreach ($cache_context_arrays as $contexts) {
|
||||
$cache_contexts = array_merge($cache_contexts, $contexts);
|
||||
}
|
||||
$cache_contexts = array_unique($cache_contexts);
|
||||
public static function mergeContexts(array $a = [], array $b = []) {
|
||||
$cache_contexts = array_unique(array_merge($a, $b));
|
||||
\Drupal::service('cache_contexts_manager')->validateTokens($cache_contexts);
|
||||
sort($cache_contexts);
|
||||
return $cache_contexts;
|
||||
|
@ -53,19 +50,16 @@ class Cache {
|
|||
* allows items to be invalidated based on all tags attached to the content
|
||||
* they're constituted from.
|
||||
*
|
||||
* @param string[] …
|
||||
* Arrays of cache tags to merge.
|
||||
* @param array $a
|
||||
* Cache tags array to merge.
|
||||
* @param array $b
|
||||
* Cache tags array to merge.
|
||||
*
|
||||
* @return string[]
|
||||
* The merged array of cache tags.
|
||||
*/
|
||||
public static function mergeTags() {
|
||||
$cache_tag_arrays = func_get_args();
|
||||
$cache_tags = [];
|
||||
foreach ($cache_tag_arrays as $tags) {
|
||||
$cache_tags = array_merge($cache_tags, $tags);
|
||||
}
|
||||
$cache_tags = array_unique($cache_tags);
|
||||
public static function mergeTags(array $a = [], array $b = []) {
|
||||
$cache_tags = array_unique(array_merge($a, $b));
|
||||
static::validateTags($cache_tags);
|
||||
sort($cache_tags);
|
||||
return $cache_tags;
|
||||
|
@ -76,29 +70,25 @@ class Cache {
|
|||
*
|
||||
* Ensures infinite max-age (Cache::PERMANENT) is taken into account.
|
||||
*
|
||||
* @param int …
|
||||
* Max-age values.
|
||||
* @param int $a
|
||||
* Max age value to merge.
|
||||
* @param int $b
|
||||
* Max age value to merge.
|
||||
*
|
||||
* @return int
|
||||
* The minimum max-age value.
|
||||
*/
|
||||
public static function mergeMaxAges() {
|
||||
$max_ages = func_get_args();
|
||||
|
||||
// Filter out all max-age values set to cache permanently.
|
||||
if (in_array(Cache::PERMANENT, $max_ages)) {
|
||||
$max_ages = array_filter($max_ages, function ($max_age) {
|
||||
return $max_age !== Cache::PERMANENT;
|
||||
});
|
||||
|
||||
// If nothing is left, then all max-age values were set to cache
|
||||
// permanently, and then that is the result.
|
||||
if (empty($max_ages)) {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
public static function mergeMaxAges($a = Cache::PERMANENT, $b = Cache::PERMANENT) {
|
||||
// If one of the values is Cache::PERMANENT, return the other value.
|
||||
if ($a === Cache::PERMANENT){
|
||||
return $b;
|
||||
}
|
||||
if ($b === Cache::PERMANENT){
|
||||
return $a;
|
||||
}
|
||||
|
||||
return min($max_ages);
|
||||
// If none or the values are Cache::PERMANENT, return the minimum value.
|
||||
return min($a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,9 @@ namespace Drupal\Core\Cache;
|
|||
* Defines a generic class for passing cacheability metadata.
|
||||
*
|
||||
* @ingroup cache
|
||||
*
|
||||
* @todo Use RefinableCacheableDependencyInterface and the corresponding trait in
|
||||
* https://www.drupal.org/node/2526326.
|
||||
*/
|
||||
class CacheableMetadata implements CacheableDependencyInterface {
|
||||
|
||||
|
@ -129,6 +132,35 @@ class CacheableMetadata implements CacheableDependencyInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency on an object: merges its cacheability metadata.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $other_object
|
||||
* The dependency. If the object implements CacheableDependencyInterface,
|
||||
* then its cacheability metadata will be used. Otherwise, the passed in
|
||||
* object must be assumed to be uncacheable, so max-age 0 is set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCacheableDependency($other_object) {
|
||||
if ($other_object instanceof CacheableDependencyInterface) {
|
||||
$this->addCacheTags($other_object->getCacheTags());
|
||||
$this->addCacheContexts($other_object->getCacheContexts());
|
||||
if ($this->maxAge === Cache::PERMANENT) {
|
||||
$this->maxAge = $other_object->getCacheMaxAge();
|
||||
}
|
||||
elseif (($max_age = $other_object->getCacheMaxAge()) && $max_age !== Cache::PERMANENT) {
|
||||
$this->maxAge = Cache::mergeMaxAges($this->maxAge, $max_age);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Not a cacheable dependency, this can not be cached.
|
||||
$this->maxAge = 0;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the values of another CacheableMetadata object with this one.
|
||||
*
|
||||
|
@ -139,7 +171,7 @@ class CacheableMetadata implements CacheableDependencyInterface {
|
|||
* A new CacheableMetadata object, with the merged data.
|
||||
*/
|
||||
public function merge(CacheableMetadata $other) {
|
||||
$result = new static();
|
||||
$result = clone $this;
|
||||
|
||||
// This is called many times per request, so avoid merging unless absolutely
|
||||
// necessary.
|
||||
|
|
|
@ -7,18 +7,21 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Session\PermissionsHashGeneratorInterface;
|
||||
|
||||
/**
|
||||
* Defines the AccountPermissionsCacheContext service, for "per permission" caching.
|
||||
*
|
||||
* Cache context ID: 'user.permissions'.
|
||||
*/
|
||||
class AccountPermissionsCacheContext extends UserCacheContext {
|
||||
class AccountPermissionsCacheContext extends UserCacheContextBase implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* The permissions hash generator.
|
||||
*
|
||||
* @var \Drupal\user\PermissionsHashInterface
|
||||
* @var \Drupal\Core\Session\PermissionsHashGeneratorInterface
|
||||
*/
|
||||
protected $permissionsHashGenerator;
|
||||
|
||||
|
@ -27,7 +30,7 @@ class AccountPermissionsCacheContext extends UserCacheContext {
|
|||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $user
|
||||
* The current user.
|
||||
* @param \Drupal\user\PermissionsHashInterface $permissions_hash_generator
|
||||
* @param \Drupal\Core\Session\PermissionsHashGeneratorInterface $permissions_hash_generator
|
||||
* The permissions hash generator.
|
||||
*/
|
||||
public function __construct(AccountInterface $user, PermissionsHashGeneratorInterface $permissions_hash_generator) {
|
||||
|
@ -46,7 +49,24 @@ class AccountPermissionsCacheContext extends UserCacheContext {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return 'ph.' . $this->permissionsHashGenerator->generate($this->user);
|
||||
return $this->permissionsHashGenerator->generate($this->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
$cacheable_metadata = new CacheableMetadata();
|
||||
|
||||
// The permissions hash changes when:
|
||||
// - a user is updated to have different roles;
|
||||
$tags = ['user:' . $this->user->id()];
|
||||
// - a role is updated to have different permissions.
|
||||
foreach ($this->user->getRoles() as $rid) {
|
||||
$tags[] = "config:user.role.$rid";
|
||||
}
|
||||
|
||||
return $cacheable_metadata->setCacheTags($tags);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,4 +31,21 @@ interface CacheContextInterface {
|
|||
*/
|
||||
public function getContext();
|
||||
|
||||
/**
|
||||
* Gets the cacheability metadata for the context.
|
||||
*
|
||||
* There are three valid cases for the returned CacheableMetadata object:
|
||||
* - An empty object means this can be optimized away safely.
|
||||
* - A max-age of 0 means that this context can never be optimized away. It
|
||||
* will never bubble up and cache tags will not be used.
|
||||
* - Any non-zero max-age and cache tags will bubble up into the cache item
|
||||
* if this is optimized away to allow for invalidation if the context
|
||||
* value changes.
|
||||
*
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheableMetadata
|
||||
* A cacheable metadata object.
|
||||
*/
|
||||
public function getCacheableMetadata();
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -99,23 +99,35 @@ class CacheContextsManager {
|
|||
* @param string[] $context_tokens
|
||||
* An array of cache context tokens.
|
||||
*
|
||||
* @return string[]
|
||||
* The array of corresponding cache keys.
|
||||
* @return \Drupal\Core\Cache\Context\ContextCacheKeys
|
||||
* The ContextCacheKeys object containing the converted cache keys and
|
||||
* cacheability metadata.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \LogicException
|
||||
* Thrown if any of the context tokens or parameters are not valid.
|
||||
*/
|
||||
public function convertTokensToKeys(array $context_tokens) {
|
||||
$context_tokens = $this->optimizeTokens($context_tokens);
|
||||
sort($context_tokens);
|
||||
$keys = [];
|
||||
foreach (static::parseTokens($context_tokens) as $context) {
|
||||
list($context_id, $parameter) = $context;
|
||||
if (!in_array($context_id, $this->contexts)) {
|
||||
throw new \InvalidArgumentException(SafeMarkup::format('"@context" is not a valid cache context ID.', ['@context' => $context_id]));
|
||||
}
|
||||
$keys[] = $this->getService($context_id)->getContext($parameter);
|
||||
$this->validateTokens($context_tokens);
|
||||
$cacheable_metadata = new CacheableMetadata();
|
||||
$optimized_tokens = $this->optimizeTokens($context_tokens);
|
||||
// Iterate over cache contexts that have been optimized away and get their
|
||||
// cacheability metadata.
|
||||
foreach (static::parseTokens(array_diff($context_tokens, $optimized_tokens)) as $context_token) {
|
||||
list($context_id, $parameter) = $context_token;
|
||||
$context = $this->getService($context_id);
|
||||
$cacheable_metadata = $cacheable_metadata->merge($context->getCacheableMetadata($parameter));
|
||||
}
|
||||
return $keys;
|
||||
|
||||
sort($optimized_tokens);
|
||||
$keys = [];
|
||||
foreach (array_combine($optimized_tokens, static::parseTokens($optimized_tokens)) as $context_token => $context) {
|
||||
list($context_id, $parameter) = $context;
|
||||
$keys[] = '[' . $context_token . ']=' . $this->getService($context_id)->getContext($parameter);
|
||||
}
|
||||
|
||||
// Create the returned object and merge in the cacheability metadata.
|
||||
$context_cache_keys = new ContextCacheKeys($keys);
|
||||
return $context_cache_keys->merge($cacheable_metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,6 +141,9 @@ class CacheContextsManager {
|
|||
* possible of a set of cache context tokens, that still captures the entire
|
||||
* universe of variations.
|
||||
*
|
||||
* If a cache context is being optimized away, it is able to set cacheable
|
||||
* metadata for itself which will be bubbled up.
|
||||
*
|
||||
* E.g. when caching per user ('user'), also caching per role ('user.roles')
|
||||
* is meaningless because "per role" is implied by "per user".
|
||||
*
|
||||
|
@ -150,6 +165,14 @@ class CacheContextsManager {
|
|||
public function optimizeTokens(array $context_tokens) {
|
||||
$optimized_content_tokens = [];
|
||||
foreach ($context_tokens as $context_token) {
|
||||
|
||||
// Extract the parameter if available.
|
||||
$parameter = NULL;
|
||||
$context_id = $context_token;
|
||||
if (strpos($context_token, ':') !== FALSE) {
|
||||
list($context_id, $parameter) = explode(':', $context_token);
|
||||
}
|
||||
|
||||
// Context tokens without:
|
||||
// - a period means they don't have a parent
|
||||
// - a colon means they're not a specific value of a cache context
|
||||
|
@ -157,6 +180,11 @@ class CacheContextsManager {
|
|||
if (strpos($context_token, '.') === FALSE && strpos($context_token, ':') === FALSE) {
|
||||
$optimized_content_tokens[] = $context_token;
|
||||
}
|
||||
// Check cacheability. If the context defines a max-age of 0, then it
|
||||
// can not be optimized away. Pass the parameter along if we have one.
|
||||
elseif ($this->getService($context_id)->getCacheableMetadata($parameter)->getCacheMaxAge() === 0) {
|
||||
$optimized_content_tokens[] = $context_token;
|
||||
}
|
||||
// The context token has a period or a colon. Iterate over all ancestor
|
||||
// cache contexts. If one exists, omit the context token.
|
||||
else {
|
||||
|
|
|
@ -34,7 +34,32 @@ interface CalculatedCacheContextInterface {
|
|||
* @return string
|
||||
* The string representation of the cache context. When $parameter is NULL,
|
||||
* a value representing all possible parameters must be generated.
|
||||
*
|
||||
* @throws \LogicException
|
||||
* Thrown if the passed in parameter is invalid.
|
||||
*/
|
||||
public function getContext($parameter = NULL);
|
||||
|
||||
/**
|
||||
* Gets the cacheability metadata for the context based on the parameter value.
|
||||
*
|
||||
* There are three valid cases for the returned CacheableMetadata object:
|
||||
* - An empty object means this can be optimized away safely.
|
||||
* - A max-age of 0 means that this context can never be optimized away. It
|
||||
* will never bubble up and cache tags will not be used.
|
||||
* - Any non-zero max-age and cache tags will bubble up into the cache item
|
||||
* if this is optimized away to allow for invalidation if the context
|
||||
* value changes.
|
||||
*
|
||||
* @param string|null $parameter
|
||||
* The parameter, or NULL to indicate all possible parameter values.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheableMetadata
|
||||
* A cacheable metadata object.
|
||||
*
|
||||
* @throws \LogicException
|
||||
* Thrown if the passed in parameter is invalid.
|
||||
*/
|
||||
public function getCacheableMetadata($parameter = NULL);
|
||||
|
||||
}
|
||||
|
|
44
core/lib/Drupal/Core/Cache/Context/ContextCacheKeys.php
Normal file
44
core/lib/Drupal/Core/Cache/Context/ContextCacheKeys.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\ContextCacheKeys.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* A value object to store generated cache keys with its cacheability metadata.
|
||||
*/
|
||||
class ContextCacheKeys extends CacheableMetadata {
|
||||
|
||||
/**
|
||||
* The generated cache keys.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $keys;
|
||||
|
||||
/**
|
||||
* Constructs a ContextCacheKeys object.
|
||||
*
|
||||
* @param string[] $keys
|
||||
* The cache context keys.
|
||||
*/
|
||||
public function __construct(array $keys) {
|
||||
$this->keys = $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the generated cache keys.
|
||||
*
|
||||
* @return string[]
|
||||
* The cache keys.
|
||||
*/
|
||||
public function getKeys() {
|
||||
return $this->keys;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,8 +7,14 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the CookiesCacheContext service, for "per cookie" caching.
|
||||
*
|
||||
* Cache context ID: 'cookies' (to vary by all cookies).
|
||||
* Calculated cache context ID: 'cookies:%name', e.g. 'cookies:device_type' (to
|
||||
* vary by the 'device_type' cookie).
|
||||
*/
|
||||
class CookiesCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
|
@ -31,4 +37,11 @@ class CookiesCacheContext extends RequestStackCacheContextBase implements Calcul
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($cookie = NULL) {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,8 +7,14 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the HeadersCacheContext service, for "per header" caching.
|
||||
*
|
||||
* Cache context ID: 'headers' (to vary by all headers).
|
||||
* Calculated cache context ID: 'headers:%name', e.g. 'headers:X-Something' (to
|
||||
* vary by the 'X-Something' header).
|
||||
*/
|
||||
class HeadersCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
|
@ -31,4 +37,11 @@ class HeadersCacheContext extends RequestStackCacheContextBase implements Calcul
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($header = NULL) {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the IpCacheContext service, for "per IP address" caching.
|
||||
*
|
||||
* Cache context ID: 'ip'.
|
||||
*/
|
||||
class IpCacheContext extends RequestStackCacheContextBase {
|
||||
class IpCacheContext extends RequestStackCacheContextBase implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -26,4 +30,11 @@ class IpCacheContext extends RequestStackCacheContextBase {
|
|||
return $this->requestStack->getCurrentRequest()->getClientIp();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the IsSuperUserCacheContext service, for "super user or not" caching.
|
||||
*
|
||||
* Cache context ID: 'user.is_super_user'.
|
||||
*/
|
||||
class IsSuperUserCacheContext extends UserCacheContext {
|
||||
class IsSuperUserCacheContext extends UserCacheContextBase implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -26,4 +30,11 @@ class IsSuperUserCacheContext extends UserCacheContext {
|
|||
return ((int) $this->user->id()) === 1 ? '1' : '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
|
||||
/**
|
||||
|
@ -74,4 +75,11 @@ class LanguagesCacheContext implements CalculatedCacheContextInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($type = NULL) {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||
|
||||
/**
|
||||
* Defines the MenuActiveTrailsCacheContext service.
|
||||
*
|
||||
* This class is container-aware to avoid initializing the 'menu.active_trail'
|
||||
* This class is container-aware to avoid initializing the 'menu.active_trails'
|
||||
* service (and its dependencies) when it is not necessary.
|
||||
*/
|
||||
class MenuActiveTrailsCacheContext extends ContainerAware implements CalculatedCacheContextInterface {
|
||||
|
@ -28,9 +29,24 @@ class MenuActiveTrailsCacheContext extends ContainerAware implements CalculatedC
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext($menu_name = NULL) {
|
||||
if (!$menu_name) {
|
||||
throw new \LogicException('No menu name provided for menu.active_trails cache context.');
|
||||
}
|
||||
|
||||
$active_trail = $this->container->get('menu.active_trail')
|
||||
->getActiveTrailIds($menu_name);
|
||||
return 'menu_trail.' . $menu_name . '|' . implode('|', $active_trail);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($menu_name = NULL) {
|
||||
if (!$menu_name) {
|
||||
throw new \LogicException('No menu name provided for menu.active_trails cache context.');
|
||||
}
|
||||
$cacheable_metadata = new CacheableMetadata();
|
||||
return $cacheable_metadata->setCacheTags(["config:system.menu.$menu_name"]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,8 +7,14 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines a cache context for "per page in a pager" caching.
|
||||
*
|
||||
* Cache context ID: 'url.query_args.pagers' (to vary by all pagers).
|
||||
* Calculated cache context ID: 'url.query_args.pagers:%pager_id', e.g.
|
||||
* 'url.query_args.pagers:1' (to vary by the pager with ID 1).
|
||||
*/
|
||||
class PagersCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
|
@ -28,10 +34,17 @@ class PagersCacheContext extends RequestStackCacheContextBase implements Calcula
|
|||
// The value of the 'page' query argument contains the information that
|
||||
// controls *all* pagers.
|
||||
if ($pager_id === NULL) {
|
||||
return 'pager' . $this->requestStack->getCurrentRequest()->query->get('page', '');
|
||||
return $this->requestStack->getCurrentRequest()->query->get('page', '');
|
||||
}
|
||||
|
||||
return 'pager.' . $pager_id . '.' . pager_find_page($pager_id);
|
||||
return $pager_id . '.' . pager_find_page($pager_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($pager_id = NULL) {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the QueryArgsCacheContext service, for "per query args" caching.
|
||||
*
|
||||
* A "host" is defined as the combination of URI scheme, domain name and port.
|
||||
*
|
||||
* @see Symfony\Component\HttpFoundation::getSchemeAndHttpHost()
|
||||
* Cache context ID: 'url.query_args' (to vary by all query arguments).
|
||||
* Calculated cache context ID: 'url.query_args:%key', e.g.'url.query_args:foo'
|
||||
* (to vary by the 'foo' query argument).
|
||||
*/
|
||||
class QueryArgsCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
|
@ -35,4 +37,11 @@ class QueryArgsCacheContext extends RequestStackCacheContextBase implements Calc
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($query_arg = NULL) {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,8 +7,12 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the RequestFormatCacheContext service, for "per format" caching.
|
||||
*
|
||||
* Cache context ID: 'request_format'.
|
||||
*/
|
||||
class RequestFormatCacheContext extends RequestStackCacheContextBase {
|
||||
|
||||
|
@ -26,4 +30,11 @@ class RequestFormatCacheContext extends RequestStackCacheContextBase {
|
|||
return $this->requestStack->getCurrentRequest()->getRequestFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,8 +11,12 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||
|
||||
/**
|
||||
* Defines a base class for cache contexts depending only on the request stack.
|
||||
*
|
||||
* Subclasses need to implement either
|
||||
* \Drupal\Core\Cache\Context\CacheContextInterface or
|
||||
* \Drupal\Core\Cache\Context\CalculatedCacheContextInterface.
|
||||
*/
|
||||
abstract class RequestStackCacheContextBase implements CacheContextInterface {
|
||||
abstract class RequestStackCacheContextBase {
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
|
|
|
@ -7,10 +7,13 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
* Defines the RouteCacheContext service, for "per route" caching.
|
||||
*
|
||||
* Cache context ID: 'route'.
|
||||
*/
|
||||
class RouteCacheContext implements CacheContextInterface {
|
||||
|
||||
|
@ -45,4 +48,11 @@ class RouteCacheContext implements CacheContextInterface {
|
|||
return $this->routeMatch->getRouteName() . hash('sha256', serialize($this->routeMatch->getRawParameters()->all()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ namespace Drupal\Core\Cache\Context;
|
|||
|
||||
/**
|
||||
* Defines the RouteCacheContext service, for "per route name" caching.
|
||||
*
|
||||
* Cache context ID: 'route.name'.
|
||||
*/
|
||||
class RouteNameCacheContext extends RouteCacheContext {
|
||||
|
||||
|
|
31
core/lib/Drupal/Core/Cache/Context/SessionCacheContext.php
Normal file
31
core/lib/Drupal/Core/Cache/Context/SessionCacheContext.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\SessionCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the SessionCacheContext service, for "per session" caching.
|
||||
*
|
||||
* Cache context ID: 'session'.
|
||||
*/
|
||||
class SessionCacheContext extends RequestStackCacheContextBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Session');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->requestStack->getCurrentRequest()->getSession()->getId();
|
||||
}
|
||||
|
||||
}
|
|
@ -7,9 +7,13 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the SiteCacheContext service, for "per site" caching.
|
||||
*
|
||||
* Cache context ID: 'site'.
|
||||
*
|
||||
* A "site" is defined as the combination of URI scheme, domain name, port and
|
||||
* base path. It allows for varying between the *same* site being accessed via
|
||||
* different entry points. (Different sites in a multisite setup have separate
|
||||
|
@ -18,7 +22,7 @@ namespace Drupal\Core\Cache\Context;
|
|||
* @see \Symfony\Component\HttpFoundation\Request::getSchemeAndHttpHost()
|
||||
* @see \Symfony\Component\HttpFoundation\Request::getBaseUrl()
|
||||
*/
|
||||
class SiteCacheContext extends RequestStackCacheContextBase {
|
||||
class SiteCacheContext extends RequestStackCacheContextBase implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -35,4 +39,11 @@ class SiteCacheContext extends RequestStackCacheContextBase {
|
|||
return $request->getSchemeAndHttpHost() . $request->getBaseUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
|
||||
/**
|
||||
* Defines the ThemeCacheContext service, for "per theme" caching.
|
||||
*
|
||||
* Cache context ID: 'theme'.
|
||||
*/
|
||||
class ThemeCacheContext implements CacheContextInterface {
|
||||
|
||||
|
@ -46,4 +48,11 @@ class ThemeCacheContext implements CacheContextInterface {
|
|||
return $this->themeManager->getActiveTheme()->getName() ?: 'stark';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,9 +7,13 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the TimeZoneCacheContext service, for "per time zone" caching.
|
||||
*
|
||||
* Cache context ID: 'timezone'.
|
||||
*
|
||||
* @see \Drupal\Core\Session\AccountProxy::setAccount()
|
||||
*/
|
||||
class TimeZoneCacheContext implements CacheContextInterface {
|
||||
|
@ -30,4 +34,11 @@ class TimeZoneCacheContext implements CacheContextInterface {
|
|||
return date_default_timezone_get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the UrlCacheContext service, for "per page" caching.
|
||||
*
|
||||
* Cache context ID: 'url'.
|
||||
*/
|
||||
class UrlCacheContext extends RequestStackCacheContextBase {
|
||||
class UrlCacheContext extends RequestStackCacheContextBase implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -26,4 +30,11 @@ class UrlCacheContext extends RequestStackCacheContextBase {
|
|||
return $this->requestStack->getCurrentRequest()->getUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,22 +7,14 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the UserCacheContext service, for "per user" caching.
|
||||
*
|
||||
* Cache context ID: 'user'.
|
||||
*/
|
||||
class UserCacheContext implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* Constructs a new UserCacheContext service.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $user
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(AccountInterface $user) {
|
||||
$this->user = $user;
|
||||
}
|
||||
class UserCacheContext extends UserCacheContextBase implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -35,7 +27,14 @@ class UserCacheContext implements CacheContextInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return "u." . $this->user->id();
|
||||
return $this->user->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
38
core/lib/Drupal/Core/Cache/Context/UserCacheContextBase.php
Normal file
38
core/lib/Drupal/Core/Cache/Context/UserCacheContextBase.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\UserCacheContextBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Base class for user-based cache contexts.
|
||||
*
|
||||
* Subclasses need to implement either
|
||||
* \Drupal\Core\Cache\Context\CacheContextInterface or
|
||||
* \Drupal\Core\Cache\Context\CalculatedCacheContextInterface.
|
||||
*/
|
||||
abstract class UserCacheContextBase {
|
||||
|
||||
/**
|
||||
* The account object.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Constructs a new UserCacheContextBase class.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $user
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(AccountInterface $user) {
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,13 +7,19 @@
|
|||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
|
||||
/**
|
||||
* Defines the UserRolesCacheContext service, for "per role" caching.
|
||||
*
|
||||
* Only use this cache context when checking explicitly for certain roles. Use
|
||||
* user.permissions for anything that checks permissions.
|
||||
*
|
||||
* Cache context ID: 'user.roles' (to vary by all roles of the current user).
|
||||
* Calculated cache context ID: 'user.roles:%role', e.g. 'user.roles:anonymous'
|
||||
* (to vary by the presence/absence of a specific role).
|
||||
*/
|
||||
class UserRolesCacheContext extends UserCacheContext implements CalculatedCacheContextInterface{
|
||||
class UserRolesCacheContext extends UserCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -34,11 +40,18 @@ class UserRolesCacheContext extends UserCacheContext implements CalculatedCacheC
|
|||
return 'is-super-user';
|
||||
}
|
||||
if ($role === NULL) {
|
||||
return 'r.' . implode(',', $this->user->getRoles());
|
||||
return implode(',', $this->user->getRoles());
|
||||
}
|
||||
else {
|
||||
return 'r.' . $role . '.' . (in_array($role, $this->user->getRoles()) ? '0' : '1');
|
||||
return (in_array($role, $this->user->getRoles()) ? '0' : '1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($role = NULL) {
|
||||
return (new CacheableMetadata())->setCacheTags(['user:' . $this->user->id()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\RefinableCacheableDependencyInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Allows to add cacheability metadata to an object for the current runtime.
|
||||
*
|
||||
* This must be used when changing an object in a way that affects its
|
||||
* cacheability. For example, when changing the active translation of an entity
|
||||
* based on the current content language then a cache context for that must be
|
||||
* added.
|
||||
*/
|
||||
interface RefinableCacheableDependencyInterface extends CacheableDependencyInterface {
|
||||
|
||||
/**
|
||||
* Adds cache contexts.
|
||||
*
|
||||
* @param string[] $cache_contexts
|
||||
* The cache contexts to be added.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCacheContexts(array $cache_contexts);
|
||||
|
||||
/**
|
||||
* Adds cache tags.
|
||||
*
|
||||
* @param string[] $cache_tags
|
||||
* The cache tags to be added.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCacheTags(array $cache_tags);
|
||||
|
||||
/**
|
||||
* Merges the maximum age (in seconds) with the existing maximum age.
|
||||
*
|
||||
* The max age will be set to the given value if it is lower than the existing
|
||||
* value.
|
||||
*
|
||||
* @param int $max_age
|
||||
* The max age to associate.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if a non-integer value is supplied.
|
||||
*/
|
||||
public function mergeCacheMaxAge($max_age);
|
||||
|
||||
/**
|
||||
* Adds a dependency on an object: merges its cacheability metadata.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheableDependencyInterface|object $other_object
|
||||
* The dependency. If the object implements CacheableDependencyInterface,
|
||||
* then its cacheability metadata will be used. Otherwise, the passed in
|
||||
* object must be assumed to be uncacheable, so max-age 0 is set.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheableMetadata::createFromObject()
|
||||
*/
|
||||
public function addCacheableDependency($other_object);
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\RefinableCacheableDependencyTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Trait for \Drupal\Core\Cache\RefinableCacheableDependencyInterface.
|
||||
*/
|
||||
trait RefinableCacheableDependencyTrait {
|
||||
|
||||
/**
|
||||
* Cache contexts.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $cacheContexts = [];
|
||||
|
||||
/**
|
||||
* Cache tags.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $cacheTags = [];
|
||||
|
||||
/**
|
||||
* Cache max-age.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $cacheMaxAge = Cache::PERMANENT;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addCacheableDependency($other_object) {
|
||||
if ($other_object instanceof CacheableDependencyInterface) {
|
||||
$this->addCacheContexts($other_object->getCacheContexts());
|
||||
$this->addCacheTags($other_object->getCacheTags());
|
||||
$this->mergeCacheMaxAge($other_object->getCacheMaxAge());
|
||||
}
|
||||
else {
|
||||
// Not a cacheable dependency, this can not be cached.
|
||||
$this->maxAge = 0;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addCacheContexts(array $cache_contexts) {
|
||||
$this->cacheContexts = Cache::mergeContexts($this->cacheContexts, $cache_contexts);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addCacheTags(array $cache_tags) {
|
||||
$this->cacheTags = Cache::mergeTags($this->cacheTags, $cache_tags);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mergeCacheMaxAge($max_age) {
|
||||
$this->cacheMaxAge = Cache::mergeMaxAges($this->cacheMaxAge, $max_age);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -220,6 +220,9 @@ class DbDumpCommand extends Command {
|
|||
// Set primary key, unique keys, and indexes.
|
||||
$this->getTableIndexes($table, $definition);
|
||||
|
||||
// Set table collation.
|
||||
$this->getTableCollation($table, $definition);
|
||||
|
||||
return $definition;
|
||||
}
|
||||
|
||||
|
@ -235,7 +238,6 @@ class DbDumpCommand extends Command {
|
|||
// Note, this query doesn't support ordering, so that is worked around
|
||||
// below by keying the array on Seq_in_index.
|
||||
$query = $this->connection->query("SHOW INDEX FROM {" . $table . "}");
|
||||
$indexes = [];
|
||||
while (($row = $query->fetchAssoc()) !== FALSE) {
|
||||
$index_name = $row['Key_name'];
|
||||
$column = $row['Column_name'];
|
||||
|
@ -259,6 +261,22 @@ class DbDumpCommand extends Command {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table collation.
|
||||
*
|
||||
* @param string $table
|
||||
* The table to find indexes for.
|
||||
* @param array &$definition
|
||||
* The schema definition to modify.
|
||||
*/
|
||||
protected function getTableCollation($table, &$definition) {
|
||||
$query = $this->connection->query("SHOW TABLE STATUS LIKE '{" . $table . "}'");
|
||||
$data = $query->fetchAssoc();
|
||||
|
||||
// Set `mysql_character_set`. This will be ignored by other backends.
|
||||
$definition['mysql_character_set'] = str_replace('_general_ci', '', $data['Collation']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all data from a given table.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Command\GenerateProxyClassApplication.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Command;
|
||||
|
||||
use Drupal\Component\ProxyBuilder\ProxyBuilder;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* Provides a console command to generate proxy classes.
|
||||
*/
|
||||
class GenerateProxyClassApplication extends Application {
|
||||
|
||||
/**
|
||||
* The proxy builder.
|
||||
*
|
||||
* @var \Drupal\Component\ProxyBuilder\ProxyBuilder
|
||||
*/
|
||||
protected $proxyBuilder;
|
||||
|
||||
/**
|
||||
* Constructs a new GenerateProxyClassApplication instance.
|
||||
*
|
||||
* @param \Drupal\Component\ProxyBuilder\ProxyBuilder $proxy_builder
|
||||
* The proxy builder.
|
||||
*/
|
||||
public function __construct(ProxyBuilder $proxy_builder) {
|
||||
$this->proxyBuilder = $proxy_builder;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCommandName(InputInterface $input) {
|
||||
return 'generate-proxy-class';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultCommands() {
|
||||
// Even though this is a single command, keep the HelpCommand (--help).
|
||||
$default_commands = parent::getDefaultCommands();
|
||||
$default_commands[] = new GenerateProxyClassCommand($this->proxyBuilder);
|
||||
return $default_commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Overridden so the application doesn't expect the command name as the first
|
||||
* argument.
|
||||
*/
|
||||
public function getDefinition() {
|
||||
$definition = parent::getDefinition();
|
||||
// Clears the normal first argument (the command name).
|
||||
$definition->setArguments();
|
||||
return $definition;
|
||||
}
|
||||
|
||||
}
|
97
core/lib/Drupal/Core/Command/GenerateProxyClassCommand.php
Normal file
97
core/lib/Drupal/Core/Command/GenerateProxyClassCommand.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Command\GenerateProxyClassCommand.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Command;
|
||||
|
||||
use Drupal\Component\ProxyBuilder\ProxyBuilder;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Provides a console command to generate proxy classes.
|
||||
*/
|
||||
class GenerateProxyClassCommand extends Command {
|
||||
|
||||
/**
|
||||
* The proxy builder.
|
||||
*
|
||||
* @var \Drupal\Component\ProxyBuilder\ProxyBuilder
|
||||
*/
|
||||
protected $proxyBuilder;
|
||||
|
||||
/**
|
||||
* Constructs a new GenerateProxyClassCommand instance.
|
||||
*
|
||||
* @param \Drupal\Component\ProxyBuilder\ProxyBuilder $proxy_builder
|
||||
* The proxy builder.
|
||||
*/
|
||||
public function __construct(ProxyBuilder $proxy_builder) {
|
||||
parent::__construct();
|
||||
|
||||
$this->proxyBuilder = $proxy_builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this->setName('generate-proxy-class')
|
||||
->setDefinition([
|
||||
new InputArgument('class_name', InputArgument::REQUIRED, 'The class to be proxied'),
|
||||
new InputArgument('namespace_root_path', InputArgument::REQUIRED, 'The filepath to the root of the namespace.'),
|
||||
])
|
||||
->setDescription('Dumps a generated proxy class into its appropriate namespace.')
|
||||
->addUsage('\'Drupal\Core\Batch\BatchStorage\' "core/lib/Drupal/Core"')
|
||||
->addUsage('\'Drupal\block\BlockRepository\' "core/modules/block/src"')
|
||||
->addUsage('\'Drupal\mymodule\MyClass\' "modules/contrib/mymodule/src"');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$class_name = ltrim($input->getArgument('class_name'), '\\');
|
||||
$namespace_root = $input->getArgument('namespace_root_path');
|
||||
|
||||
$match = [];
|
||||
preg_match('/([a-zA-Z0-9_]+\\\\[a-zA-Z0-9_]+)\\\\(.+)/', $class_name, $match);
|
||||
|
||||
if ($match) {
|
||||
$root_namespace = $match[1];
|
||||
$rest_fqcn = $match[2];
|
||||
|
||||
$proxy_filename = $namespace_root . '/ProxyClass/' . str_replace('\\', '/', $rest_fqcn) . '.php';
|
||||
$proxy_class_name = $root_namespace . '\\ProxyClass\\' . $rest_fqcn;
|
||||
|
||||
$proxy_class_string = $this->proxyBuilder->build($class_name);
|
||||
|
||||
$file_string = <<<EOF
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \{{ proxy_class_name }}.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file was generated via php core/scripts/generate-proxy-class.php '$class_name' "$namespace_root".
|
||||
*/
|
||||
{{ proxy_class_string }}
|
||||
EOF;
|
||||
$file_string = str_replace(['{{ proxy_class_name }}', '{{ proxy_class_string }}'], [$proxy_class_name, $proxy_class_string], $file_string);
|
||||
|
||||
mkdir(dirname($proxy_filename), 0775, TRUE);
|
||||
file_put_contents($proxy_filename, $file_string);
|
||||
|
||||
$output->writeln(sprintf('Proxy of class %s written to %s', $class_name, $proxy_filename));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@ namespace Drupal\Core\Condition;
|
|||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Executable\ExecutableInterface;
|
||||
use Drupal\Core\Executable\ExecutableManagerInterface;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
|
@ -46,12 +47,12 @@ use Drupal\Core\Plugin\PluginFormInterface;
|
|||
*
|
||||
* @ingroup plugin_api
|
||||
*/
|
||||
interface ConditionInterface extends ExecutableInterface, PluginFormInterface, ConfigurablePluginInterface, PluginInspectionInterface {
|
||||
interface ConditionInterface extends ExecutableInterface, PluginFormInterface, ConfigurablePluginInterface, PluginInspectionInterface, CacheableDependencyInterface {
|
||||
|
||||
/**
|
||||
* Determines whether condition result will be negated.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* Whether the condition result will be negated.
|
||||
*/
|
||||
public function isNegated();
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\Core\Condition;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Executable\ExecutableManagerInterface;
|
||||
use Drupal\Core\Executable\ExecutablePluginBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
|
||||
use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
|
@ -28,8 +28,9 @@ use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
|||
* @see \Drupal\Core\Config\Config
|
||||
* @see \Drupal\Core\Theme\ThemeSettings
|
||||
*/
|
||||
abstract class ConfigBase implements CacheableDependencyInterface {
|
||||
abstract class ConfigBase implements RefinableCacheableDependencyInterface {
|
||||
use DependencySerializationTrait;
|
||||
use RefinableCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The name of the configuration object.
|
||||
|
@ -97,24 +98,17 @@ abstract class ConfigBase implements CacheableDependencyInterface {
|
|||
public static function validateName($name) {
|
||||
// The name must be namespaced by owner.
|
||||
if (strpos($name, '.') === FALSE) {
|
||||
throw new ConfigNameException(SafeMarkup::format('Missing namespace in Config object name @name.', array(
|
||||
'@name' => $name,
|
||||
)));
|
||||
throw new ConfigNameException("Missing namespace in Config object name $name.");
|
||||
}
|
||||
// The name must be shorter than Config::MAX_NAME_LENGTH characters.
|
||||
if (strlen($name) > self::MAX_NAME_LENGTH) {
|
||||
throw new ConfigNameException(SafeMarkup::format('Config object name @name exceeds maximum allowed length of @length characters.', array(
|
||||
'@name' => $name,
|
||||
'@length' => self::MAX_NAME_LENGTH,
|
||||
)));
|
||||
throw new ConfigNameException("Config object name $name exceeds maximum allowed length of " . static::MAX_NAME_LENGTH . " characters.");
|
||||
}
|
||||
|
||||
// The name must not contain any of the following characters:
|
||||
// : ? * < > " ' / \
|
||||
if (preg_match('/[:?*<>"\'\/\\\\]/', $name)) {
|
||||
throw new ConfigNameException(SafeMarkup::format('Invalid character in Config object name @name.', array(
|
||||
'@name' => $name,
|
||||
)));
|
||||
throw new ConfigNameException("Invalid character in Config object name $name.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +216,7 @@ abstract class ConfigBase implements CacheableDependencyInterface {
|
|||
protected function validateKeys(array $data) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (strpos($key, '.') !== FALSE) {
|
||||
throw new ConfigValueException(SafeMarkup::format('@key key contains a dot which is not supported.', array('@key' => $key)));
|
||||
throw new ConfigValueException("$key key contains a dot which is not supported.");
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$this->validateKeys($value);
|
||||
|
@ -269,21 +263,21 @@ abstract class ConfigBase implements CacheableDependencyInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return [];
|
||||
return $this->cacheContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return ['config:' . $this->name];
|
||||
return Cache::mergeTags(['config:' . $this->name], $this->cacheTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return Cache::PERMANENT;
|
||||
return $this->cacheMaxAge;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,11 +21,16 @@ final class ConfigEvents {
|
|||
* object is saved. The event listener method receives a
|
||||
* \Drupal\Core\Config\ConfigCrudEvent instance.
|
||||
*
|
||||
* See hook_update_N() documentation for safe configuration API usage and
|
||||
* restrictions as this event will be fired when configuration is saved by
|
||||
* hook_update_N().
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigCrudEvent
|
||||
* @see \Drupal\Core\Config\Config::save()
|
||||
* @see \Drupal\Core\Config\ConfigFactory::onConfigSave()
|
||||
* @see hook_update_N()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
@ -38,11 +43,16 @@ final class ConfigEvents {
|
|||
* object is deleted. The event listener method receives a
|
||||
* \Drupal\Core\Config\ConfigCrudEvent instance.
|
||||
*
|
||||
* See hook_update_N() documentation for safe configuration API usage and
|
||||
* restrictions as this event will be fired when configuration is deleted by
|
||||
* hook_update_N().
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigCrudEvent
|
||||
* @see \Drupal\Core\Config\Config::delete()
|
||||
* @see \Drupal\Core\Config\ConfigFactory::onConfigDelete()
|
||||
* @see hook_update_N()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
@ -55,10 +65,15 @@ final class ConfigEvents {
|
|||
* object's name is changed. The event listener method receives a
|
||||
* \Drupal\Core\Config\ConfigRenameEvent instance.
|
||||
*
|
||||
* See hook_update_N() documentation for safe configuration API usage and
|
||||
* restrictions as this event will be fired when configuration is renamed by
|
||||
* hook_update_N().
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigRenameEvent
|
||||
* @see \Drupal\Core\Config\ConfigFactoryInterface::rename().
|
||||
* @see \Drupal\Core\Config\ConfigFactoryInterface::rename()
|
||||
* @see hook_update_N()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
|
|
@ -126,6 +126,9 @@ class ConfigFactory implements ConfigFactoryInterface, EventSubscriberInterface
|
|||
$this->cache[$cache_key]->setSettingsOverride($GLOBALS['config'][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->propagateConfigOverrideCacheability($cache_key, $name);
|
||||
|
||||
return $this->cache[$cache_key];
|
||||
}
|
||||
}
|
||||
|
@ -183,6 +186,9 @@ class ConfigFactory implements ConfigFactoryInterface, EventSubscriberInterface
|
|||
$this->cache[$cache_key]->setSettingsOverride($GLOBALS['config'][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->propagateConfigOverrideCacheability($cache_key, $name);
|
||||
|
||||
$list[$name] = $this->cache[$cache_key];
|
||||
}
|
||||
}
|
||||
|
@ -209,6 +215,20 @@ class ConfigFactory implements ConfigFactoryInterface, EventSubscriberInterface
|
|||
return $overrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates cacheability of config overrides to cached config objects.
|
||||
*
|
||||
* @param string $cache_key
|
||||
* The key of the cached config object to update.
|
||||
* @param string $name
|
||||
* The name of the configuration object to construct.
|
||||
*/
|
||||
protected function propagateConfigOverrideCacheability($cache_key, $name) {
|
||||
foreach ($this->configFactoryOverrides as $override) {
|
||||
$this->cache[$cache_key]->addCacheableDependency($override->getCacheableMetadata($name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -74,7 +74,7 @@ abstract class ConfigFactoryOverrideBase implements EventSubscriberInterface {
|
|||
}
|
||||
elseif ($changed) {
|
||||
// Otherwise set the filtered override values back.
|
||||
$override->setData($override_data)->save();
|
||||
$override->setData($override_data)->save(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,4 +58,15 @@ interface ConfigFactoryOverrideInterface {
|
|||
*/
|
||||
public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION);
|
||||
|
||||
/**
|
||||
* Gets the cacheability metadata associated with the config factory override.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the configuration override to get metadata for.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheableMetadata
|
||||
* A cacheable metadata object.
|
||||
*/
|
||||
public function getCacheableMetadata($name);
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ use Drupal\Core\Config\Importer\MissingContentEvent;
|
|||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ModuleInstallerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\Entity\ImportableEntityStorageInterface;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
|
@ -763,7 +762,7 @@ class ConfigImporter {
|
|||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->logError($this->t('Unexpected error during import with operation @op for @name: @message', array('@op' => $op, '@name' => $name, '@message' => $e->getMessage())));
|
||||
$this->logError($this->t('Unexpected error during import with operation @op for @name: !message', array('@op' => $op, '@name' => $name, '!message' => $e->getMessage())));
|
||||
// Error for that operation was logged, mark it as processed so that
|
||||
// the import can continue.
|
||||
$this->setProcessedConfiguration($collection, $op, $name);
|
||||
|
@ -972,7 +971,7 @@ class ConfigImporter {
|
|||
// Call to the configuration entity's storage to handle the configuration
|
||||
// change.
|
||||
if (!($entity_storage instanceof ImportableEntityStorageInterface)) {
|
||||
throw new EntityStorageException(SafeMarkup::format('The entity storage "@storage" for the "@entity_type" entity type does not support imports', array('@storage' => get_class($entity_storage), '@entity_type' => $entity_type)));
|
||||
throw new EntityStorageException(sprintf('The entity storage "%s" for the "%s" entity type does not support imports', get_class($entity_storage), $entity_type));
|
||||
}
|
||||
$entity_storage->$method($name, $new_config, $old_config);
|
||||
$this->setProcessedConfiguration($collection, $op, $name);
|
||||
|
@ -1018,7 +1017,7 @@ class ConfigImporter {
|
|||
// Call to the configuration entity's storage to handle the configuration
|
||||
// change.
|
||||
if (!($entity_storage instanceof ImportableEntityStorageInterface)) {
|
||||
throw new EntityStorageException(SafeMarkup::format('The entity storage "@storage" for the "@entity_type" entity type does not support imports', array('@storage' => get_class($entity_storage), '@entity_type' => $entity_type_id)));
|
||||
throw new EntityStorageException(sprintf("The entity storage '%s' for the '%s' entity type does not support imports", get_class($entity_storage), $entity_type_id));
|
||||
}
|
||||
$entity_storage->importRename($names['old_name'], $new_config, $old_config);
|
||||
$this->setProcessedConfiguration($collection, 'rename', $rename_name);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Config\Entity;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Config\ConfigException;
|
||||
use Drupal\Core\Config\Schema\SchemaIncompleteException;
|
||||
|
@ -192,8 +191,6 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function disable() {
|
||||
// An entity was disabled, invalidate its own cache tag.
|
||||
Cache::invalidateTags($this->getCacheTags());
|
||||
return $this->setStatus(FALSE);
|
||||
}
|
||||
|
||||
|
@ -280,7 +277,7 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
$config_name = $entity_type->getConfigPrefix() . '.' . $this->id();
|
||||
$definition = $this->getTypedConfig()->getDefinition($config_name);
|
||||
if (!isset($definition['mapping'])) {
|
||||
throw new SchemaIncompleteException(SafeMarkup::format('Incomplete or missing schema for @config_name', array('@config_name' => $config_name)));
|
||||
throw new SchemaIncompleteException("Incomplete or missing schema for $config_name");
|
||||
}
|
||||
$properties_to_export = array_combine(array_keys($definition['mapping']), array_keys($definition['mapping']));
|
||||
}
|
||||
|
@ -333,7 +330,7 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
->execute();
|
||||
$matched_entity = reset($matching_entities);
|
||||
if (!empty($matched_entity) && ($matched_entity != $this->id()) && $matched_entity != $this->getOriginalId()) {
|
||||
throw new ConfigDuplicateUUIDException(SafeMarkup::format('Attempt to save a configuration entity %id with UUID %uuid when this UUID is already used for %matched', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%matched' => $matched_entity)));
|
||||
throw new ConfigDuplicateUUIDException("Attempt to save a configuration entity '{$this->id()}' with UUID '{$this->uuid()}' when this UUID is already used for '$matched_entity'");
|
||||
}
|
||||
|
||||
// If this entity is not new, load the original entity for comparison.
|
||||
|
@ -341,7 +338,7 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
$original = $storage->loadUnchanged($this->getOriginalId());
|
||||
// Ensure that the UUID cannot be changed for an existing entity.
|
||||
if ($original && ($original->uuid() != $this->uuid())) {
|
||||
throw new ConfigDuplicateUUIDException(SafeMarkup::format('Attempt to save a configuration entity %id with UUID %uuid when this entity already exists with UUID %original_uuid', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%original_uuid' => $original->uuid())));
|
||||
throw new ConfigDuplicateUUIDException("Attempt to save a configuration entity '{$this->id()}' with UUID '{$this->uuid()}' when this entity already exists with UUID '{$original->uuid()}'");
|
||||
}
|
||||
}
|
||||
if (!$this->isSyncing() && !$this->trustedData) {
|
||||
|
@ -409,7 +406,7 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
public function getCacheTagsToInvalidate() {
|
||||
// Use cache tags that match the underlying config object's name.
|
||||
// @see \Drupal\Core\Config\ConfigBase::getCacheTags()
|
||||
return ['config:' . $this->getConfigDependencyName()];
|
||||
|
|
|
@ -7,14 +7,13 @@
|
|||
|
||||
namespace Drupal\Core\Config\Entity;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\ConfigImporterException;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityMalformedException;
|
||||
use Drupal\Core\Entity\EntityStorageBase;
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Component\Uuid\UuidInterface;
|
||||
|
@ -184,11 +183,41 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
|
|||
}
|
||||
|
||||
// Load all of the configuration entities.
|
||||
$records = array();
|
||||
/** @var \Drupal\Core\Config\Config[] $configs */
|
||||
$configs = [];
|
||||
$records = [];
|
||||
foreach ($this->configFactory->loadMultiple($names) as $config) {
|
||||
$records[$config->get($this->idKey)] = $this->overrideFree ? $config->getOriginal(NULL, FALSE) : $config->get();
|
||||
$id = $config->get($this->idKey);
|
||||
$records[$id] = $this->overrideFree ? $config->getOriginal(NULL, FALSE) : $config->get();
|
||||
$configs[$id] = $config;
|
||||
}
|
||||
return $this->mapFromStorageRecords($records);
|
||||
$entities = $this->mapFromStorageRecords($records, $configs);
|
||||
|
||||
// Config entities wrap config objects, and therefore they need to inherit
|
||||
// the cacheability metadata of config objects (to ensure e.g. additional
|
||||
// cacheability metadata added by config overrides is not lost).
|
||||
foreach ($entities as $id => $entity) {
|
||||
// But rather than simply inheriting all cacheability metadata of config
|
||||
// objects, we need to make sure the self-referring cache tag that is
|
||||
// present on Config objects is not added to the Config entity. It must be
|
||||
// removed for 3 reasons:
|
||||
// 1. When renaming/duplicating a Config entity, the cache tag of the
|
||||
// original config object would remain present, which would be wrong.
|
||||
// 2. Some Config entities choose to not use the cache tag that the under-
|
||||
// lying Config object provides by default (For performance and
|
||||
// cacheability reasons it may not make sense to have a unique cache
|
||||
// tag for every Config entity. The DateFormat Config entity specifies
|
||||
// the 'rendered' cache tag for example, because A) date formats are
|
||||
// changed extremely rarely, so invalidating all render cache items is
|
||||
// fine, B) it means fewer cache tags per page.).
|
||||
// 3. Fewer cache tags is better for performance.
|
||||
$self_referring_cache_tag = ['config:' . $configs[$id]->getName()];
|
||||
$config_cacheability = CacheableMetadata::createFromObject($configs[$id]);
|
||||
$config_cacheability->setCacheTags(array_diff($config_cacheability->getCacheTags(), $self_referring_cache_tag));
|
||||
$entity->addCacheableDependency($config_cacheability);
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -229,10 +258,7 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
|
|||
// @todo Consider moving this to a protected method on the parent class, and
|
||||
// abstracting it for all entity types.
|
||||
if (strlen($entity->get($this->idKey)) > self::MAX_ID_LENGTH) {
|
||||
throw new ConfigEntityIdLengthException(SafeMarkup::format('Configuration entity ID @id exceeds maximum allowed length of @length characters.', array(
|
||||
'@id' => $entity->get($this->idKey),
|
||||
'@length' => self::MAX_ID_LENGTH,
|
||||
)));
|
||||
throw new ConfigEntityIdLengthException("Configuration entity ID {$entity->get($this->idKey)} exceeds maximum allowed length of " . self::MAX_ID_LENGTH . " characters.");
|
||||
}
|
||||
|
||||
return parent::save($entity);
|
||||
|
@ -374,7 +400,7 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
|
|||
$id = static::getIDFromConfigName($name, $this->entityType->getConfigPrefix());
|
||||
$entity = $this->load($id);
|
||||
if (!$entity) {
|
||||
throw new ConfigImporterException(SafeMarkup::format('Attempt to update non-existing entity "@id".', array('@id' => $id)));
|
||||
throw new ConfigImporterException("Attempt to update non-existing entity '$id'.");
|
||||
}
|
||||
$entity->setSyncing(TRUE);
|
||||
$entity = $this->updateFromStorageRecord($entity, $new_config->get());
|
||||
|
|
|
@ -10,7 +10,6 @@ namespace Drupal\Core\Config\Entity;
|
|||
use Drupal\Core\Config\Entity\Exception\ConfigEntityStorageClassException;
|
||||
use Drupal\Core\Entity\EntityType;
|
||||
use Drupal\Core\Config\ConfigPrefixLengthException;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Provides an implementation of a configuration entity type and its metadata.
|
||||
|
@ -93,10 +92,7 @@ class ConfigEntityType extends EntityType implements ConfigEntityTypeInterface {
|
|||
}
|
||||
|
||||
if (strlen($config_prefix) > static::PREFIX_LENGTH) {
|
||||
throw new ConfigPrefixLengthException(SafeMarkup::format('The configuration file name prefix @config_prefix exceeds the maximum character limit of @max_char.', array(
|
||||
'@config_prefix' => $config_prefix,
|
||||
'@max_char' => static::PREFIX_LENGTH,
|
||||
)));
|
||||
throw new ConfigPrefixLengthException("The configuration file name prefix $config_prefix exceeds the maximum character limit of " . static::PREFIX_LENGTH);
|
||||
}
|
||||
return $config_prefix;
|
||||
}
|
||||
|
@ -158,7 +154,7 @@ class ConfigEntityType extends EntityType implements ConfigEntityTypeInterface {
|
|||
*/
|
||||
protected function checkStorageClass($class) {
|
||||
if (!is_a($class, 'Drupal\Core\Config\Entity\ConfigEntityStorage', TRUE)) {
|
||||
throw new ConfigEntityStorageClassException(SafeMarkup::format('@class is not \Drupal\Core\Config\Entity\ConfigEntityStorage or it does not extend it', ['@class' => $class]));
|
||||
throw new ConfigEntityStorageClassException("$class is not \\Drupal\\Core\\Config\\Entity\\ConfigEntityStorage or it does not extend it");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ namespace Drupal\Core\Config;
|
|||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Defines the file storage.
|
||||
|
@ -101,10 +100,7 @@ class FileStorage implements StorageInterface {
|
|||
$data = $this->decode($data);
|
||||
}
|
||||
catch (InvalidDataTypeException $e) {
|
||||
throw new UnsupportedDataTypeConfigException(SafeMarkup::format('Invalid data type in config @name: !message', array(
|
||||
'@name' => $name,
|
||||
'!message' => $e->getMessage(),
|
||||
)));
|
||||
throw new UnsupportedDataTypeConfigException("Invalid data type in config $name: {$e->getMessage()}");
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
@ -130,10 +126,7 @@ class FileStorage implements StorageInterface {
|
|||
$data = $this->encode($data);
|
||||
}
|
||||
catch (InvalidDataTypeException $e) {
|
||||
throw new StorageException(SafeMarkup::format('Invalid data type in config @name: !message', array(
|
||||
'@name' => $name,
|
||||
'!message' => $e->getMessage(),
|
||||
)));
|
||||
throw new StorageException("Invalid data type in config $name: {$e->getMessage()}");
|
||||
}
|
||||
|
||||
$target = $this->getFilePath($name);
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Defines the immutable configuration object.
|
||||
*
|
||||
|
@ -31,21 +29,21 @@ class ImmutableConfig extends Config {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
throw new ImmutableConfigException(SafeMarkup::format('Can not set values on immutable configuration !name:!key. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object', ['!name' => $this->getName(), '!key' => $key]));
|
||||
throw new ImmutableConfigException("Can not set values on immutable configuration {$this->getName()}:$key. Use \\Drupal\\Core\\Config\\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clear($key) {
|
||||
throw new ImmutableConfigException(SafeMarkup::format('Can not clear !key key in immutable configuration !name. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object', ['!name' => $this->getName(), '!key' => $key]));
|
||||
throw new ImmutableConfigException("Can not clear $key key in immutable configuration {$this->getName()}. Use \\Drupal\\Core\\Config\\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($has_trusted_data = FALSE) {
|
||||
throw new ImmutableConfigException(SafeMarkup::format('Can not save immutable configuration !name. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object', ['!name' => $this->getName()]));
|
||||
throw new ImmutableConfigException("Can not save immutable configuration {$this->getName()}. Use \\Drupal\\Core\\Config\\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,7 +53,7 @@ class ImmutableConfig extends Config {
|
|||
* The configuration object.
|
||||
*/
|
||||
public function delete() {
|
||||
throw new ImmutableConfigException(SafeMarkup::format('Can not delete immutable configuration !name. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object', ['!name' => $this->getName()]));
|
||||
throw new ImmutableConfigException("Can not delete immutable configuration {$this->getName()}. Use \\Drupal\\Core\\Config\\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -91,9 +91,7 @@ class InstallStorage extends FileStorage {
|
|||
}
|
||||
// If any code in the early installer requests a configuration object that
|
||||
// does not exist anywhere as default config, then that must be mistake.
|
||||
throw new StorageException(format_string('Missing configuration file: @name', array(
|
||||
'@name' => $name,
|
||||
)));
|
||||
throw new StorageException("Missing configuration file: $name");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Config\Schema;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\TypedData\TypedData;
|
||||
|
||||
|
@ -94,7 +93,7 @@ abstract class ArrayElement extends TypedData implements \IteratorAggregate, Typ
|
|||
return $element;
|
||||
}
|
||||
else {
|
||||
throw new \InvalidArgumentException(SafeMarkup::format("The configuration property @key doesn't exist.", array('@key' => $name)));
|
||||
throw new \InvalidArgumentException("The configuration property $name doesn't exist.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ interface TypedConfigInterface extends TraversableTypedDataInterface {
|
|||
/**
|
||||
* Determines whether the data structure is empty.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* TRUE if the data structure is empty, FALSE otherwise.
|
||||
*/
|
||||
public function isEmpty();
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\Schema\Ignore;
|
||||
use Drupal\Core\TypedData\PrimitiveInterface;
|
||||
use Drupal\Core\TypedData\Type\FloatInterface;
|
||||
|
@ -163,10 +162,7 @@ abstract class StorableConfigBase extends ConfigBase {
|
|||
}
|
||||
}
|
||||
elseif ($value !== NULL && !is_scalar($value)) {
|
||||
throw new UnsupportedDataTypeConfigException(SafeMarkup::format('Invalid data type for config element @name:@key', array(
|
||||
'@name' => $this->getName(),
|
||||
'@key' => $key,
|
||||
)));
|
||||
throw new UnsupportedDataTypeConfigException("Invalid data type for config element {$this->getName()}:$key");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,10 +209,7 @@ abstract class StorableConfigBase extends ConfigBase {
|
|||
else {
|
||||
// Throw exception on any non-scalar or non-array value.
|
||||
if (!is_array($value)) {
|
||||
throw new UnsupportedDataTypeConfigException(SafeMarkup::format('Invalid data type for config element @name:@key', array(
|
||||
'@name' => $this->getName(),
|
||||
'@key' => $key,
|
||||
)));
|
||||
throw new UnsupportedDataTypeConfigException("Invalid data type for config element {$this->getName()}:$key");
|
||||
}
|
||||
// Recurse into any nested keys.
|
||||
foreach ($value as $nested_value_key => $nested_value) {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Cache\MemoryBackend;
|
||||
use Drupal\Core\Config\Entity\ConfigDependencyManager;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
|
@ -196,7 +195,7 @@ class StorageComparer implements StorageComparerInterface {
|
|||
// ensure the array is keyed from 0.
|
||||
$this->changelist[$collection][$op] = array_values(array_intersect($sort_order, $this->changelist[$collection][$op]));
|
||||
if ($count != count($this->changelist[$collection][$op])) {
|
||||
throw new \InvalidArgumentException(SafeMarkup::format('Sorting the @op changelist should not change its length.', array('@op' => $op)));
|
||||
throw new \InvalidArgumentException("Sorting the $op changelist should not change its length.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ interface StorageInterface {
|
|||
* (optional) The prefix to search for. If omitted, all configuration
|
||||
* objects that exist will be deleted.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
public function deleteAll($prefix = '');
|
||||
|
|
|
@ -88,14 +88,14 @@ class ConfigSchemaChecker implements EventSubscriberInterface {
|
|||
$this->checked[$name . ':' . $checksum] = TRUE;
|
||||
$errors = $this->checkConfigSchema($this->typedManager, $name, $data);
|
||||
if ($errors === FALSE) {
|
||||
throw new SchemaIncompleteException(SafeMarkup::format('No schema for @config_name', array('@config_name' => $name)));
|
||||
throw new SchemaIncompleteException("No schema for $name");
|
||||
}
|
||||
elseif (is_array($errors)) {
|
||||
$text_errors = [];
|
||||
foreach ($errors as $key => $error) {
|
||||
$text_errors[] = SafeMarkup::format('@key @error', array('@key' => $key, '@error' => $error));
|
||||
}
|
||||
throw new SchemaIncompleteException(SafeMarkup::format('Schema errors for @config_name with the following errors: @errors', array('@config_name' => $name, '@errors' => implode(', ', $text_errors))));
|
||||
throw new SchemaIncompleteException("Schema errors for $name with the following errors: " . implode(', ', $text_errors));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Config\Schema\ArrayElement;
|
||||
use Drupal\Core\Config\Schema\ConfigSchemaAlterException;
|
||||
|
@ -17,7 +16,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
|
|||
use Drupal\Core\TypedData\TypedDataManager;
|
||||
|
||||
/**
|
||||
* Manages config type plugins.
|
||||
* Manages config schema type plugins.
|
||||
*/
|
||||
class TypedConfigManager extends TypedDataManager implements TypedConfigManagerInterface {
|
||||
|
||||
|
@ -324,18 +323,18 @@ class TypedConfigManager extends TypedDataManager implements TypedConfigManagerI
|
|||
parent::alterDefinitions($definitions);
|
||||
$altered_schema = array_keys($definitions);
|
||||
if ($discovered_schema != $altered_schema) {
|
||||
$added_keys = array_diff($altered_schema, $discovered_schema);
|
||||
$removed_keys = array_diff($discovered_schema, $altered_schema);
|
||||
$added_keys = implode(',', array_diff($altered_schema, $discovered_schema));
|
||||
$removed_keys = implode(',', array_diff($discovered_schema, $altered_schema));
|
||||
if (!empty($added_keys) && !empty($removed_keys)) {
|
||||
$message = 'Invoking hook_config_schema_info_alter() has added (@added) and removed (@removed) schema definitions';
|
||||
$message = "Invoking hook_config_schema_info_alter() has added ($added_keys) and removed ($removed_keys) schema definitions";
|
||||
}
|
||||
elseif (!empty($added_keys)) {
|
||||
$message = 'Invoking hook_config_schema_info_alter() has added (@added) schema definitions';
|
||||
$message = "Invoking hook_config_schema_info_alter() has added ($added_keys) schema definitions";
|
||||
}
|
||||
else {
|
||||
$message = 'Invoking hook_config_schema_info_alter() has removed (@removed) schema definitions';
|
||||
$message = "Invoking hook_config_schema_info_alter() has removed ($removed_keys) schema definitions";
|
||||
}
|
||||
throw new ConfigSchemaAlterException(SafeMarkup::format($message, ['@added' => implode(',', $added_keys), '@removed' => implode(',', $removed_keys)]));
|
||||
throw new ConfigSchemaAlterException($message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,12 @@ use Drupal\Component\Plugin\PluginManagerInterface;
|
|||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for typed configuration manager.
|
||||
* Defines an interface for managing config schema type plugins.
|
||||
*
|
||||
* @package Drupal\Core\Config
|
||||
* @see \Drupal\Core\Config\TypedConfigManager
|
||||
* @see \Drupal\Core\Config\Schema\ConfigSchemaDiscovery
|
||||
* @see hook_config_schema_info_alter()
|
||||
* @see https://www.drupal.org/node/1905070
|
||||
*/
|
||||
Interface TypedConfigManagerInterface extends PluginManagerInterface, CachedDiscoveryInterface {
|
||||
|
||||
|
|
|
@ -37,10 +37,6 @@ class ContentNegotiation {
|
|||
return $request->query->get('_format');
|
||||
}
|
||||
|
||||
if ($request->isXmlHttpRequest()) {
|
||||
return 'ajax';
|
||||
}
|
||||
|
||||
// Do HTML last so that it always wins.
|
||||
return 'html';
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ namespace Drupal\Core;
|
|||
use Drupal\Core\Cache\Context\CacheContextsPass;
|
||||
use Drupal\Core\Cache\ListCacheBinsPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\GuzzleMiddlewarePass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers;
|
||||
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteFilters;
|
||||
use Drupal\Core\DependencyInjection\Compiler\DependencySerializationTraitPass;
|
||||
|
@ -60,6 +63,8 @@ class CoreServiceProvider implements ServiceProviderInterface {
|
|||
// list-building passes are operating on the post-alter services list.
|
||||
$container->addCompilerPass(new ModifyServiceDefinitionsPass());
|
||||
|
||||
$container->addCompilerPass(new ProxyServicesPass());
|
||||
|
||||
$container->addCompilerPass(new BackendCompilerPass());
|
||||
|
||||
$container->addCompilerPass(new StackedKernelPass());
|
||||
|
@ -71,6 +76,7 @@ class CoreServiceProvider implements ServiceProviderInterface {
|
|||
// Collect tagged handler services as method calls on consumer services.
|
||||
$container->addCompilerPass(new TaggedHandlersPass());
|
||||
$container->addCompilerPass(new RegisterStreamWrappersPass());
|
||||
$container->addCompilerPass(new GuzzleMiddlewarePass());
|
||||
|
||||
// Add a compiler pass for registering event subscribers.
|
||||
$container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
|
@ -85,6 +91,7 @@ class CoreServiceProvider implements ServiceProviderInterface {
|
|||
// Add the compiler pass that will process the tagged services.
|
||||
$container->addCompilerPass(new ListCacheBinsPass());
|
||||
$container->addCompilerPass(new CacheContextsPass());
|
||||
$container->addCompilerPass(new ContextProvidersPass());
|
||||
|
||||
// Register plugin managers.
|
||||
$container->addCompilerPass(new PluginManagerPass());
|
||||
|
@ -127,10 +134,10 @@ class CoreServiceProvider implements ServiceProviderInterface {
|
|||
if (!drupal_valid_test_ua()) {
|
||||
return;
|
||||
}
|
||||
// Add the HTTP request subscriber to Guzzle.
|
||||
// Add the HTTP request middleware to Guzzle.
|
||||
$container
|
||||
->register('test.http_client.request_subscriber', 'Drupal\Core\Test\EventSubscriber\HttpRequestSubscriber')
|
||||
->addTag('http_client_subscriber');
|
||||
->register('test.http_client.middleware', 'Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware')
|
||||
->addTag('http_client_middleware');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ namespace Drupal\Core;
|
|||
|
||||
/**
|
||||
* An interface for running cron tasks.
|
||||
*
|
||||
* @see https://www.drupal.org/cron
|
||||
*/
|
||||
interface CronInterface {
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ abstract class Connection {
|
|||
*
|
||||
* We need this information for later auditing and logging.
|
||||
*
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
protected $target = NULL;
|
||||
|
||||
|
@ -35,14 +35,14 @@ abstract class Connection {
|
|||
* connection can be a single server or a cluster of primary and replicas
|
||||
* (use target to pick between primary and replica).
|
||||
*
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
protected $key = NULL;
|
||||
|
||||
/**
|
||||
* The current database logging object for this connection.
|
||||
*
|
||||
* @var Log
|
||||
* @var \Drupal\Core\Database\Log|null
|
||||
*/
|
||||
protected $logger = NULL;
|
||||
|
||||
|
@ -111,7 +111,9 @@ abstract class Connection {
|
|||
/**
|
||||
* The schema object for this connection.
|
||||
*
|
||||
* @var object
|
||||
* Set to NULL when the schema is destroyed.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Schema|null
|
||||
*/
|
||||
protected $schema = NULL;
|
||||
|
||||
|
@ -138,6 +140,14 @@ abstract class Connection {
|
|||
|
||||
/**
|
||||
* Constructs a Connection object.
|
||||
*
|
||||
* @param \PDO $connection
|
||||
* An object of the PDO class representing a database connection.
|
||||
* @param array $connection_options
|
||||
* An array of options for the connection. May include the following:
|
||||
* - prefix
|
||||
* - namespace
|
||||
* - Other driver-specific options.
|
||||
*/
|
||||
public function __construct(\PDO $connection, array $connection_options) {
|
||||
// Initialize and prepare the connection prefix.
|
||||
|
@ -221,7 +231,7 @@ abstract class Connection {
|
|||
* that behavior and simply return NULL on failure, set this option to
|
||||
* FALSE.
|
||||
*
|
||||
* @return
|
||||
* @return array
|
||||
* An array of default query options.
|
||||
*/
|
||||
protected function defaultOptions() {
|
||||
|
@ -241,7 +251,7 @@ abstract class Connection {
|
|||
* is for requesting the connection information of this specific
|
||||
* open connection object.
|
||||
*
|
||||
* @return
|
||||
* @return array
|
||||
* An array of the connection information. The exact list of
|
||||
* properties is driver-dependent.
|
||||
*/
|
||||
|
@ -252,9 +262,9 @@ abstract class Connection {
|
|||
/**
|
||||
* Set the list of prefixes used by this database connection.
|
||||
*
|
||||
* @param $prefix
|
||||
* The prefixes, in any of the multiple forms documented in
|
||||
* default.settings.php.
|
||||
* @param array|string $prefix
|
||||
* Either a single prefix, or an array of prefixes, in any of the multiple
|
||||
* forms documented in default.settings.php.
|
||||
*/
|
||||
protected function setPrefix($prefix) {
|
||||
if (is_array($prefix)) {
|
||||
|
@ -289,10 +299,10 @@ abstract class Connection {
|
|||
* tables, allowing Drupal to coexist with other systems in the same database
|
||||
* and/or schema if necessary.
|
||||
*
|
||||
* @param $sql
|
||||
* @param string $sql
|
||||
* A string containing a partial or entire SQL query.
|
||||
*
|
||||
* @return
|
||||
* @return string
|
||||
* The properly-prefixed string.
|
||||
*/
|
||||
public function prefixTables($sql) {
|
||||
|
@ -304,6 +314,9 @@ abstract class Connection {
|
|||
*
|
||||
* This function is for when you want to know the prefix of a table. This
|
||||
* is not used in prefixTables due to performance reasons.
|
||||
*
|
||||
* @param string $table
|
||||
* (optional) The table to find the prefix for.
|
||||
*/
|
||||
public function tablePrefix($table = 'default') {
|
||||
if (isset($this->prefixes[$table])) {
|
||||
|
@ -355,9 +368,8 @@ abstract class Connection {
|
|||
* signature. We therefore also ensure that this function is only ever
|
||||
* called once.
|
||||
*
|
||||
* @param $target
|
||||
* The target this connection is for. Set to NULL (default) to disable
|
||||
* logging entirely.
|
||||
* @param string $target
|
||||
* (optional) The target this connection is for.
|
||||
*/
|
||||
public function setTarget($target = NULL) {
|
||||
if (!isset($this->target)) {
|
||||
|
@ -368,8 +380,8 @@ abstract class Connection {
|
|||
/**
|
||||
* Returns the target this connection is associated with.
|
||||
*
|
||||
* @return
|
||||
* The target string of this connection.
|
||||
* @return string|null
|
||||
* The target string of this connection, or NULL if no target is set.
|
||||
*/
|
||||
public function getTarget() {
|
||||
return $this->target;
|
||||
|
@ -378,7 +390,7 @@ abstract class Connection {
|
|||
/**
|
||||
* Tells this connection object what its key is.
|
||||
*
|
||||
* @param $target
|
||||
* @param string $key
|
||||
* The key this connection is for.
|
||||
*/
|
||||
public function setKey($key) {
|
||||
|
@ -390,8 +402,8 @@ abstract class Connection {
|
|||
/**
|
||||
* Returns the key this connection is associated with.
|
||||
*
|
||||
* @return
|
||||
* The key of this connection.
|
||||
* @return string|null
|
||||
* The key of this connection, or NULL if no key is set.
|
||||
*/
|
||||
public function getKey() {
|
||||
return $this->key;
|
||||
|
@ -400,7 +412,7 @@ abstract class Connection {
|
|||
/**
|
||||
* Associates a logging object with this connection.
|
||||
*
|
||||
* @param $logger
|
||||
* @param \Drupal\Core\Database\Log $logger
|
||||
* The logging object we want to use.
|
||||
*/
|
||||
public function setLogger(Log $logger) {
|
||||
|
@ -410,7 +422,7 @@ abstract class Connection {
|
|||
/**
|
||||
* Gets the current logging object for this connection.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Log
|
||||
* @return \Drupal\Core\Database\Log|null
|
||||
* The current logging object for this connection. If there isn't one,
|
||||
* NULL is returned.
|
||||
*/
|
||||
|
@ -424,12 +436,12 @@ abstract class Connection {
|
|||
* This information is exposed to all database drivers, although it is only
|
||||
* useful on some of them. This method is table prefix-aware.
|
||||
*
|
||||
* @param $table
|
||||
* @param string $table
|
||||
* The table name to use for the sequence.
|
||||
* @param $field
|
||||
* @param string $field
|
||||
* The field name to use for the sequence.
|
||||
*
|
||||
* @return
|
||||
* @return string
|
||||
* A table prefix-parsed string for the sequence name.
|
||||
*/
|
||||
public function makeSequenceName($table, $field) {
|
||||
|
@ -441,10 +453,10 @@ abstract class Connection {
|
|||
*
|
||||
* The comment string will be sanitized to avoid SQL injection attacks.
|
||||
*
|
||||
* @param $comments
|
||||
* @param string[] $comments
|
||||
* An array of query comment strings.
|
||||
*
|
||||
* @return
|
||||
* @return string
|
||||
* A sanitized comment string.
|
||||
*/
|
||||
public function makeComment($comments) {
|
||||
|
@ -483,10 +495,10 @@ abstract class Connection {
|
|||
* Unless the comment is sanitised first, the SQL server would drop the
|
||||
* node table and ignore the rest of the SQL statement.
|
||||
*
|
||||
* @param $comment
|
||||
* @param string $comment
|
||||
* A query comment string.
|
||||
*
|
||||
* @return
|
||||
* @return string
|
||||
* A sanitized version of the query comment string.
|
||||
*/
|
||||
protected function filterComment($comment = '') {
|
||||
|
@ -500,7 +512,7 @@ abstract class Connection {
|
|||
* query. All queries executed by Drupal are executed as PDO prepared
|
||||
* statements.
|
||||
*
|
||||
* @param $query
|
||||
* @param string|\Drupal\Core\Database\StatementInterface $query
|
||||
* The query to execute. In most cases this will be a string containing
|
||||
* an SQL query with placeholders. An already-prepared instance of
|
||||
* StatementInterface may also be passed in order to allow calling
|
||||
|
@ -509,26 +521,36 @@ abstract class Connection {
|
|||
* It is extremely rare that module code will need to pass a statement
|
||||
* object to this method. It is used primarily for database drivers for
|
||||
* databases that require special LOB field handling.
|
||||
* @param $args
|
||||
* @param array $args
|
||||
* An array of arguments for the prepared statement. If the prepared
|
||||
* statement uses ? placeholders, this array must be an indexed array.
|
||||
* If it contains named placeholders, it must be an associative array.
|
||||
* @param $options
|
||||
* An associative array of options to control how the query is run. See
|
||||
* the documentation for DatabaseConnection::defaultOptions() for details.
|
||||
* @param array $options
|
||||
* An associative array of options to control how the query is run. The
|
||||
* given options will be merged with self::defaultOptions(). See the
|
||||
* documentation for self::defaultOptions() for details.
|
||||
* Typically, $options['return'] will be set by a default or by a query
|
||||
* builder, and should not be set by a user.
|
||||
*
|
||||
* @return \Drupal\Core\Database\StatementInterface|int|null
|
||||
* This method will return one of: the executed statement, the number of
|
||||
* rows affected by the query (not the number matched), or the generated
|
||||
* insert ID of the last query, depending on the value of
|
||||
* $options['return']. Typically that value will be set by default or a
|
||||
* query builder and should not be set by a user. If there is an error,
|
||||
* this method will return NULL and may throw an exception if
|
||||
* $options['throw_exception'] is TRUE.
|
||||
* This method will return one of the following:
|
||||
* - If either $options['return'] === self::RETURN_STATEMENT, or
|
||||
* $options['return'] is not set (due to self::defaultOptions()),
|
||||
* returns the executed statement.
|
||||
* - If $options['return'] === self::RETURN_AFFECTED,
|
||||
* returns the number of rows affected by the query
|
||||
* (not the number matched).
|
||||
* - If $options['return'] === self::RETURN_INSERT_ID,
|
||||
* returns the generated insert ID of the last query.
|
||||
* - If either $options['return'] === self::RETURN_NULL, or
|
||||
* an exception occurs and $options['throw_exception'] evaluates to FALSE,
|
||||
* returns NULL.
|
||||
*
|
||||
* @throws \Drupal\Core\Database\DatabaseExceptionWrapper
|
||||
* @throws \Drupal\Core\Database\IntegrityConstraintViolationException
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @see \Drupal\Core\Database\Connection::defaultOptions()
|
||||
*/
|
||||
public function query($query, array $args = array(), $options = array()) {
|
||||
// Use default values if not already set.
|
||||
|
@ -628,6 +650,13 @@ abstract class Connection {
|
|||
*
|
||||
* @return bool
|
||||
* TRUE if the query was modified, FALSE otherwise.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* This exception is thrown when:
|
||||
* - A placeholder that ends in [] is supplied, and the supplied value is
|
||||
* not an array.
|
||||
* - A placeholder that does not end in [] is supplied, and the supplied
|
||||
* value is an array.
|
||||
*/
|
||||
protected function expandArguments(&$query, &$args) {
|
||||
$modified = FALSE;
|
||||
|
@ -700,12 +729,12 @@ abstract class Connection {
|
|||
/**
|
||||
* Prepares and returns a SELECT query object.
|
||||
*
|
||||
* @param $table
|
||||
* @param string $table
|
||||
* The base table for this query, that is, the first table in the FROM
|
||||
* clause. This table will also be used as the "base" table for query_alter
|
||||
* hook implementations.
|
||||
* @param $alias
|
||||
* The alias of the base table of this query.
|
||||
* @param string $alias
|
||||
* (optional) The alias of the base table of this query.
|
||||
* @param $options
|
||||
* An array of options on the query.
|
||||
*
|
||||
|
@ -724,8 +753,10 @@ abstract class Connection {
|
|||
/**
|
||||
* Prepares and returns an INSERT query object.
|
||||
*
|
||||
* @param $options
|
||||
* An array of options on the query.
|
||||
* @param string $table
|
||||
* The table to use for the insert statement.
|
||||
* @param array $options
|
||||
* (optional) An array of options on the query.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\Insert
|
||||
* A new Insert query object.
|
||||
|
@ -740,8 +771,10 @@ abstract class Connection {
|
|||
/**
|
||||
* Prepares and returns a MERGE query object.
|
||||
*
|
||||
* @param $options
|
||||
* An array of options on the query.
|
||||
* @param string $table
|
||||
* The table to use for the merge statement.
|
||||
* @param array $options
|
||||
* (optional) An array of options on the query.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\Merge
|
||||
* A new Merge query object.
|
||||
|
@ -757,8 +790,10 @@ abstract class Connection {
|
|||
/**
|
||||
* Prepares and returns an UPDATE query object.
|
||||
*
|
||||
* @param $options
|
||||
* An array of options on the query.
|
||||
* @param string $table
|
||||
* The table to use for the update statement.
|
||||
* @param array $options
|
||||
* (optional) An array of options on the query.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\Update
|
||||
* A new Update query object.
|
||||
|
@ -773,8 +808,10 @@ abstract class Connection {
|
|||
/**
|
||||
* Prepares and returns a DELETE query object.
|
||||
*
|
||||
* @param $options
|
||||
* An array of options on the query.
|
||||
* @param string $table
|
||||
* The table to use for the delete statement.
|
||||
* @param array $options
|
||||
* (optional) An array of options on the query.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\Delete
|
||||
* A new Delete query object.
|
||||
|
@ -789,8 +826,10 @@ abstract class Connection {
|
|||
/**
|
||||
* Prepares and returns a TRUNCATE query object.
|
||||
*
|
||||
* @param $options
|
||||
* An array of options on the query.
|
||||
* @param string $table
|
||||
* The table to use for the truncate statement.
|
||||
* @param array $options
|
||||
* (optional) An array of options on the query.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\Truncate
|
||||
* A new Truncate query object.
|
||||
|
@ -825,8 +864,11 @@ abstract class Connection {
|
|||
* For some database drivers, it may also wrap the database name in
|
||||
* database-specific escape characters.
|
||||
*
|
||||
* @param string $database
|
||||
* An unsanitized database name.
|
||||
*
|
||||
* @return string
|
||||
* The sanitized database name string.
|
||||
* The sanitized database name.
|
||||
*/
|
||||
public function escapeDatabase($database) {
|
||||
return preg_replace('/[^A-Za-z0-9_.]+/', '', $database);
|
||||
|
@ -839,8 +881,11 @@ abstract class Connection {
|
|||
* For some database drivers, it may also wrap the table name in
|
||||
* database-specific escape characters.
|
||||
*
|
||||
* @return
|
||||
* The sanitized table name string.
|
||||
* @param string $table
|
||||
* An unsanitized table name.
|
||||
*
|
||||
* @return string
|
||||
* The sanitized table name.
|
||||
*/
|
||||
public function escapeTable($table) {
|
||||
return preg_replace('/[^A-Za-z0-9_.]+/', '', $table);
|
||||
|
@ -853,8 +898,11 @@ abstract class Connection {
|
|||
* For some database drivers, it may also wrap the field name in
|
||||
* database-specific escape characters.
|
||||
*
|
||||
* @return
|
||||
* The sanitized field name string.
|
||||
* @param string $field
|
||||
* An unsanitized field name.
|
||||
*
|
||||
* @return string
|
||||
* The sanitized field name.
|
||||
*/
|
||||
public function escapeField($field) {
|
||||
return preg_replace('/[^A-Za-z0-9_.]+/', '', $field);
|
||||
|
@ -868,8 +916,11 @@ abstract class Connection {
|
|||
* DatabaseConnection::escapeTable(), this doesn't allow the period (".")
|
||||
* because that is not allowed in aliases.
|
||||
*
|
||||
* @return
|
||||
* The sanitized field name string.
|
||||
* @param string $field
|
||||
* An unsanitized alias name.
|
||||
*
|
||||
* @return string
|
||||
* The sanitized alias name.
|
||||
*/
|
||||
public function escapeAlias($field) {
|
||||
return preg_replace('/[^A-Za-z0-9_]+/', '', $field);
|
||||
|
@ -894,10 +945,10 @@ abstract class Connection {
|
|||
* Backslash is defined as escape character for LIKE patterns in
|
||||
* Drupal\Core\Database\Query\Condition::mapConditionOperator().
|
||||
*
|
||||
* @param $string
|
||||
* @param string $string
|
||||
* The string to escape.
|
||||
*
|
||||
* @return
|
||||
* @return string
|
||||
* The escaped string.
|
||||
*/
|
||||
public function escapeLike($string) {
|
||||
|
@ -907,7 +958,7 @@ abstract class Connection {
|
|||
/**
|
||||
* Determines if there is an active transaction open.
|
||||
*
|
||||
* @return
|
||||
* @return bool
|
||||
* TRUE if we're currently in a transaction, FALSE otherwise.
|
||||
*/
|
||||
public function inTransaction() {
|
||||
|
@ -915,7 +966,10 @@ abstract class Connection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determines current transaction depth.
|
||||
* Determines the current transaction depth.
|
||||
*
|
||||
* @return int
|
||||
* The current transaction depth.
|
||||
*/
|
||||
public function transactionDepth() {
|
||||
return count($this->transactionLayers);
|
||||
|
@ -924,8 +978,8 @@ abstract class Connection {
|
|||
/**
|
||||
* Returns a new DatabaseTransaction object on this connection.
|
||||
*
|
||||
* @param $name
|
||||
* Optional name of the savepoint.
|
||||
* @param string $name
|
||||
* (optional) The name of the savepoint.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Transaction
|
||||
* A Transaction object.
|
||||
|
@ -942,10 +996,11 @@ abstract class Connection {
|
|||
*
|
||||
* This method throws an exception if no transaction is active.
|
||||
*
|
||||
* @param $savepoint_name
|
||||
* The name of the savepoint. The default, 'drupal_transaction', will roll
|
||||
* the entire transaction back.
|
||||
* @param string $savepoint_name
|
||||
* (optional) The name of the savepoint. The default, 'drupal_transaction',
|
||||
* will roll the entire transaction back.
|
||||
*
|
||||
* @throws \Drupal\Core\Database\TransactionOutOfOrderException
|
||||
* @throws \Drupal\Core\Database\TransactionNoActiveException
|
||||
*
|
||||
* @see \Drupal\Core\Database\Transaction::rollback()
|
||||
|
@ -997,6 +1052,9 @@ abstract class Connection {
|
|||
*
|
||||
* If no transaction is already active, we begin a new transaction.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the transaction.
|
||||
*
|
||||
* @throws \Drupal\Core\Database\TransactionNameNonUniqueException
|
||||
*
|
||||
* @see \Drupal\Core\Database\Transaction
|
||||
|
@ -1026,8 +1084,8 @@ abstract class Connection {
|
|||
* back the transaction as necessary. If no transaction is active, we return
|
||||
* because the transaction may have manually been rolled back.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the savepoint
|
||||
* @param string $name
|
||||
* The name of the savepoint.
|
||||
*
|
||||
* @throws \Drupal\Core\Database\TransactionNoActiveException
|
||||
* @throws \Drupal\Core\Database\TransactionCommitFailedException
|
||||
|
@ -1083,16 +1141,17 @@ abstract class Connection {
|
|||
* separate parameters so that they can be properly escaped to avoid SQL
|
||||
* injection attacks.
|
||||
*
|
||||
* @param $query
|
||||
* @param string $query
|
||||
* A string containing an SQL query.
|
||||
* @param $args
|
||||
* An array of values to substitute into the query at placeholder markers.
|
||||
* @param $from
|
||||
* @param int $from
|
||||
* The first result row to return.
|
||||
* @param $count
|
||||
* @param int $count
|
||||
* The maximum number of result rows to return.
|
||||
* @param $options
|
||||
* An array of options on the query.
|
||||
* @param array $args
|
||||
* (optional) An array of values to substitute into the query at placeholder
|
||||
* markers.
|
||||
* @param array $options
|
||||
* (optional) An array of options on the query.
|
||||
*
|
||||
* @return \Drupal\Core\Database\StatementInterface
|
||||
* A database query result resource, or NULL if the query was not executed
|
||||
|
@ -1103,7 +1162,7 @@ abstract class Connection {
|
|||
/**
|
||||
* Generates a temporary table name.
|
||||
*
|
||||
* @return
|
||||
* @return string
|
||||
* A table name.
|
||||
*/
|
||||
protected function generateTemporaryTableName() {
|
||||
|
@ -1122,15 +1181,17 @@ abstract class Connection {
|
|||
* Note that if you need to know how many results were returned, you should do
|
||||
* a SELECT COUNT(*) on the temporary table afterwards.
|
||||
*
|
||||
* @param $query
|
||||
* @param string $query
|
||||
* A string containing a normal SELECT SQL query.
|
||||
* @param $args
|
||||
* An array of values to substitute into the query at placeholder markers.
|
||||
* @param $options
|
||||
* An associative array of options to control how the query is run. See
|
||||
* the documentation for DatabaseConnection::defaultOptions() for details.
|
||||
* @param array $args
|
||||
* (optional) An array of values to substitute into the query at placeholder
|
||||
* markers.
|
||||
* @param array $options
|
||||
* (optional) An associative array of options to control how the query is
|
||||
* run. See the documentation for DatabaseConnection::defaultOptions() for
|
||||
* details.
|
||||
*
|
||||
* @return
|
||||
* @return string
|
||||
* The name of the temporary table.
|
||||
*/
|
||||
abstract function queryTemporary($query, array $args = array(), array $options = array());
|
||||
|
@ -1142,6 +1203,9 @@ abstract class Connection {
|
|||
* instance, there could be two MySQL drivers, mysql and mysql_mock. This
|
||||
* function would return different values for each, but both would return
|
||||
* "mysql" for databaseType().
|
||||
*
|
||||
* @return string
|
||||
* The type of database driver.
|
||||
*/
|
||||
abstract public function driver();
|
||||
|
||||
|
@ -1155,7 +1219,7 @@ abstract class Connection {
|
|||
/**
|
||||
* Determines if this driver supports transactions.
|
||||
*
|
||||
* @return
|
||||
* @return bool
|
||||
* TRUE if this connection supports transactions, FALSE otherwise.
|
||||
*/
|
||||
public function supportsTransactions() {
|
||||
|
@ -1167,7 +1231,7 @@ abstract class Connection {
|
|||
*
|
||||
* DDL queries are those that change the schema, such as ALTER queries.
|
||||
*
|
||||
* @return
|
||||
* @return bool
|
||||
* TRUE if this connection supports transactions for DDL queries, FALSE
|
||||
* otherwise.
|
||||
*/
|
||||
|
@ -1199,7 +1263,7 @@ abstract class Connection {
|
|||
* overridable lookup function. Database connections should define only
|
||||
* those operators they wish to be handled differently than the default.
|
||||
*
|
||||
* @param $operator
|
||||
* @param string $operator
|
||||
* The condition operator, such as "IN", "BETWEEN", etc. Case-sensitive.
|
||||
*
|
||||
* @return
|
||||
|
@ -1226,7 +1290,7 @@ abstract class Connection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves an unique id from a given sequence.
|
||||
* Retrieves an unique ID from a given sequence.
|
||||
*
|
||||
* Use this function if for some reason you can't use a serial field. For
|
||||
* example, MySQL has no ways of reading of the current value of a sequence
|
||||
|
@ -1234,9 +1298,9 @@ abstract class Connection {
|
|||
* value. Or sometimes you just need a unique integer.
|
||||
*
|
||||
* @param $existing_id
|
||||
* After a database import, it might be that the sequences table is behind,
|
||||
* so by passing in the maximum existing id, it can be assured that we
|
||||
* never issue the same id.
|
||||
* (optional) After a database import, it might be that the sequences table
|
||||
* is behind, so by passing in the maximum existing ID, it can be assured
|
||||
* that we never issue the same ID.
|
||||
*
|
||||
* @return
|
||||
* An integer number larger than any number returned by earlier calls and
|
||||
|
|
|
@ -14,6 +14,7 @@ use Drupal\Core\Database\DatabaseNotFoundException;
|
|||
use Drupal\Core\Database\TransactionCommitFailedException;
|
||||
use Drupal\Core\Database\DatabaseException;
|
||||
use Drupal\Core\Database\Connection as DatabaseConnection;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
|
||||
/**
|
||||
* @addtogroup database
|
||||
|
@ -34,6 +35,16 @@ class Connection extends DatabaseConnection {
|
|||
*/
|
||||
protected $needsCleanup = FALSE;
|
||||
|
||||
/**
|
||||
* The minimal possible value for the max_allowed_packet setting of MySQL.
|
||||
*
|
||||
* @link https://mariadb.com/kb/en/mariadb/server-system-variables/#max_allowed_packet
|
||||
* @link https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_allowed_packet
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MIN_MAX_ALLOWED_PACKET = 1024;
|
||||
|
||||
/**
|
||||
* Constructs a Connection object.
|
||||
*/
|
||||
|
@ -49,6 +60,24 @@ class Connection extends DatabaseConnection {
|
|||
$this->connectionOptions = $connection_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query($query, array $args = array(), $options = array()) {
|
||||
try {
|
||||
return parent::query($query, $args, $options);
|
||||
} catch (DatabaseException $e) {
|
||||
if ($e->getPrevious()->errorInfo[1] == 1153) {
|
||||
// If a max_allowed_packet error occurs the message length is truncated.
|
||||
// This should prevent the error from recurring if the exception is
|
||||
// logged to the database using dblog or the like.
|
||||
$message = Unicode::truncateBytes($e->getMessage(), self::MIN_MAX_ALLOWED_PACKET);
|
||||
$e = new DatabaseExceptionWrapper($message, $e->getCode(), $e->getPrevious());
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -278,6 +307,7 @@ class Connection extends DatabaseConnection {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -31,6 +31,27 @@ class Connection extends DatabaseConnection {
|
|||
*/
|
||||
const DATABASE_NOT_FOUND = 7;
|
||||
|
||||
/**
|
||||
* The list of PostgreSQL reserved key words.
|
||||
*
|
||||
* @see http://www.postgresql.org/docs/9.4/static/sql-keywords-appendix.html
|
||||
*/
|
||||
protected $postgresqlReservedKeyWords = ['all', 'analyse', 'analyze', 'and',
|
||||
'any', 'array', 'as', 'asc', 'asymmetric', 'authorization', 'binary', 'both',
|
||||
'case', 'cast', 'check', 'collate', 'collation', 'column', 'concurrently',
|
||||
'constraint', 'create', 'cross', 'current_catalog', 'current_date',
|
||||
'current_role', 'current_schema', 'current_time', 'current_timestamp',
|
||||
'current_user', 'default', 'deferrable', 'desc', 'distinct', 'do', 'else',
|
||||
'end', 'except', 'false', 'fetch', 'for', 'foreign', 'freeze', 'from', 'full',
|
||||
'grant', 'group', 'having', 'ilike', 'in', 'initially', 'inner', 'intersect',
|
||||
'into', 'is', 'isnull', 'join', 'lateral', 'leading', 'left', 'like', 'limit',
|
||||
'localtime', 'localtimestamp', 'natural', 'not', 'notnull', 'null', 'offset',
|
||||
'on', 'only', 'or', 'order', 'outer', 'over', 'overlaps', 'placing',
|
||||
'primary', 'references', 'returning', 'right', 'select', 'session_user',
|
||||
'similar', 'some', 'symmetric', 'table', 'then', 'to', 'trailing', 'true',
|
||||
'union', 'unique', 'user', 'using', 'variadic', 'verbose', 'when', 'where',
|
||||
'window', 'with'];
|
||||
|
||||
/**
|
||||
* Constructs a connection object.
|
||||
*/
|
||||
|
@ -167,6 +188,10 @@ class Connection extends DatabaseConnection {
|
|||
// Quote the field name for case-sensitivity.
|
||||
$escaped = '"' . $escaped . '"';
|
||||
}
|
||||
elseif (in_array(strtolower($escaped), $this->postgresqlReservedKeyWords)) {
|
||||
// Quote the field name for PostgreSQL reserved key words.
|
||||
$escaped = '"' . $escaped . '"';
|
||||
}
|
||||
|
||||
return $escaped;
|
||||
}
|
||||
|
@ -181,6 +206,10 @@ class Connection extends DatabaseConnection {
|
|||
if (preg_match('/[A-Z]/', $escaped)) {
|
||||
$escaped = '"' . $escaped . '"';
|
||||
}
|
||||
elseif (in_array(strtolower($escaped), $this->postgresqlReservedKeyWords)) {
|
||||
// Quote the alias name for PostgreSQL reserved key words.
|
||||
$escaped = '"' . $escaped . '"';
|
||||
}
|
||||
|
||||
return $escaped;
|
||||
}
|
||||
|
@ -195,6 +224,10 @@ class Connection extends DatabaseConnection {
|
|||
if (preg_match('/[A-Z]/', $escaped)) {
|
||||
$escaped = '"' . $escaped . '"';
|
||||
}
|
||||
elseif (in_array(strtolower($escaped), $this->postgresqlReservedKeyWords)) {
|
||||
// Quote the table name for PostgreSQL reserved key words.
|
||||
$escaped = '"' . $escaped . '"';
|
||||
}
|
||||
|
||||
return $escaped;
|
||||
}
|
||||
|
|
|
@ -117,6 +117,8 @@ class Insert extends QueryInsert {
|
|||
// Default fields are always placed first for consistency.
|
||||
$insert_fields = array_merge($this->defaultFields, $this->insertFields);
|
||||
|
||||
$insert_fields = array_map(function($f) { return $this->connection->escapeField($f); }, $insert_fields);
|
||||
|
||||
// If we're selecting from a SelectQuery, finish building the query and
|
||||
// pass it back, as any remaining options are irrelevant.
|
||||
if (!empty($this->fromQuery)) {
|
||||
|
@ -154,4 +156,5 @@ class Insert extends QueryInsert {
|
|||
|
||||
return $query;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ class Tasks extends InstallTasks {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function minimumVersion() {
|
||||
return '8.3';
|
||||
return '9.1.2';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -329,7 +329,7 @@ class Schema extends DatabaseSchema {
|
|||
}
|
||||
|
||||
if (!empty($field['unsigned'])) {
|
||||
// Unsigned datatypes are not supported in PostgreSQL 8.3. In MySQL,
|
||||
// Unsigned datatypes are not supported in PostgreSQL 9.1. In MySQL,
|
||||
// they are used to ensure a positive number is inserted and it also
|
||||
// doubles the maximum integer size that can be stored in a field.
|
||||
// The PostgreSQL schema in Drupal creates a check constraint
|
||||
|
@ -569,7 +569,7 @@ class Schema extends DatabaseSchema {
|
|||
}
|
||||
|
||||
public function indexExists($table, $name) {
|
||||
// Details http://www.postgresql.org/docs/8.3/interactive/view-pg-indexes.html
|
||||
// Details http://www.postgresql.org/docs/9.1/interactive/view-pg-indexes.html
|
||||
$index_name = $this->ensureIdentifiersLength($table, $name, 'idx');
|
||||
// Remove leading and trailing quotes because the index name is in a WHERE
|
||||
// clause and not used as an identifier.
|
||||
|
|
|
@ -22,7 +22,7 @@ use Drupal\Core\Database\StatementInterface;
|
|||
class Statement extends StatementPrefetch implements StatementInterface {
|
||||
|
||||
/**
|
||||
* SQLite specific implementation of getStatement().
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* The PDO SQLite layer doesn't replace numeric placeholders in queries
|
||||
* correctly, and this makes numeric expressions (such as COUNT(*) >= :count)
|
||||
|
@ -87,6 +87,9 @@ class Statement extends StatementPrefetch implements StatementInterface {
|
|||
return $this->pdoConnection->prepare($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($args = array(), $options = array()) {
|
||||
try {
|
||||
$return = parent::execute($args, $options);
|
||||
|
|
|
@ -17,6 +17,8 @@ use Drupal\Core\Database\Connection;
|
|||
*/
|
||||
class Delete extends Query implements ConditionInterface {
|
||||
|
||||
use QueryConditionTrait;
|
||||
|
||||
/**
|
||||
* The table from which to delete.
|
||||
*
|
||||
|
@ -24,15 +26,6 @@ class Delete extends Query implements ConditionInterface {
|
|||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* The condition object for this query.
|
||||
*
|
||||
* Condition handling is handled via composition.
|
||||
*
|
||||
* @var Condition
|
||||
*/
|
||||
protected $condition;
|
||||
|
||||
/**
|
||||
* Constructs a Delete object.
|
||||
*
|
||||
|
@ -51,82 +44,6 @@ class Delete extends Query implements ConditionInterface {
|
|||
$this->condition = new Condition('AND');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::condition().
|
||||
*/
|
||||
public function condition($field, $value = NULL, $operator = '=') {
|
||||
$this->condition->condition($field, $value, $operator);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::isNull().
|
||||
*/
|
||||
public function isNull($field) {
|
||||
$this->condition->isNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::isNotNull().
|
||||
*/
|
||||
public function isNotNull($field) {
|
||||
$this->condition->isNotNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::exists().
|
||||
*/
|
||||
public function exists(SelectInterface $select) {
|
||||
$this->condition->exists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::notExists().
|
||||
*/
|
||||
public function notExists(SelectInterface $select) {
|
||||
$this->condition->notExists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::conditions().
|
||||
*/
|
||||
public function &conditions() {
|
||||
return $this->condition->conditions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::arguments().
|
||||
*/
|
||||
public function arguments() {
|
||||
return $this->condition->arguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::where().
|
||||
*/
|
||||
public function where($snippet, $args = array()) {
|
||||
$this->condition->where($snippet, $args);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::compile().
|
||||
*/
|
||||
public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
|
||||
return $this->condition->compile($connection, $queryPlaceholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::compiled().
|
||||
*/
|
||||
public function compiled() {
|
||||
return $this->condition->compiled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the DELETE query.
|
||||
*
|
||||
|
|
|
@ -49,6 +49,9 @@ use Drupal\Core\Database\IntegrityConstraintViolationException;
|
|||
* fields() and updateFields().
|
||||
*/
|
||||
class Merge extends Query implements ConditionInterface {
|
||||
|
||||
use QueryConditionTrait;
|
||||
|
||||
/**
|
||||
* Returned by execute() if an INSERT query has been executed.
|
||||
*/
|
||||
|
@ -339,82 +342,6 @@ class Merge extends Query implements ConditionInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::condition().
|
||||
*/
|
||||
public function condition($field, $value = NULL, $operator = '=') {
|
||||
$this->condition->condition($field, $value, $operator);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::isNull().
|
||||
*/
|
||||
public function isNull($field) {
|
||||
$this->condition->isNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::isNotNull().
|
||||
*/
|
||||
public function isNotNull($field) {
|
||||
$this->condition->isNotNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::exists().
|
||||
*/
|
||||
public function exists(SelectInterface $select) {
|
||||
$this->condition->exists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::notExists().
|
||||
*/
|
||||
public function notExists(SelectInterface $select) {
|
||||
$this->condition->notExists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::conditions().
|
||||
*/
|
||||
public function &conditions() {
|
||||
return $this->condition->conditions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::arguments().
|
||||
*/
|
||||
public function arguments() {
|
||||
return $this->condition->arguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::where().
|
||||
*/
|
||||
public function where($snippet, $args = array()) {
|
||||
$this->condition->where($snippet, $args);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::compile().
|
||||
*/
|
||||
public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
|
||||
return $this->condition->compile($connection, $queryPlaceholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::compiled().
|
||||
*/
|
||||
public function compiled() {
|
||||
return $this->condition->compiled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements PHP magic __toString method to convert the query to a string.
|
||||
*
|
||||
|
|
|
@ -180,25 +180,4 @@ abstract class Query implements PlaceholderInterface {
|
|||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function conditionGroupFactory($conjunction = 'AND') {
|
||||
return new Condition($conjunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function andConditionGroup() {
|
||||
return $this->conditionGroupFactory('AND');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orConditionGroup() {
|
||||
return $this->conditionGroupFactory('OR');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
125
core/lib/Drupal/Core/Database/Query/QueryConditionTrait.php
Normal file
125
core/lib/Drupal/Core/Database/Query/QueryConditionTrait.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Database\Query\QueryConditionTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Database\Query;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
|
||||
/**
|
||||
* Provides an implementation of ConditionInterface.
|
||||
*
|
||||
* @see \Drupal\Core\Database\Query\ConditionInterface
|
||||
*/
|
||||
trait QueryConditionTrait {
|
||||
|
||||
/**
|
||||
* The condition object for this query.
|
||||
*
|
||||
* Condition handling is handled via composition.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Query\Condition
|
||||
*/
|
||||
protected $condition;
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::condition().
|
||||
*/
|
||||
public function condition($field, $value = NULL, $operator = '=') {
|
||||
$this->condition->condition($field, $value, $operator);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::isNull().
|
||||
*/
|
||||
public function isNull($field) {
|
||||
$this->condition->isNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::isNotNull().
|
||||
*/
|
||||
public function isNotNull($field) {
|
||||
$this->condition->isNotNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::exists().
|
||||
*/
|
||||
public function exists(SelectInterface $select) {
|
||||
$this->condition->exists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::notExists().
|
||||
*/
|
||||
public function notExists(SelectInterface $select) {
|
||||
$this->condition->notExists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::conditions().
|
||||
*/
|
||||
public function &conditions() {
|
||||
return $this->condition->conditions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::arguments().
|
||||
*/
|
||||
public function arguments() {
|
||||
return $this->condition->arguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::where().
|
||||
*/
|
||||
public function where($snippet, $args = array()) {
|
||||
$this->condition->where($snippet, $args);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::compile().
|
||||
*/
|
||||
public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
|
||||
$this->condition->compile($connection, $queryPlaceholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::compiled().
|
||||
*/
|
||||
public function compiled() {
|
||||
return $this->condition->compiled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::conditionGroupFactory().
|
||||
*/
|
||||
public function conditionGroupFactory($conjunction = 'AND') {
|
||||
return new Condition($conjunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::andConditionGroup().
|
||||
*/
|
||||
public function andConditionGroup() {
|
||||
return $this->conditionGroupFactory('AND');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::orConditionGroup().
|
||||
*/
|
||||
public function orConditionGroup() {
|
||||
return $this->conditionGroupFactory('OR');
|
||||
}
|
||||
|
||||
}
|
|
@ -18,6 +18,8 @@ use Drupal\Core\Database\Connection;
|
|||
*/
|
||||
class Select extends Query implements SelectInterface {
|
||||
|
||||
use QueryConditionTrait;
|
||||
|
||||
/**
|
||||
* The fields to SELECT.
|
||||
*
|
||||
|
@ -71,13 +73,6 @@ class Select extends Query implements SelectInterface {
|
|||
*/
|
||||
protected $group = array();
|
||||
|
||||
/**
|
||||
* The conditional object for the WHERE clause.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Query\Condition
|
||||
*/
|
||||
protected $where;
|
||||
|
||||
/**
|
||||
* The conditional object for the HAVING clause.
|
||||
*
|
||||
|
@ -139,7 +134,7 @@ class Select extends Query implements SelectInterface {
|
|||
$options['return'] = Database::RETURN_STATEMENT;
|
||||
parent::__construct($connection, $options);
|
||||
$conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND';
|
||||
$this->where = new Condition($conjunction);
|
||||
$this->condition = new Condition($conjunction);
|
||||
$this->having = new Condition($conjunction);
|
||||
$this->addJoin(NULL, $table, $alias);
|
||||
}
|
||||
|
@ -188,21 +183,6 @@ class Select extends Query implements SelectInterface {
|
|||
return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function condition($field, $value = NULL, $operator = '=') {
|
||||
$this->where->condition($field, $value, $operator);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &conditions() {
|
||||
return $this->where->conditions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -211,7 +191,7 @@ class Select extends Query implements SelectInterface {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
$args = $this->where->arguments() + $this->having->arguments();
|
||||
$args = $this->condition->arguments() + $this->having->arguments();
|
||||
|
||||
foreach ($this->tables as $table) {
|
||||
if ($table['arguments']) {
|
||||
|
@ -238,51 +218,11 @@ class Select extends Query implements SelectInterface {
|
|||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function where($snippet, $args = array()) {
|
||||
$this->where->where($snippet, $args);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isNull($field) {
|
||||
$this->where->isNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isNotNull($field) {
|
||||
$this->where->isNotNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists(SelectInterface $select) {
|
||||
$this->where->exists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function notExists(SelectInterface $select) {
|
||||
$this->where->notExists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
|
||||
$this->where->compile($connection, $queryPlaceholder);
|
||||
$this->condition->compile($connection, $queryPlaceholder);
|
||||
$this->having->compile($connection, $queryPlaceholder);
|
||||
|
||||
foreach ($this->tables as $table) {
|
||||
|
@ -302,7 +242,7 @@ class Select extends Query implements SelectInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function compiled() {
|
||||
if (!$this->where->compiled() || !$this->having->compiled()) {
|
||||
if (!$this->condition->compiled() || !$this->having->compiled()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -463,6 +403,13 @@ class Select extends Query implements SelectInterface {
|
|||
return $this->connection->escapeLike($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function escapeField($string) {
|
||||
return $this->connection->escapeField($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -885,9 +832,9 @@ class Select extends Query implements SelectInterface {
|
|||
}
|
||||
|
||||
// WHERE
|
||||
if (count($this->where)) {
|
||||
if (count($this->condition)) {
|
||||
// There is an implicit string cast on $this->condition.
|
||||
$query .= "\nWHERE " . $this->where;
|
||||
$query .= "\nWHERE " . $this->condition;
|
||||
}
|
||||
|
||||
// GROUP BY
|
||||
|
@ -943,7 +890,7 @@ class Select extends Query implements SelectInterface {
|
|||
// want to clone the database connection object as that would duplicate the
|
||||
// connection itself.
|
||||
|
||||
$this->where = clone($this->where);
|
||||
$this->condition = clone($this->condition);
|
||||
$this->having = clone($this->having);
|
||||
foreach ($this->union as $key => $aggregate) {
|
||||
$this->union[$key]['query'] = clone($aggregate['query']);
|
||||
|
|
|
@ -210,6 +210,14 @@ class SelectExtender implements SelectInterface {
|
|||
return $this->query->escapeLike($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function escapeField($string) {
|
||||
$this->query->escapeField($string);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) {
|
||||
return $this->query->getArguments($queryPlaceholder);
|
||||
}
|
||||
|
|
|
@ -140,6 +140,18 @@ interface SelectInterface extends ConditionInterface, AlterableInterface, Extend
|
|||
*/
|
||||
public function escapeLike($string);
|
||||
|
||||
/**
|
||||
* Escapes a field name string.
|
||||
*
|
||||
* Force all field names to be strictly alphanumeric-plus-underscore.
|
||||
* For some database drivers, it may also wrap the field name in
|
||||
* database-specific escape characters.
|
||||
*
|
||||
* @return
|
||||
* The sanitized field name string.
|
||||
*/
|
||||
public function escapeField($string);
|
||||
|
||||
/**
|
||||
* Compiles and returns an associative array of the arguments for this prepared statement.
|
||||
*
|
||||
|
@ -632,4 +644,5 @@ interface SelectInterface extends ConditionInterface, AlterableInterface, Extend
|
|||
* The called object.
|
||||
*/
|
||||
public function forUpdate($set = TRUE);
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ use Drupal\Core\Database\Connection;
|
|||
*/
|
||||
class Update extends Query implements ConditionInterface {
|
||||
|
||||
use QueryConditionTrait;
|
||||
|
||||
/**
|
||||
* The table to update.
|
||||
*
|
||||
|
@ -38,15 +40,6 @@ class Update extends Query implements ConditionInterface {
|
|||
*/
|
||||
protected $arguments = array();
|
||||
|
||||
/**
|
||||
* The condition object for this query.
|
||||
*
|
||||
* Condition handling is handled via composition.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Query\Condition
|
||||
*/
|
||||
protected $condition;
|
||||
|
||||
/**
|
||||
* Array of fields to update to an expression in case of a duplicate record.
|
||||
*
|
||||
|
@ -80,82 +73,6 @@ class Update extends Query implements ConditionInterface {
|
|||
$this->condition = new Condition('AND');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::condition().
|
||||
*/
|
||||
public function condition($field, $value = NULL, $operator = '=') {
|
||||
$this->condition->condition($field, $value, $operator);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::isNull().
|
||||
*/
|
||||
public function isNull($field) {
|
||||
$this->condition->isNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::isNotNull().
|
||||
*/
|
||||
public function isNotNull($field) {
|
||||
$this->condition->isNotNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::exists().
|
||||
*/
|
||||
public function exists(SelectInterface $select) {
|
||||
$this->condition->exists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::notExists().
|
||||
*/
|
||||
public function notExists(SelectInterface $select) {
|
||||
$this->condition->notExists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::conditions().
|
||||
*/
|
||||
public function &conditions() {
|
||||
return $this->condition->conditions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::arguments().
|
||||
*/
|
||||
public function arguments() {
|
||||
return $this->condition->arguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::where().
|
||||
*/
|
||||
public function where($snippet, $args = array()) {
|
||||
$this->condition->where($snippet, $args);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::compile().
|
||||
*/
|
||||
public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
|
||||
return $this->condition->compile($connection, $queryPlaceholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\ConditionInterface::compiled().
|
||||
*/
|
||||
public function compiled() {
|
||||
return $this->condition->compiled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of field->value pairs to be updated.
|
||||
*
|
||||
|
@ -256,13 +173,13 @@ class Update extends Query implements ConditionInterface {
|
|||
$data['expression']->compile($this->connection, $this);
|
||||
$data['expression'] = ' (' . $data['expression'] . ')';
|
||||
}
|
||||
$update_fields[] = $field . '=' . $data['expression'];
|
||||
$update_fields[] = $this->connection->escapeField($field) . '=' . $data['expression'];
|
||||
unset($fields[$field]);
|
||||
}
|
||||
|
||||
$max_placeholder = 0;
|
||||
foreach ($fields as $field => $value) {
|
||||
$update_fields[] = $field . '=:db_update_placeholder_' . ($max_placeholder++);
|
||||
$update_fields[] = $this->connection->escapeField($field) . '=:db_update_placeholder_' . ($max_placeholder++);
|
||||
}
|
||||
|
||||
$query = $comments . 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields);
|
||||
|
|
|
@ -41,6 +41,9 @@ class Statement extends \PDOStatement implements StatementInterface {
|
|||
$this->setFetchMode(\PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($args = array(), $options = array()) {
|
||||
if (isset($options['fetch'])) {
|
||||
if (is_string($options['fetch'])) {
|
||||
|
@ -68,14 +71,23 @@ class Statement extends \PDOStatement implements StatementInterface {
|
|||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQueryString() {
|
||||
return $this->queryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchCol($index = 0) {
|
||||
return $this->fetchAll(\PDO::FETCH_COLUMN, $index);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAllAssoc($key, $fetch = NULL) {
|
||||
$return = array();
|
||||
if (isset($fetch)) {
|
||||
|
@ -95,6 +107,9 @@ class Statement extends \PDOStatement implements StatementInterface {
|
|||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAllKeyed($key_index = 0, $value_index = 1) {
|
||||
$return = array();
|
||||
$this->setFetchMode(\PDO::FETCH_NUM);
|
||||
|
@ -104,11 +119,17 @@ class Statement extends \PDOStatement implements StatementInterface {
|
|||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchField($index = 0) {
|
||||
// Call \PDOStatement::fetchColumn to fetch the field.
|
||||
return $this->fetchColumn($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAssoc() {
|
||||
// Call \PDOStatement::fetch to fetch the row.
|
||||
return $this->fetch(\PDO::FETCH_ASSOC);
|
||||
|
@ -127,4 +148,42 @@ class Statement extends \PDOStatement implements StatementInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFetchMode($mode, $a1 = NULL, $a2 = array()) {
|
||||
// Call \PDOStatement::setFetchMode to set fetch mode.
|
||||
// \PDOStatement is picky about the number of arguments in some cases so we
|
||||
// need to be pass the exact number of arguments we where given.
|
||||
switch (func_num_args()) {
|
||||
case 1:
|
||||
return parent::setFetchMode($mode);
|
||||
case 2:
|
||||
return parent::setFetchMode($mode, $a1);
|
||||
case 3:
|
||||
default:
|
||||
return parent::setFetchMode($mode, $a1, $a2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAll($mode = NULL, $column_index = NULL, $constructor_arguments = NULL) {
|
||||
// Call \PDOStatement::fetchAll to fetch all rows.
|
||||
// \PDOStatement is picky about the number of arguments in some cases so we
|
||||
// need to be pass the exact number of arguments we where given.
|
||||
switch (func_num_args()) {
|
||||
case 0:
|
||||
return parent::fetchAll();
|
||||
case 1:
|
||||
return parent::fetchAll($mode);
|
||||
case 2:
|
||||
return parent::fetchAll($mode, $column_index);
|
||||
case 3:
|
||||
default:
|
||||
return parent::fetchAll($mode, $column_index, $constructor_arguments);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,14 +28,23 @@ class StatementEmpty implements \Iterator, StatementInterface {
|
|||
*/
|
||||
public $allowRowCount = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($args = array(), $options = array()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQueryString() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rowCount() {
|
||||
if ($this->allowRowCount) {
|
||||
return 0;
|
||||
|
@ -43,61 +52,102 @@ class StatementEmpty implements \Iterator, StatementInterface {
|
|||
throw new RowCountException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFetchMode($mode, $a1 = NULL, $a2 = array()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($mode = NULL, $cursor_orientation = NULL, $cursor_offset = NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchField($index = 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchObject() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAssoc() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
function fetchAll($mode = NULL, $column_index = NULL, array $constructor_arguments = array()) {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAll($mode = NULL, $column_index = NULL, $constructor_arguments = NULL) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchCol($index = 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAllKeyed($key_index = 0, $value_index = 1) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAllAssoc($key, $fetch = NULL) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/* Implementations of Iterator. */
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function current() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function key() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rewind() {
|
||||
// Nothing to do: our DatabaseStatement can't be rewound.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function next() {
|
||||
// Do nothing, since this is an always-empty implementation.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function valid() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,11 +10,6 @@ namespace Drupal\Core\Database;
|
|||
/**
|
||||
* Represents a prepared statement.
|
||||
*
|
||||
* Some methods in that class are purposefully commented out. Due to a change in
|
||||
* how PHP defines PDOStatement, we can't define a signature for those methods
|
||||
* that will work the same way between versions older than 5.2.6 and later
|
||||
* versions. See http://bugs.php.net/bug.php?id=42452 for more details.
|
||||
*
|
||||
* Child implementations should either extend PDOStatement:
|
||||
* @code
|
||||
* class Drupal\Core\Database\Driver\oracle\Statement extends PDOStatement implements Drupal\Core\Database\StatementInterface {}
|
||||
|
@ -100,7 +95,7 @@ interface StatementInterface extends \Traversable {
|
|||
* If $mode is PDO::FETCH_CLASS, the optional arguments to pass to the
|
||||
* constructor.
|
||||
*/
|
||||
// public function setFetchMode($mode, $a1 = NULL, $a2 = array());
|
||||
public function setFetchMode($mode, $a1 = NULL, $a2 = array());
|
||||
|
||||
/**
|
||||
* Fetches the next row from a result set.
|
||||
|
@ -119,7 +114,7 @@ interface StatementInterface extends \Traversable {
|
|||
* @return
|
||||
* A result, formatted according to $mode.
|
||||
*/
|
||||
// public function fetch($mode = NULL, $cursor_orientation = NULL, $cursor_offset = NULL);
|
||||
public function fetch($mode = NULL, $cursor_orientation = NULL, $cursor_offset = NULL);
|
||||
|
||||
/**
|
||||
* Returns a single field from the next record of a result set.
|
||||
|
@ -138,7 +133,7 @@ interface StatementInterface extends \Traversable {
|
|||
* The object will be of the class specified by StatementInterface::setFetchMode()
|
||||
* or stdClass if not specified.
|
||||
*/
|
||||
// public function fetchObject();
|
||||
public function fetchObject();
|
||||
|
||||
/**
|
||||
* Fetches the next row and returns it as an associative array.
|
||||
|
@ -165,7 +160,7 @@ interface StatementInterface extends \Traversable {
|
|||
* @return
|
||||
* An array of results.
|
||||
*/
|
||||
// function fetchAll($mode = NULL, $column_index = NULL, array $constructor_arguments);
|
||||
function fetchAll($mode = NULL, $column_index = NULL, $constructor_arguments = NULL);
|
||||
|
||||
/**
|
||||
* Returns an entire single column of a result set as an indexed array.
|
||||
|
|
|
@ -139,14 +139,7 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Executes a prepared statement.
|
||||
*
|
||||
* @param $args
|
||||
* An array of values with as many elements as there are bound parameters in the SQL statement being executed.
|
||||
* @param $options
|
||||
* An array of options for this query.
|
||||
* @return
|
||||
* TRUE on success, or FALSE on failure.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($args = array(), $options = array()) {
|
||||
if (isset($options['fetch'])) {
|
||||
|
@ -236,29 +229,29 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the object's SQL query string.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQueryString() {
|
||||
return $this->queryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \PDOStatement::setFetchMode()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFetchMode($fetchStyle, $a2 = NULL, $a3 = NULL) {
|
||||
$this->defaultFetchStyle = $fetchStyle;
|
||||
switch ($fetchStyle) {
|
||||
public function setFetchMode($mode, $a1 = NULL, $a2 = array()) {
|
||||
$this->defaultFetchStyle = $mode;
|
||||
switch ($mode) {
|
||||
case \PDO::FETCH_CLASS:
|
||||
$this->defaultFetchOptions['class'] = $a2;
|
||||
if ($a3) {
|
||||
$this->defaultFetchOptions['constructor_args'] = $a3;
|
||||
$this->defaultFetchOptions['class'] = $a1;
|
||||
if ($a2) {
|
||||
$this->defaultFetchOptions['constructor_args'] = $a2;
|
||||
}
|
||||
break;
|
||||
case \PDO::FETCH_COLUMN:
|
||||
$this->defaultFetchOptions['column'] = $a2;
|
||||
$this->defaultFetchOptions['column'] = $a1;
|
||||
break;
|
||||
case \PDO::FETCH_INTO:
|
||||
$this->defaultFetchOptions['object'] = $a2;
|
||||
$this->defaultFetchOptions['object'] = $a1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -274,8 +267,8 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
* array position in $this->data and format it according to $this->fetchStyle
|
||||
* and $this->fetchMode.
|
||||
*
|
||||
* @return
|
||||
* The current row formatted as requested.
|
||||
* @return mixed
|
||||
* The current row formatted as requested.
|
||||
*/
|
||||
public function current() {
|
||||
if (isset($this->currentRow)) {
|
||||
|
@ -327,16 +320,23 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/* Implementations of Iterator. */
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function key() {
|
||||
return $this->currentKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rewind() {
|
||||
// Nothing to do: our DatabaseStatement can't be rewound.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function next() {
|
||||
if (!empty($this->data)) {
|
||||
$this->currentRow = reset($this->data);
|
||||
|
@ -348,6 +348,9 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function valid() {
|
||||
return isset($this->currentRow);
|
||||
}
|
||||
|
@ -365,6 +368,9 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($fetch_style = NULL, $cursor_orientation = \PDO::FETCH_ORI_NEXT, $cursor_offset = NULL) {
|
||||
if (isset($this->currentRow)) {
|
||||
// Set the fetch parameter.
|
||||
|
@ -398,10 +404,16 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchField($index = 0) {
|
||||
return $this->fetchColumn($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchObject($class_name = NULL, $constructor_args = array()) {
|
||||
if (isset($this->currentRow)) {
|
||||
if (!isset($class_name)) {
|
||||
|
@ -427,6 +439,9 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAssoc() {
|
||||
if (isset($this->currentRow)) {
|
||||
$result = $this->currentRow;
|
||||
|
@ -438,14 +453,17 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
}
|
||||
|
||||
public function fetchAll($fetch_style = NULL, $fetch_column = NULL, $constructor_args = NULL) {
|
||||
$this->fetchStyle = isset($fetch_style) ? $fetch_style : $this->defaultFetchStyle;
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAll($mode = NULL, $column_index = NULL, $constructor_arguments = NULL) {
|
||||
$this->fetchStyle = isset($mode) ? $mode : $this->defaultFetchStyle;
|
||||
$this->fetchOptions = $this->defaultFetchOptions;
|
||||
if (isset($fetch_column)) {
|
||||
$this->fetchOptions['column'] = $fetch_column;
|
||||
if (isset($column_index)) {
|
||||
$this->fetchOptions['column'] = $column_index;
|
||||
}
|
||||
if (isset($constructor_args)) {
|
||||
$this->fetchOptions['constructor_args'] = $constructor_args;
|
||||
if (isset($constructor_arguments)) {
|
||||
$this->fetchOptions['constructor_args'] = $constructor_arguments;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
@ -462,6 +480,9 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchCol($index = 0) {
|
||||
if (isset($this->columnNames[$index])) {
|
||||
$result = array();
|
||||
|
@ -477,6 +498,9 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAllKeyed($key_index = 0, $value_index = 1) {
|
||||
if (!isset($this->columnNames[$key_index]) || !isset($this->columnNames[$value_index]))
|
||||
return array();
|
||||
|
@ -493,6 +517,9 @@ class StatementPrefetch implements \Iterator, StatementInterface {
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAllAssoc($key, $fetch_style = NULL) {
|
||||
$this->fetchStyle = isset($fetch_style) ? $fetch_style : $this->defaultFetchStyle;
|
||||
$this->fetchOptions = $this->defaultFetchOptions;
|
||||
|
|
|
@ -307,7 +307,8 @@
|
|||
* specification). Each specification is an array containing the name of
|
||||
* the referenced table ('table'), and an array of column mappings
|
||||
* ('columns'). Column mappings are defined by key pairs ('source_column' =>
|
||||
* 'referenced_column').
|
||||
* 'referenced_column'). This key is for documentation purposes only; foreign
|
||||
* keys are not created in the database, nor are they enforced by Drupal.
|
||||
* - 'indexes': An associative array of indexes ('indexname' =>
|
||||
* specification). Each specification is an array of one or more
|
||||
* key column specifiers (see below) that form an index on the
|
||||
|
@ -357,6 +358,8 @@
|
|||
* 'unique keys' => array(
|
||||
* 'vid' => array('vid'),
|
||||
* ),
|
||||
* // For documentation purposes only; foreign keys are not created in the
|
||||
* // database.
|
||||
* 'foreign keys' => array(
|
||||
* 'node_revision' => array(
|
||||
* 'table' => 'node_field_revision',
|
||||
|
@ -467,7 +470,9 @@ function hook_query_TAG_alter(Drupal\Core\Database\Query\AlterableInterface $que
|
|||
* creation and alteration of the supported database engines.
|
||||
*
|
||||
* See the Schema API Handbook at https://www.drupal.org/node/146843 for details
|
||||
* on schema definition structures.
|
||||
* on schema definition structures. Note that foreign key definitions are for
|
||||
* documentation purposes only; foreign keys are not created in the database,
|
||||
* nor are they enforced by Drupal.
|
||||
*
|
||||
* @return array
|
||||
* A schema definition structure array. For each element of the
|
||||
|
@ -517,6 +522,8 @@ function hook_schema() {
|
|||
'nid_vid' => array('nid', 'vid'),
|
||||
'vid' => array('vid'),
|
||||
),
|
||||
// For documentation purposes only; foreign keys are not created in the
|
||||
// database.
|
||||
'foreign keys' => array(
|
||||
'node_revision' => array(
|
||||
'table' => 'node_field_revision',
|
||||
|
|
|
@ -204,6 +204,11 @@ class DateFormatter {
|
|||
$interval %= $value;
|
||||
$granularity--;
|
||||
}
|
||||
elseif ($output) {
|
||||
// Break if there was previous output but not any output at this level,
|
||||
// to avoid skipping levels and getting output like "1 year 1 second".
|
||||
break;
|
||||
}
|
||||
|
||||
if ($granularity == 0) {
|
||||
break;
|
||||
|
@ -373,15 +378,21 @@ class DateFormatter {
|
|||
// ourselves.
|
||||
$interval_output = '';
|
||||
$days = $interval->d;
|
||||
if ($days >= 7) {
|
||||
$weeks = floor($days / 7);
|
||||
$weeks = floor($days / 7);
|
||||
if ($weeks) {
|
||||
$interval_output .= $this->formatPlural($weeks, '1 week', '@count weeks', array(), array('langcode' => $options['langcode']));
|
||||
$days -= $weeks * 7;
|
||||
$granularity--;
|
||||
}
|
||||
if ($granularity > 0 && $days > 0) {
|
||||
|
||||
if ((!$output || $weeks > 0) && $granularity > 0 && $days > 0) {
|
||||
$interval_output .= ($interval_output ? ' ' : '') . $this->formatPlural($days, '1 day', '@count days', array(), array('langcode' => $options['langcode']));
|
||||
}
|
||||
else {
|
||||
// If we did not output days, set the granularity to 0 so that we
|
||||
// will not output hours and get things like "1 week 1 hour".
|
||||
$granularity = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
|
@ -397,9 +408,14 @@ class DateFormatter {
|
|||
break;
|
||||
|
||||
}
|
||||
$output .= ($output ? ' ' : '') . $interval_output;
|
||||
$output .= ($output && $interval_output ? ' ' : '') . $interval_output;
|
||||
$granularity--;
|
||||
}
|
||||
elseif ($output) {
|
||||
// Break if there was previous output but not any output at this level,
|
||||
// to avoid skipping levels and getting output like "1 year 1 second".
|
||||
break;
|
||||
}
|
||||
|
||||
if ($granularity <= 0) {
|
||||
break;
|
||||
|
|
|
@ -335,10 +335,10 @@ class Datelist extends DateElementBase {
|
|||
protected static function incrementRound(&$date, $increment) {
|
||||
// Round minutes and seconds, if necessary.
|
||||
if ($date instanceOf DrupalDateTime && $increment > 1) {
|
||||
$day = intval(date_format($date, 'j'));
|
||||
$hour = intval(date_format($date, 'H'));
|
||||
$second = intval(round(intval(date_format($date, 's')) / $increment) * $increment);
|
||||
$minute = intval(date_format($date, 'i'));
|
||||
$day = intval($date->format('j'));
|
||||
$hour = intval($date->format('H'));
|
||||
$second = intval(round(intval($date->format('s')) / $increment) * $increment);
|
||||
$minute = intval($date->format('i'));
|
||||
if ($second == 60) {
|
||||
$minute += 1;
|
||||
$second = 0;
|
||||
|
@ -348,12 +348,12 @@ class Datelist extends DateElementBase {
|
|||
$hour += 1;
|
||||
$minute = 0;
|
||||
}
|
||||
date_time_set($date, $hour, $minute, $second);
|
||||
$date->setTime($hour, $minute, $second);
|
||||
if ($hour == 24) {
|
||||
$day += 1;
|
||||
$year = date_format($date, 'Y');
|
||||
$month = date_format($date, 'n');
|
||||
date_date_set($date, $year, $month, $day);
|
||||
$year = $date->format('Y');
|
||||
$month = $date->format('n');
|
||||
$date->setDate($year, $month, $day);
|
||||
}
|
||||
}
|
||||
return $date;
|
||||
|
|
|
@ -268,6 +268,7 @@ class Datetime extends DateElementBase {
|
|||
'#required' => $element['#required'],
|
||||
'#size' => max(12, strlen($element['#value']['date'])),
|
||||
'#error_no_message' => TRUE,
|
||||
'#date_date_format' => $element['#date_date_format'],
|
||||
);
|
||||
|
||||
// Allows custom callbacks to alter the element.
|
||||
|
|
|
@ -41,14 +41,14 @@ class DateFormat extends ConfigEntityBase implements DateFormatInterface {
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the date format entity.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The date format pattern.
|
||||
|
@ -101,7 +101,7 @@ class DateFormat extends ConfigEntityBase implements DateFormatInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
public function getCacheTagsToInvalidate() {
|
||||
return ['rendered'];
|
||||
}
|
||||
|
||||
|
|
|
@ -42,11 +42,25 @@ class BackendCompilerPass implements CompilerPassInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
$default_backend = $container->hasParameter('default_backend') ? $container->getParameter('default_backend') : NULL;
|
||||
// No default backend was configured, so continue as normal.
|
||||
if (!isset($default_backend)) {
|
||||
return;
|
||||
if ($container->hasParameter('default_backend')) {
|
||||
$default_backend = $container->getParameter('default_backend');
|
||||
// Opt out from the default backend.
|
||||
if (!$default_backend) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
$default_backend = $container->get('database')->driver();
|
||||
$container->set('database', NULL);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If Drupal is not installed or a test doesn't define database there
|
||||
// is nothing to override.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($container->findTaggedServiceIds('backend_overridable') as $id => $attributes) {
|
||||
// If the service is already an alias it is not the original backend, so
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* Adds the context provider service IDs to the context manager.
|
||||
*/
|
||||
class ContextProvidersPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Passes the service IDs of all context providers to the context repository.
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
$context_providers = [];
|
||||
foreach (array_keys($container->findTaggedServiceIds('context_provider')) as $id) {
|
||||
$context_providers[] = $id;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('context.repository');
|
||||
$definition->addArgument($context_providers);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\DependencyInjection\Compiler\GuzzleMiddlewarePass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class GuzzleMiddlewarePass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
$middleware_ids = array_keys($container->findTaggedServiceIds('http_client_middleware'));
|
||||
$container->getDefinition('http_handler_stack_configurator')
|
||||
->addArgument($middleware_ids);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\DependencyInjection\Compiler;
|
||||
|
||||
use Drupal\Component\ProxyBuilder\ProxyBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Replaces all services with a lazy flag.
|
||||
*/
|
||||
class ProxyServicesPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
foreach ($container->getDefinitions() as $service_id => $definition) {
|
||||
if ($definition->isLazy()) {
|
||||
$proxy_class = ProxyBuilder::buildProxyClassName($definition->getClass());
|
||||
if (class_exists($proxy_class)) {
|
||||
// Copy the existing definition to a new entry.
|
||||
$definition->setLazy(FALSE);
|
||||
// Ensure that the service is accessible.
|
||||
$definition->setPublic(TRUE);
|
||||
$new_service_id = 'drupal.proxy_original_service.' . $service_id;
|
||||
$container->setDefinition($new_service_id, $definition);
|
||||
|
||||
$container->register($service_id, $proxy_class)
|
||||
->setArguments([new Reference('service_container'), $new_service_id]);
|
||||
}
|
||||
else {
|
||||
$class_name = $definition->getClass();
|
||||
|
||||
// Find the root namespace.
|
||||
$match = [];
|
||||
preg_match('/([a-zA-Z0-9_]+\\\\[a-zA-Z0-9_]+)\\\\(.+)/', $class_name, $match);
|
||||
$root_namespace = $match[1];
|
||||
|
||||
// Find the root namespace path.
|
||||
$root_namespace_dir = '[namespace_root_path]';
|
||||
|
||||
$namespaces = $container->getParameter('container.namespaces');
|
||||
|
||||
// Hardcode Drupal Core, because it is not registered.
|
||||
$namespaces['Drupal\Core'] = 'core/lib/Drupal/Core';
|
||||
|
||||
if (isset($namespaces[$root_namespace])) {
|
||||
$root_namespace_dir = $namespaces[$root_namespace];
|
||||
}
|
||||
|
||||
$message =<<<EOF
|
||||
|
||||
Missing proxy class '$proxy_class' for lazy service '$service_id'.
|
||||
Use the following command to generate the proxy class:
|
||||
php core/scripts/generate-proxy-class.php '$class_name' "$root_namespace_dir"
|
||||
|
||||
|
||||
EOF;
|
||||
trigger_error($message, E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,6 @@
|
|||
namespace Drupal\Core;
|
||||
|
||||
use Drupal\Component\FileCache\FileCacheFactory;
|
||||
use Drupal\Component\ProxyBuilder\ProxyDumper;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
|
@ -24,7 +23,6 @@ use Drupal\Core\Http\TrustedHostsRequestFactory;
|
|||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\PageCache\RequestPolicyInterface;
|
||||
use Drupal\Core\PhpStorage\PhpStorageFactory;
|
||||
use Drupal\Core\ProxyBuilder\ProxyBuilder;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
@ -54,8 +52,6 @@ use Symfony\Component\Routing\Route;
|
|||
*/
|
||||
class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
||||
|
||||
const CONTAINER_BASE_CLASS = '\Drupal\Core\DependencyInjection\Container';
|
||||
|
||||
/**
|
||||
* Holds the container instance.
|
||||
*
|
||||
|
@ -128,6 +124,13 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
*/
|
||||
protected $allowDumping;
|
||||
|
||||
/**
|
||||
* Whether the container needs to be rebuilt the next time it is initialized.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $containerNeedsRebuild = FALSE;
|
||||
|
||||
/**
|
||||
* Whether the container needs to be dumped once booting is complete.
|
||||
*
|
||||
|
@ -463,8 +466,8 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
$this->container->get('request_stack')->push($request);
|
||||
|
||||
// Set the allowed protocols once we have the config available.
|
||||
$allowed_protocols = $this->container->get('config.factory')->get('system.filter')->get('protocols');
|
||||
if (!isset($allowed_protocols)) {
|
||||
$allowed_protocols = $this->container->getParameter('filter_protocols');
|
||||
if (!$allowed_protocols) {
|
||||
// \Drupal\Component\Utility\UrlHelper::filterBadProtocol() is called by
|
||||
// the installer and update.php, in which case the configuration may not
|
||||
// exist (yet). Provide a minimal default set of allowed protocols for
|
||||
|
@ -697,11 +700,13 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
}
|
||||
|
||||
// If we haven't yet booted, we don't need to do anything: the new module
|
||||
// list will take effect when boot() is called. If we have already booted,
|
||||
// then rebuild the container in order to refresh the serviceProvider list
|
||||
// and container.
|
||||
// list will take effect when boot() is called. However we set a
|
||||
// flag that the container needs a rebuild, so that a potentially cached
|
||||
// container is not used. If we have already booted, then rebuild the
|
||||
// container in order to refresh the serviceProvider list and container.
|
||||
$this->containerNeedsRebuild = TRUE;
|
||||
if ($this->booted) {
|
||||
$this->initializeContainer(TRUE);
|
||||
$this->initializeContainer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -740,11 +745,9 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
/**
|
||||
* Initializes the service container.
|
||||
*
|
||||
* @param bool $rebuild
|
||||
* Force a container rebuild.
|
||||
* @return \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected function initializeContainer($rebuild = FALSE) {
|
||||
protected function initializeContainer() {
|
||||
$this->containerNeedsDumping = FALSE;
|
||||
$session_started = FALSE;
|
||||
if (isset($this->container)) {
|
||||
|
@ -766,7 +769,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
|
||||
// If the module list hasn't already been set in updateModules and we are
|
||||
// not forcing a rebuild, then try and load the container from the disk.
|
||||
if (empty($this->moduleList) && !$rebuild) {
|
||||
if (empty($this->moduleList) && !$this->containerNeedsRebuild) {
|
||||
$fully_qualified_class_name = '\\' . $this->getClassNamespace() . '\\' . $this->getClassName();
|
||||
|
||||
// First, try to load from storage.
|
||||
|
@ -783,6 +786,9 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
$container = $this->compileContainer();
|
||||
}
|
||||
|
||||
// The container was rebuilt successfully.
|
||||
$this->containerNeedsRebuild = FALSE;
|
||||
|
||||
$this->attachSynthetic($container);
|
||||
|
||||
$this->container = $container;
|
||||
|
@ -807,7 +813,8 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
\Drupal::setContainer($this->container);
|
||||
|
||||
// If needs dumping flag was set, dump the container.
|
||||
if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, static::CONTAINER_BASE_CLASS)) {
|
||||
$base_class = Settings::get('container_base_class', '\Drupal\Core\DependencyInjection\Container');
|
||||
if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, $base_class)) {
|
||||
$this->container->get('logger.factory')->get('DrupalKernel')->notice('Container cannot be written to disk');
|
||||
}
|
||||
|
||||
|
@ -1000,15 +1007,33 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Force a container rebuild.
|
||||
*
|
||||
* @return \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rebuildContainer() {
|
||||
// Empty module properties and for them to be reloaded from scratch.
|
||||
$this->moduleList = NULL;
|
||||
$this->moduleData = array();
|
||||
return $this->initializeContainer(TRUE);
|
||||
$this->containerNeedsRebuild = TRUE;
|
||||
return $this->initializeContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateContainer() {
|
||||
// An invalidated container needs a rebuild.
|
||||
$this->containerNeedsRebuild = TRUE;
|
||||
|
||||
// If we have not yet booted, settings or bootstrap services might not yet
|
||||
// be available. In that case the container will not be loaded from cache
|
||||
// due to the above setting when the Kernel is booted.
|
||||
if (!$this->booted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Also wipe the PHP Storage caches, so that the container is rebuilt
|
||||
// for the next request.
|
||||
$this->storage()->deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1180,7 +1205,6 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
}
|
||||
// Cache the container.
|
||||
$dumper = new PhpDumper($container);
|
||||
$dumper->setProxyDumper(new ProxyDumper(new ProxyBuilder()));
|
||||
$class = $this->getClassName();
|
||||
$namespace = $this->getClassNamespace();
|
||||
$content = $dumper->dump([
|
||||
|
@ -1336,8 +1360,6 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
*
|
||||
* @return bool
|
||||
* TRUE if the hostmame is valid, or FALSE otherwise.
|
||||
*
|
||||
* @todo Adjust per resolution to https://github.com/symfony/symfony/issues/12349
|
||||
*/
|
||||
public static function validateHostname(Request $request) {
|
||||
// $request->getHost() can throw an UnexpectedValueException if it
|
||||
|
@ -1380,7 +1402,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
* @param array $host_patterns
|
||||
* The array of trusted host patterns.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* TRUE if the Host header is trusted, FALSE otherwise.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1992030
|
||||
|
|
|
@ -96,6 +96,18 @@ interface DrupalKernelInterface extends HttpKernelInterface {
|
|||
*/
|
||||
public function updateModules(array $module_list, array $module_filenames = array());
|
||||
|
||||
/**
|
||||
* Force a container rebuild.
|
||||
*
|
||||
* @return \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
public function rebuildContainer();
|
||||
|
||||
/**
|
||||
* Invalidate the service container for the next request.
|
||||
*/
|
||||
public function invalidateContainer();
|
||||
|
||||
/**
|
||||
* Prepare the kernel for handling a request without handling the request.
|
||||
*
|
||||
|
|
|
@ -138,12 +138,33 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
protected $isDefaultRevision = TRUE;
|
||||
|
||||
/**
|
||||
* Holds entity keys like the ID, bundle and revision ID.
|
||||
* Holds translatable entity keys such as the ID, bundle and revision ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $entityKeys = array();
|
||||
|
||||
/**
|
||||
* Holds translatable entity keys such as the label.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $translatableEntityKeys = array();
|
||||
|
||||
/**
|
||||
* Whether entity validation was performed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $validated = FALSE;
|
||||
|
||||
/**
|
||||
* Whether entity validation is required before saving the entity.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $validationRequired = FALSE;
|
||||
|
||||
/**
|
||||
* Overrides Entity::__construct().
|
||||
*/
|
||||
|
@ -165,14 +186,36 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
$this->values = $values;
|
||||
foreach ($this->getEntityType()->getKeys() as $key => $field_name) {
|
||||
if (isset($this->values[$field_name])) {
|
||||
if (is_array($this->values[$field_name]) && isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
|
||||
if (is_array($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
|
||||
if (isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT][0]['value'])) {
|
||||
$this->entityKeys[$key] = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT][0]['value'];
|
||||
if (is_array($this->values[$field_name])) {
|
||||
// We store untranslatable fields into an entity key without using a
|
||||
// langcode key.
|
||||
if (!$this->getFieldDefinition($field_name)->isTranslatable()) {
|
||||
if (isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
|
||||
if (is_array($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
|
||||
if (isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT][0]['value'])) {
|
||||
$this->entityKeys[$key] = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT][0]['value'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->entityKeys[$key] = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->entityKeys[$key] = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT];
|
||||
// We save translatable fields such as the publishing status of a node
|
||||
// into an entity key array keyed by langcode as a performance
|
||||
// optimization, so we don't have to go through TypedData when we
|
||||
// need these values.
|
||||
foreach ($this->values[$field_name] as $langcode => $field_value) {
|
||||
if (is_array($this->values[$field_name][$langcode])) {
|
||||
if (isset($this->values[$field_name][$langcode][0]['value'])) {
|
||||
$this->translatableEntityKeys[$key][$langcode] = $this->values[$field_name][$langcode][0]['value'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->translatableEntityKeys[$key][$langcode] = $this->values[$field_name][$langcode];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +263,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
*/
|
||||
public function setNewRevision($value = TRUE) {
|
||||
if (!$this->getEntityType()->hasKey('revision')) {
|
||||
throw new \LogicException(SafeMarkup::format('Entity type @entity_type does not support revisions.', ['@entity_type' => $this->getEntityTypeId()]));
|
||||
throw new \LogicException("Entity type {$this->getEntityTypeId()} does not support revisions.");
|
||||
}
|
||||
|
||||
if ($value && !$this->newRevision) {
|
||||
|
@ -302,6 +345,23 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
return !empty($bundles[$this->bundle()]['translatable']) && !$this->getUntranslated()->language()->isLocked() && $this->languageManager()->isMultilingual();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
// An entity requiring validation should not be saved if it has not been
|
||||
// actually validated.
|
||||
if ($this->validationRequired && !$this->validated) {
|
||||
// @todo Make this an assertion in https://www.drupal.org/node/2408013.
|
||||
throw new \LogicException('Entity validation was skipped.');
|
||||
}
|
||||
else {
|
||||
$this->validated = FALSE;
|
||||
}
|
||||
|
||||
parent::preSave($storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -312,10 +372,26 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate() {
|
||||
$this->validated = TRUE;
|
||||
$violations = $this->getTypedData()->validate();
|
||||
return new EntityConstraintViolationList($this, iterator_to_array($violations));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValidationRequired() {
|
||||
return (bool) $this->validationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValidationRequired($required) {
|
||||
$this->validationRequired = $required;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear entity translation object cache to remove stale references.
|
||||
*/
|
||||
|
@ -388,15 +464,14 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
*/
|
||||
protected function getTranslatedField($name, $langcode) {
|
||||
if ($this->translations[$this->activeLangcode]['status'] == static::TRANSLATION_REMOVED) {
|
||||
$message = 'The entity object refers to a removed translation (@langcode) and cannot be manipulated.';
|
||||
throw new \InvalidArgumentException(SafeMarkup::format($message, array('@langcode' => $this->activeLangcode)));
|
||||
throw new \InvalidArgumentException("The entity object refers to a removed translation ({$this->activeLangcode}) and cannot be manipulated.");
|
||||
}
|
||||
// Populate $this->fields to speed-up further look-ups and to keep track of
|
||||
// fields objects, possibly holding changes to field values.
|
||||
if (!isset($this->fields[$name][$langcode])) {
|
||||
$definition = $this->getFieldDefinition($name);
|
||||
if (!$definition) {
|
||||
throw new \InvalidArgumentException('Field ' . SafeMarkup::checkPlain($name) . ' is unknown.');
|
||||
throw new \InvalidArgumentException("Field $name is unknown.");
|
||||
}
|
||||
// Non-translatable fields are always stored with
|
||||
// LanguageInterface::LANGCODE_DEFAULT as key.
|
||||
|
@ -413,7 +488,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
if (isset($this->values[$name][$langcode])) {
|
||||
$value = $this->values[$name][$langcode];
|
||||
}
|
||||
$field = \Drupal::service('plugin.manager.field.field_type')->createFieldItemList($this, $name, $value);
|
||||
$field = \Drupal::service('plugin.manager.field.field_type')->createFieldItemList($this->getTranslation($langcode), $name, $value);
|
||||
if ($default) {
|
||||
// $this->defaultLangcode might not be set if we are initializing the
|
||||
// default language code cache, in which case there is no valid
|
||||
|
@ -454,6 +529,19 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslatableFields($include_computed = TRUE) {
|
||||
$fields = [];
|
||||
foreach ($this->getFieldDefinitions() as $name => $definition) {
|
||||
if (($include_computed || !$definition->isComputed()) && $definition->isTranslatable()) {
|
||||
$fields[$name] = $this->get($name);
|
||||
}
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -537,12 +625,12 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
// Get the language code if the property exists.
|
||||
// Try to read the value directly from the list of entity keys which got
|
||||
// initialized in __construct(). This avoids creating a field item object.
|
||||
if (isset($this->entityKeys['langcode'])) {
|
||||
$this->defaultLangcode = $this->entityKeys['langcode'];
|
||||
if (isset($this->translatableEntityKeys['langcode'][$this->activeLangcode])) {
|
||||
$this->defaultLangcode = $this->translatableEntityKeys['langcode'][$this->activeLangcode];
|
||||
}
|
||||
elseif ($this->hasField($this->langcodeKey) && ($item = $this->get($this->langcodeKey)) && isset($item->language)) {
|
||||
$this->defaultLangcode = $item->language->getId();
|
||||
$this->entityKeys['langcode'] = $this->defaultLangcode;
|
||||
$this->translatableEntityKeys['langcode'][$this->activeLangcode] = $this->defaultLangcode;
|
||||
}
|
||||
|
||||
if (empty($this->defaultLangcode)) {
|
||||
|
@ -583,8 +671,13 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
// that check, as it ready only and must not change, unsetting it could
|
||||
// lead to recursions.
|
||||
if ($key = array_search($name, $this->getEntityType()->getKeys())) {
|
||||
if (isset($this->entityKeys[$key]) && $key != 'bundle') {
|
||||
unset($this->entityKeys[$key]);
|
||||
if ($key != 'bundle') {
|
||||
if (isset($this->entityKeys[$key])) {
|
||||
unset($this->entityKeys[$key]);
|
||||
}
|
||||
elseif (isset($this->translatableEntityKeys[$key][$this->activeLangcode])) {
|
||||
unset($this->translatableEntityKeys[$key][$this->activeLangcode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -663,8 +756,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
}
|
||||
|
||||
if (empty($translation)) {
|
||||
$message = 'Invalid translation language (@langcode) specified.';
|
||||
throw new \InvalidArgumentException(SafeMarkup::format($message, array('@langcode' => $langcode)));
|
||||
throw new \InvalidArgumentException("Invalid translation language ($langcode) specified.");
|
||||
}
|
||||
|
||||
return $translation;
|
||||
|
@ -710,8 +802,6 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
$translation->enforceIsNew = &$this->enforceIsNew;
|
||||
$translation->newRevision = &$this->newRevision;
|
||||
$translation->translationInitialize = FALSE;
|
||||
// Reset language-dependent properties.
|
||||
unset($translation->entityKeys['label']);
|
||||
$translation->typedData = NULL;
|
||||
|
||||
return $translation;
|
||||
|
@ -733,8 +823,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
public function addTranslation($langcode, array $values = array()) {
|
||||
$this->getLanguages();
|
||||
if (!isset($this->languages[$langcode]) || $this->hasTranslation($langcode)) {
|
||||
$message = 'Invalid translation language (@langcode) specified.';
|
||||
throw new \InvalidArgumentException(SafeMarkup::format($message, array('@langcode' => $langcode)));
|
||||
throw new \InvalidArgumentException("Invalid translation language ($langcode) specified.");
|
||||
}
|
||||
|
||||
// Instantiate a new empty entity so default values will be populated in the
|
||||
|
@ -784,8 +873,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
$this->translations[$langcode]['status'] = static::TRANSLATION_REMOVED;
|
||||
}
|
||||
else {
|
||||
$message = 'The specified translation (@langcode) cannot be removed.';
|
||||
throw new \InvalidArgumentException(SafeMarkup::format($message, array('@langcode' => $langcode)));
|
||||
throw new \InvalidArgumentException("The specified translation ($langcode) cannot be removed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -918,8 +1006,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
*/
|
||||
public function createDuplicate() {
|
||||
if ($this->translations[$this->activeLangcode]['status'] == static::TRANSLATION_REMOVED) {
|
||||
$message = 'The entity object refers to a removed translation (@langcode) and cannot be manipulated.';
|
||||
throw new \InvalidArgumentException(SafeMarkup::format($message, array('@langcode' => $this->activeLangcode)));
|
||||
throw new \InvalidArgumentException("The entity object refers to a removed translation ({$this->activeLangcode}) and cannot be manipulated.");
|
||||
}
|
||||
|
||||
$duplicate = clone $this;
|
||||
|
@ -962,7 +1049,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
}
|
||||
foreach ($values as $langcode => $items) {
|
||||
$this->fields[$name][$langcode] = clone $items;
|
||||
$this->fields[$name][$langcode]->setContext($name, $this->getTypedData());
|
||||
$this->fields[$name][$langcode]->setContext($name, $this->getTranslation($langcode)->getTypedData());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1020,18 +1107,34 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
* The value of the entity key, NULL if not defined.
|
||||
*/
|
||||
protected function getEntityKey($key) {
|
||||
if (!isset($this->entityKeys[$key]) || !array_key_exists($key, $this->entityKeys)) {
|
||||
if ($this->getEntityType()->hasKey($key)) {
|
||||
$field_name = $this->getEntityType()->getKey($key);
|
||||
$property = $this->getFieldDefinition($field_name)->getFieldStorageDefinition()->getMainPropertyName();
|
||||
$this->entityKeys[$key] = $this->get($field_name)->$property;
|
||||
// If the value is known already, return it.
|
||||
if (isset($this->entityKeys[$key])) {
|
||||
return $this->entityKeys[$key];
|
||||
}
|
||||
if (isset($this->translatableEntityKeys[$key][$this->activeLangcode])) {
|
||||
return $this->translatableEntityKeys[$key][$this->activeLangcode];
|
||||
}
|
||||
|
||||
// Otherwise fetch the value by creating a field object.
|
||||
$value = NULL;
|
||||
if ($this->getEntityType()->hasKey($key)) {
|
||||
$field_name = $this->getEntityType()->getKey($key);
|
||||
$definition = $this->getFieldDefinition($field_name);
|
||||
$property = $definition->getFieldStorageDefinition()->getMainPropertyName();
|
||||
$value = $this->get($field_name)->$property;
|
||||
|
||||
// Put it in the right array, depending on whether it is translatable.
|
||||
if ($definition->isTranslatable()) {
|
||||
$this->translatableEntityKeys[$key][$this->activeLangcode] = $value;
|
||||
}
|
||||
else {
|
||||
$this->entityKeys[$key] = NULL;
|
||||
$this->entityKeys[$key] = $value;
|
||||
}
|
||||
|
||||
}
|
||||
return $this->entityKeys[$key];
|
||||
else {
|
||||
$this->entityKeys[$key] = $value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -86,9 +86,6 @@ abstract class ContentEntityConfirmFormBase extends ContentEntityForm implements
|
|||
'submit' => array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->getConfirmText(),
|
||||
'#validate' => array(
|
||||
array($this, 'validate'),
|
||||
),
|
||||
'#submit' => array(
|
||||
array($this, 'submitForm'),
|
||||
),
|
||||
|
@ -121,9 +118,10 @@ abstract class ContentEntityConfirmFormBase extends ContentEntityForm implements
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array $form, FormStateInterface $form_state) {
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
// Override the default validation implementation as it is not necessary
|
||||
// nor possible to validate an entity in a confirmation form.
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,17 +64,28 @@ class ContentEntityForm extends EntityForm implements ContentEntityFormInterface
|
|||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Note that extending classes should not override this method to add entity
|
||||
* validation logic, but define further validation constraints using the
|
||||
* entity validation API and/or provide a new validation constraint if
|
||||
* necessary. This is the only way to ensure that the validation logic
|
||||
* is correctly applied independently of form submissions; e.g., for REST
|
||||
* requests.
|
||||
* For more information about entity validation, see
|
||||
* https://www.drupal.org/node/2015613.
|
||||
*/
|
||||
public function validate(array $form, FormStateInterface $form_state) {
|
||||
public function buildEntity(array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = parent::buildEntity($form, $form_state);
|
||||
|
||||
// Mark the entity as requiring validation.
|
||||
$entity->setValidationRequired(!$form_state->getTemporaryValue('entity_validated'));
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Button-level validation handlers are highly discouraged for entity forms,
|
||||
* as they will prevent entity validation from running. If the entity is going
|
||||
* to be saved during the form submission, this method should be manually
|
||||
* invoked from the button-level validation handler, otherwise an exception
|
||||
* will be thrown.
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $this->buildEntity($form, $form_state);
|
||||
|
||||
|
@ -87,10 +98,10 @@ class ContentEntityForm extends EntityForm implements ContentEntityFormInterface
|
|||
|
||||
$this->flagViolations($violations, $form, $form_state);
|
||||
|
||||
// @todo Remove this.
|
||||
// Execute legacy global validation handlers.
|
||||
$form_state->setValidateHandlers([]);
|
||||
\Drupal::service('form_validator')->executeValidateHandlers($form, $form_state);
|
||||
// The entity was validated.
|
||||
$entity->setValidationRequired(FALSE);
|
||||
$form_state->setTemporaryValue('entity_validated', TRUE);
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue