Commit 20564b99 authored by catch's avatar catch

Issue #2282119 by amateescu, jibran, Jo Fitzgerald, yched, plach, timmillwood,...

Issue #2282119 by amateescu, jibran, Jo Fitzgerald, yched, plach, timmillwood, fago, Sam152, larowlan: Make the Entity Field API handle field purging
parent dbc01628
......@@ -510,12 +510,6 @@ services:
- { name: module_install.uninstall_validator }
arguments: ['@entity.manager', '@string_translation']
lazy: true
field_uninstall_validator:
class: Drupal\Core\Field\FieldModuleUninstallValidator
tags:
- { name: module_install.uninstall_validator }
arguments: ['@entity.manager', '@string_translation']
lazy: true
required_module_uninstall_validator:
class: Drupal\Core\Extension\RequiredModuleUninstallValidator
tags:
......@@ -576,9 +570,12 @@ services:
entity.last_installed_schema.repository:
class: Drupal\Core\Entity\EntityLastInstalledSchemaRepository
arguments: ['@keyvalue']
entity_field.deleted_fields_repository:
class: Drupal\Core\Field\DeletedFieldsRepository
arguments: ['@state']
field_storage_definition.listener:
class: Drupal\Core\Field\FieldStorageDefinitionListener
arguments: ['@entity_type.manager', '@event_dispatcher', '@entity.last_installed_schema.repository', '@entity_field.manager']
arguments: ['@entity_type.manager', '@event_dispatcher', '@entity.last_installed_schema.repository', '@entity_field.manager', '@entity_field.deleted_fields_repository']
field_definition.listener:
class: Drupal\Core\Field\FieldDefinitionListener
arguments: ['@entity_type.manager', '@entity_field.manager', '@keyvalue', '@cache.discovery']
......
......@@ -2,9 +2,7 @@
namespace Drupal\Core\Entity;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldDefinitionListenerInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
/**
......@@ -18,27 +16,4 @@
*/
interface DynamicallyFieldableEntityStorageInterface extends FieldableEntityStorageInterface, FieldStorageDefinitionListenerInterface, FieldDefinitionListenerInterface {
/**
* Purges a batch of field data.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The deleted field whose data is being purged.
* @param $batch_size
* The maximum number of field data records to purge before returning,
* relating to the count of field data records returned by
* \Drupal\Core\Entity\FieldableEntityStorageInterface::countFieldData().
*
* @return int
* The number of field data records that have been purged.
*/
public function purgeFieldData(FieldDefinitionInterface $field_definition, $batch_size);
/**
* Performs final cleanup after all data of a field has been purged.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
* The field being purged.
*/
public function finalizePurge(FieldStorageDefinitionInterface $storage_definition);
}
......@@ -311,7 +311,8 @@ protected function getChangeList() {
}
// @todo Support deleting entity definitions when we support base field
// purging. See https://www.drupal.org/node/2282119.
// purging.
// @see https://www.drupal.org/node/2907779
$this->entityManager->useCaches(TRUE);
......
......@@ -2,6 +2,9 @@
namespace Drupal\Core\Entity;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
* A storage that supports entity types with field definitions.
*/
......@@ -24,4 +27,27 @@ interface FieldableEntityStorageInterface extends EntityStorageInterface {
*/
public function countFieldData($storage_definition, $as_bool = FALSE);
/**
* Purges a batch of field data.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The deleted field whose data is being purged.
* @param int $batch_size
* The maximum number of field data records to purge before returning,
* relating to the count of field data records returned by
* \Drupal\Core\Entity\FieldableEntityStorageInterface::countFieldData().
*
* @return int
* The number of field data records that have been purged.
*/
public function purgeFieldData(FieldDefinitionInterface $field_definition, $batch_size);
/**
* Performs final cleanup after all data of a field has been purged.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
* The field storage being purged.
*/
public function finalizePurge(FieldStorageDefinitionInterface $storage_definition);
}
......@@ -272,7 +272,7 @@ public function setExtraColumns($table_name, array $column_names) {
* TRUE if the field can be stored in a dedicated table, FALSE otherwise.
*/
public function allowsSharedTableStorage(FieldStorageDefinitionInterface $storage_definition) {
return !$storage_definition->hasCustomStorage() && $storage_definition->isBaseField() && !$storage_definition->isMultiple();
return !$storage_definition->hasCustomStorage() && $storage_definition->isBaseField() && !$storage_definition->isMultiple() && !$storage_definition->isDeleted();
}
/**
......@@ -331,8 +331,8 @@ public function getReservedColumns() {
public function getDedicatedDataTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) {
if ($is_deleted) {
// When a field is a deleted, the table is renamed to
// {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
// table names longer than 64 characters, we hash the unique storage
// {field_deleted_data_UNIQUE_STORAGE_ID}. To make sure we don't end up
// with table names longer than 64 characters, we hash the unique storage
// identifier and return the first 10 characters so we end up with a short
// unique ID.
return "field_deleted_data_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
......@@ -357,10 +357,10 @@ public function getDedicatedDataTableName(FieldStorageDefinitionInterface $stora
public function getDedicatedRevisionTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) {
if ($is_deleted) {
// When a field is a deleted, the table is renamed to
// {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with
// table names longer than 64 characters, we hash the unique storage
// identifier and return the first 10 characters so we end up with a short
// unique ID.
// {field_deleted_revision_UNIQUE_STORAGE_ID}. To make sure we don't end
// up with table names longer than 64 characters, we hash the unique
// storage identifier and return the first 10 characters so we end up with
// a short unique ID.
return "field_deleted_revision_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
}
else {
......@@ -389,7 +389,7 @@ protected function generateFieldTableName(FieldStorageDefinitionInterface $stora
// prefixes.
if (strlen($table_name) > 48) {
// Use a shorter separator, a truncated entity_type, and a hash of the
// field UUID.
// field storage unique identifier.
$separator = $revision ? '_r__' : '__';
// Truncate to the same length for the current and revision tables.
$entity_type = substr($storage_definition->getTargetEntityTypeId(), 0, 34);
......
......@@ -19,7 +19,6 @@
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\field\FieldStorageConfigInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -1468,9 +1467,7 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
$this->entityManager->getLastInstalledFieldStorageDefinitions($this->entityType->id())
);
// @todo Remove the FieldStorageConfigInterface check when non-configurable
// fields support purging: https://www.drupal.org/node/2282119.
if ($storage_definition instanceof FieldStorageConfigInterface && $table_mapping->requiresDedicatedTableStorage($storage_definition)) {
if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) {
// Mark all data associated with the field for deletion.
$table = $table_mapping->getDedicatedDataTableName($storage_definition);
$revision_table = $table_mapping->getDedicatedRevisionTableName($storage_definition);
......@@ -1554,9 +1551,8 @@ protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definit
// Check whether the whole field storage definition is gone, or just some
// bundle fields.
$storage_definition = $field_definition->getFieldStorageDefinition();
$is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
$table_mapping = $this->getTableMapping();
$table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
$table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $storage_definition->isDeleted());
// Get the entities which we want to purge first.
$entity_query = $this->database->select($table_name, 't', ['fetch' => \PDO::FETCH_ASSOC]);
......@@ -1614,7 +1610,7 @@ protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definit
*/
protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition) {
$storage_definition = $field_definition->getFieldStorageDefinition();
$is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
$is_deleted = $storage_definition->isDeleted();
$table_mapping = $this->getTableMapping();
$table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
$revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted);
......@@ -1651,7 +1647,7 @@ public function countFieldData($storage_definition, $as_bool = FALSE) {
$table_mapping = $this->getTableMapping($storage_definitions);
if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) {
$is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
$is_deleted = $storage_definition->isDeleted();
if ($this->entityType->isRevisionable()) {
$table_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted);
}
......@@ -1724,15 +1720,14 @@ public function countFieldData($storage_definition, $as_bool = FALSE) {
*
* @return bool
* Whether the field has been already deleted.
*
* @deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use
* \Drupal\Core\Field\FieldStorageDefinitionInterface::isDeleted() instead.
*
* @see https://www.drupal.org/node/2907785
*/
protected function storageDefinitionIsDeleted(FieldStorageDefinitionInterface $storage_definition) {
// Configurable fields are marked for deletion.
if ($storage_definition instanceof FieldStorageConfigInterface) {
return $storage_definition->isDeleted();
}
// For non configurable fields check whether they are still in the last
// installed schema repository.
return !array_key_exists($storage_definition->getName(), $this->entityManager->getLastInstalledFieldStorageDefinitions($this->entityTypeId));
return $storage_definition->isDeleted();
}
}
......@@ -415,16 +415,11 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
$update_manager->uninstallEntityType($entity_type);
}
elseif ($entity_type->entityClassImplements(FieldableEntityInterface::CLASS)) {
// The module being installed may be adding new fields to existing
// entity types. Field definitions for any entity type defined by
// the module are handled in the if branch.
$entity_type_id = $entity_type->id();
/** @var \Drupal\Core\Entity\FieldableEntityStorageInterface $storage */
$storage = $entity_manager->getStorage($entity_type_id);
foreach ($entity_manager->getFieldStorageDefinitions($entity_type_id) as $storage_definition) {
// @todo We need to trigger field purging here.
// See https://www.drupal.org/node/2282119.
if ($storage_definition->getProvider() == $module && !$storage->countFieldData($storage_definition, TRUE)) {
// The module being uninstalled might have added new fields to
// existing entity types. This will add them to the deleted fields
// repository so their data will be purged on cron.
foreach ($entity_manager->getFieldStorageDefinitions($entity_type->id()) as $storage_definition) {
if ($storage_definition->getProvider() == $module) {
$update_manager->uninstallFieldStorageDefinition($storage_definition);
}
}
......
......@@ -801,6 +801,39 @@ public function getUniqueStorageIdentifier() {
return $this->getTargetEntityTypeId() . '-' . $this->getName();
}
/**
* {@inheritdoc}
*/
public function getUniqueIdentifier() {
// If we have a specified target bundle, we're dealing with a bundle base
// field definition, so we need to include it in the unique identifier.
if ($this->getTargetBundle()) {
return $this->getTargetEntityTypeId() . '-' . $this->getTargetBundle() . '-' . $this->getName();
}
return $this->getUniqueStorageIdentifier();
}
/**
* {@inheritdoc}
*/
public function isDeleted() {
return !empty($this->definition['deleted']);
}
/**
* Sets whether the field storage is deleted.
*
* @param bool $deleted
* Whether the field storage is deleted.
*
* @return $this
*/
public function setDeleted($deleted) {
$this->definition['deleted'] = $deleted;
return $this;
}
/**
* {@inheritdoc}
*/
......
<?php
namespace Drupal\Core\Field;
use Drupal\Core\State\StateInterface;
/**
* Provides a repository for deleted field and field storage objects.
*
* @internal
*/
class DeletedFieldsRepository implements DeletedFieldsRepositoryInterface {
/**
* The state key/value store.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs a new deleted fields repository.
*
* @param \Drupal\Core\State\StateInterface $state
* The state key/value store.
*/
public function __construct(StateInterface $state) {
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public function getFieldDefinitions($field_storage_unique_id = NULL) {
$deleted_field_definitions = $this->state->get('field.field.deleted', []);
if ($field_storage_unique_id) {
$deleted_field_definitions = array_filter($deleted_field_definitions, function (FieldDefinitionInterface $field_definition) use ($field_storage_unique_id) {
return $field_definition->getFieldStorageDefinition()->getUniqueStorageIdentifier() === $field_storage_unique_id;
});
}
return $deleted_field_definitions;
}
/**
* {@inheritdoc}
*/
public function getFieldStorageDefinitions() {
return $this->state->get('field.storage.deleted', []);
}
/**
* {@inheritdoc}
*/
public function addFieldDefinition(FieldDefinitionInterface $field_definition) {
$deleted_field_definitions = $this->state->get('field.field.deleted', []);
$deleted_field_definitions[$field_definition->getUniqueIdentifier()] = $field_definition;
$this->state->set('field.field.deleted', $deleted_field_definitions);
return $this;
}
/**
* {@inheritdoc}
*/
public function addFieldStorageDefinition(FieldStorageDefinitionInterface $field_storage_definition) {
$deleted_storage_definitions = $this->state->get('field.storage.deleted', []);
$deleted_storage_definitions[$field_storage_definition->getUniqueStorageIdentifier()] = $field_storage_definition;
$this->state->set('field.storage.deleted', $deleted_storage_definitions);
return $this;
}
/**
* {@inheritdoc}
*/
public function removeFieldDefinition(FieldDefinitionInterface $field_definition) {
$deleted_field_definitions = $this->state->get('field.field.deleted', []);;
unset($deleted_field_definitions[$field_definition->getUniqueIdentifier()]);
$this->state->set('field.field.deleted', $deleted_field_definitions);
return $this;
}
/**
* {@inheritdoc}
*/
public function removeFieldStorageDefinition(FieldStorageDefinitionInterface $field_storage_definition) {
$deleted_storage_definitions = $this->state->get('field.storage.deleted', []);
unset($deleted_storage_definitions[$field_storage_definition->getUniqueStorageIdentifier()]);
$this->state->set('field.storage.deleted', $deleted_storage_definitions);
return $this;
}
}
<?php
namespace Drupal\Core\Field;
/**
* Provides an interface for a deleted fields repository.
*
* @internal
*/
interface DeletedFieldsRepositoryInterface {
/**
* Returns a list of deleted field definitions.
*
* @param string $field_storage_unique_id
* (optional) A unique ID of field storage definition for filtering the
* deleted fields. Defaults to NULL.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* An array of field definition objects, keyed by their unique identifier.
*/
public function getFieldDefinitions($field_storage_unique_id = NULL);
/**
* Returns a list of deleted field storage definitions.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
* An array of field storage definition objects, keyed by their unique
* storage identifier.
*/
public function getFieldStorageDefinitions();
/**
* Adds a field definition object to the deleted list.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* A field definition object.
*
* @return $this
*/
public function addFieldDefinition(FieldDefinitionInterface $field_definition);
/**
* Adds a field storage definition object to the deleted list.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
* A field storage definition object.
*
* @return $this
*/
public function addFieldStorageDefinition(FieldStorageDefinitionInterface $field_storage_definition);
/**
* Removes a field definition object from the deleted list.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* A field definition object.
*
* @return $this
*/
public function removeFieldDefinition(FieldDefinitionInterface $field_definition);
/**
* Removes a field storage definition object from the deleted list.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
* A field storage definition object.
*
* @return $this
*/
public function removeFieldStorageDefinition(FieldStorageDefinitionInterface $field_storage_definition);
}
......@@ -149,6 +149,13 @@ public function getClass() {
return $this->getBaseFieldDefinition()->getClass();
}
/**
* {@inheritdoc}
*/
public function getUniqueIdentifier() {
return $this->getBaseFieldDefinition()->getUniqueIdentifier();
}
/**
* Gets the base field definition.
*
......
......@@ -259,4 +259,11 @@ public function getFieldStorageDefinition();
*/
public function getConfig($bundle);
/**
* Returns a unique identifier for the field.
*
* @return string
*/
public function getUniqueIdentifier();
}
<?php
namespace Drupal\Core\Field;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\FieldableEntityStorageInterface;
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
/**
* Validates module uninstall readiness based on defined storage definitions.
*
* @todo Remove this once we support field purging for base fields. See
* https://www.drupal.org/node/2282119.
*/
class FieldModuleUninstallValidator implements ModuleUninstallValidatorInterface {
use StringTranslationTrait;
/**
* Constructs the object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
*/
public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
$this->entityManager = $entity_manager;
$this->stringTranslation = $string_translation;
}
/**
* {@inheritdoc}
*/
public function validate($module_name) {
$reasons = [];
// We skip fields provided by the Field module as it implements field
// purging.
if ($module_name != 'field') {
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
// We skip entity types defined by the module as there must be no
// content to be able to uninstall them anyway.
// See \Drupal\Core\Entity\ContentUninstallValidator.
if ($entity_type->getProvider() != $module_name && $entity_type->entityClassImplements(FieldableEntityInterface::class)) {
foreach ($this->entityManager->getFieldStorageDefinitions($entity_type_id) as $storage_definition) {
if ($storage_definition->getProvider() == $module_name) {
$storage = $this->entityManager->getStorage($entity_type_id);
if ($storage instanceof FieldableEntityStorageInterface && $storage->countFieldData($storage_definition, TRUE)) {
$reasons[] = $this->t('There is data for the field @field-name on entity type @entity_type', [
'@field-name' => $storage_definition->getName(),
'@entity_type' => $entity_type->getLabel(),
]);
}
}
}
}
}
}
return $reasons;
}
}
......@@ -333,10 +333,18 @@ public function hasCustomStorage();
public function isBaseField();
/**
* Returns a unique identifier for the field.
* Returns a unique identifier for the field storage.
*
* @return string
*/
public function getUniqueStorageIdentifier();
/**
* Returns whether the field is deleted or not.
*
* @return bool
* TRUE if the field is deleted, FALSE otherwise.
*/
public function isDeleted();
}
......@@ -2,9 +2,11 @@
namespace Drupal\Core\Field;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityStorageInterface;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
......@@ -43,6 +45,13 @@ class FieldStorageDefinitionListener implements FieldStorageDefinitionListenerIn
*/
protected $entityFieldManager;
/**
* The deleted fields repository.
*
* @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface
*/
protected $deletedFieldsRepository;
/**
* Constructs a new FieldStorageDefinitionListener.
*
......@@ -54,12 +63,15 @@ class FieldStorageDefinitionListener implements FieldStorageDefinitionListenerIn
* The entity last installed schema repository.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Field\DeletedFieldsRepositoryInterface $deleted_fields_repository
* The deleted fields repository.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository, EntityFieldManagerInterface $entity_field_manager) {
public function __construct(EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository, EntityFieldManagerInterface $entity_field_manager, DeletedFieldsRepositoryInterface $deleted_fields_repository) {
$this->entityTypeManager = $entity_type_manager;
$this->eventDispatcher = $event_dispatcher;
$this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
$this->entityFieldManager = $entity_field_manager;
$this->deletedFieldsRepository = $deleted_fields_repository;
}
/**
......@@ -139,6 +151,23 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
$storage->setEntityType($last_installed_entity_type);
}
// Keep the field definition in the deleted fields repository so we can use
// it later during field_purge_batch(), but only if the field has data.
try {
if ($storage_definition instanceof BaseFieldDefinition && $storage instanceof FieldableEntityStorageInterface && $storage->countFieldData($storage_definition, TRUE)) {
$deleted_storage_definition = clone $storage_definition;
$deleted_storage_definition->setDeleted(TRUE);
$this->deletedFieldsRepository->addFieldDefinition($deleted_storage_definition);
$this->deletedFieldsRepository->addFieldStorageDefinition($deleted_storage_definition);
}
}
catch (DatabaseExceptionWrapper $e) {
// This may happen when changing field storage schema, since we are not
// able to use a table mapping matching the passed storage definition.
// @todo Revisit this once we are able to instantiate the table mapping
// properly. See https://www.drupal.org/node/2274017.
}
if ($storage instanceof FieldStorageDefinitionListenerInterface) {
$storage->onFieldStorageDefinitionDelete($storage_definition);
}
......
<?php
// @codingStandardsIgnoreFile
/**
* This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\Core\Field\FieldModuleUninstallValidator' "core/lib/Drupal/Core".
*/
namespace Drupal\Core\ProxyClass\Field {
/**
* Provides a proxy class for \Drupal\Core\Field\FieldModuleUninstallValidator.
*
* @see \Drupal\Component\ProxyBuilder
*/
class FieldModuleUninstallValidator implements \Drupal\Core\Extension\ModuleUninstallValidatorInterface
{
use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
/**
* The id of the original proxied service.
*
* @var string
*/
protected $drupalProxyOriginalServiceId;
/**
* The real proxied service, after it was lazy loaded.
*
* @var \Drupal\Core\Field\FieldModuleUninstallValidator
*/
protected $service;
/**
* The service container.
*
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
/**
* Constructs a ProxyClass Drupal proxy object.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The container.
* @param string $drupal_proxy_original_service_id
* The service ID of the original service.
*/
public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
{
$this->container = $container;
$this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
}
/**
* Lazy loads the real service from the container.
*
* @return object
* Returns the constructed real service.
*/