You are here

class StateManager in Field Encryption 3.0.x

Manages state for the module.

The module state includes:

  • the list of entity types with encrypted fields.
  • the installation and removal of the encrypted_field_storage base field.
  • the management of the entity definitions.

Hierarchy

Expanded class hierarchy of StateManager

2 files declare their use of StateManager
ConfigSubscriber.php in src/EventSubscriber/ConfigSubscriber.php
field_encrypt.module in ./field_encrypt.module
Contains module hooks for field_encrypt.
1 string reference to 'StateManager'
field_encrypt.services.yml in ./field_encrypt.services.yml
field_encrypt.services.yml
1 service uses StateManager
field_encrypt.state_manager in ./field_encrypt.services.yml
Drupal\field_encrypt\StateManager

File

src/StateManager.php, line 24

Namespace

Drupal\field_encrypt
View source
class StateManager {

  /**
   * The entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The queue factory.
   *
   * @var \Drupal\Core\Queue\QueueFactory
   */
  protected $queueFactory;

  /**
   * The state key value store.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * The entity last installed schema repository.
   *
   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
   */
  protected $entitySchemaRepository;

  /**
   * The entity definition update manager.
   *
   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
   */
  protected $entityDefinitionUpdateManager;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * Constructs a new ConfigSubscriber object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager service.
   * @param \Drupal\Core\Queue\QueueFactory $queue_factory
   *   The queue factory.
   * @param \Drupal\Core\State\StateInterface $state
   *   The state key value store.
   * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_schema_repository
   *   The entity last installed schema repository.
   * @param \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $entity_definition_update_manager
   *   The entity definition update manager.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, QueueFactory $queue_factory, StateInterface $state, EntityLastInstalledSchemaRepositoryInterface $entity_schema_repository, EntityDefinitionUpdateManagerInterface $entity_definition_update_manager, ModuleHandlerInterface $module_handler) {
    $this->entityTypeManager = $entity_type_manager;
    $this->queueFactory = $queue_factory;
    $this->state = $state;
    $this->entitySchemaRepository = $entity_schema_repository;
    $this->entityDefinitionUpdateManager = $entity_definition_update_manager;
    $this->moduleHandler = $module_handler;
  }

  /**
   * Figure out which entity types are encrypted.
   */
  public function update() {
    $old_entity_types = $this->state
      ->get('field_encrypt.entity_types', []);
    $new_entity_types = $this
      ->getEntityTypes();
    if ($old_entity_types === $new_entity_types) {

      // No changes to make. Early return to do nothing and preserve caches.
      return;
    }

    // Get entities where we need to add a field.
    foreach (array_diff($new_entity_types, $old_entity_types) as $type) {
      $definition = static::getEncryptedFieldStorageDefinition();
      $this->entityDefinitionUpdateManager
        ->installFieldStorageDefinition(ProcessEntities::ENCRYPTED_FIELD_STORAGE_NAME, $type, 'field_encrypt', $definition);
    }

    // We can't remove the field if there are queue items to process because if
    // there is data we'll destroy it. So merge in the old entity types.
    $this->state
      ->set('field_encrypt.entity_types', array_merge($old_entity_types, $new_entity_types));

    // @see field_encrypt.module
    $this->moduleHandler
      ->resetImplementations();

    // @see field_encrypt_entity_type_alter()
    $this->entityTypeManager
      ->clearCachedDefinitions();
    $this
      ->setEntityTypeCacheInformation($new_entity_types);
  }

  /**
   * Reacts to field_encrypt.settings:make_entities_uncacheable changes.
   *
   * @return static
   */
  public function onFieldEncryptSettingsCacheChange() {
    $this->entityTypeManager
      ->clearCachedDefinitions();
    $this
      ->setEntityTypeCacheInformation($this->state
      ->get('field_encrypt.entity_types', []));
    return $this;
  }

  /**
   * Sets the last installed entity cache information correctly.
   *
   * @param string[] $entity_type_ids
   *   The entity type IDs to set the cache information for.
   *
   * @see field_encrypt_entity_type_alter()
   */
  protected function setEntityTypeCacheInformation(array $entity_type_ids) {
    $entity_types = $this->entityTypeManager
      ->getDefinitions();

    // Types that have changed need to have their last installed definition
    // updated. We need to be careful to only change the settings we are
    // interested in.
    foreach ($entity_type_ids as $type) {
      $last_installed_definition = $this->entitySchemaRepository
        ->getLastInstalledDefinition($type);
      $last_installed_definition
        ->set('render_cache', $entity_types[$type]
        ->get('render_cache') ?? FALSE)
        ->set('persistent_cache', $entity_types[$type]
        ->get('persistent_cache') ?? FALSE);
      $this->entitySchemaRepository
        ->setLastInstalledDefinition($last_installed_definition);
    }
  }

  /**
   * Removes storage base fields if possible.
   */
  public function removeStorageFields() {
    $queue = $this->queueFactory
      ->get('field_encrypt_update_entity_encryption');

    // We can't remove the field if there are queue items to process because if
    // there is data we'll destroy it.
    if ($queue
      ->numberOfItems() > 0) {
      return;
    }
    $old_entity_types = $this->state
      ->get('field_encrypt.entity_types', []);
    $new_entity_types = $this
      ->getEntityTypes();
    if ($old_entity_types === $new_entity_types) {

      // No changes to make. Early return to do nothing and preserve caches.
      return;
    }
    $this->state
      ->set('field_encrypt.entity_types', $new_entity_types);

    // @see field_encrypt.module
    $this->moduleHandler
      ->resetImplementations();
    foreach (array_diff($old_entity_types, $new_entity_types) as $type) {
      $field = $this->entityDefinitionUpdateManager
        ->getFieldStorageDefinition(ProcessEntities::ENCRYPTED_FIELD_STORAGE_NAME, $type);
      if ($field) {
        $this->entityDefinitionUpdateManager
          ->uninstallFieldStorageDefinition($field);
      }
    }
  }

  /**
   * Gets the field definition for the blob where we store data.
   *
   * @return \Drupal\Core\Field\BaseFieldDefinition
   *   The field definition for the blob where we store data.
   */
  public static function getEncryptedFieldStorageDefinition() {
    return BaseFieldDefinition::create('encrypted_field_storage')
      ->setLabel(new TranslatableMarkup('Encrypted data'))
      ->setDescription(new TranslatableMarkup('Stores data from encrypted fields.'))
      ->setInternal(TRUE)
      ->setTranslatable(TRUE)
      ->setRevisionable(TRUE);
  }

  /**
   * Lists entity types which have encrypted fields.
   *
   * @return string[]
   *   The list of entity types with encrypted fields. Keyed by entity type ID.
   */
  protected function getEntityTypes() {
    $entity_types = [];
    foreach ($this->entityTypeManager
      ->getDefinitions() as $entity_type) {
      if ($entity_type instanceof ContentEntityTypeInterface) {
        $storage_class = $this->entityTypeManager
          ->createHandlerInstance($entity_type
          ->getStorageClass(), $entity_type);
        if ($storage_class instanceof DynamicallyFieldableEntityStorageInterface) {
          $entity_type_id = $entity_type
            ->id();

          // Check base fields.
          if ($this->entityTypeManager
            ->getStorage('field_encrypt_entity_type')
            ->load($entity_type_id)) {
            $entity_types[$entity_type_id] = $entity_type_id;
            continue;
          }

          // Query by filtering on the ID as this is more efficient than
          // filtering on the entity_type property directly.
          $ids = $this->entityTypeManager
            ->getStorage('field_storage_config')
            ->getQuery()
            ->condition('id', $entity_type_id . '.', 'STARTS_WITH')
            ->execute();

          // Fetch all fields on entity type.

          /** @var \Drupal\field\FieldStorageConfigInterface[] $field_storages */
          $field_storages = $this->entityTypeManager
            ->getStorage('field_storage_config')
            ->loadMultiple($ids);
          foreach ($field_storages as $storage) {

            // Check if field is encrypted.
            if ($storage
              ->getThirdPartySetting('field_encrypt', 'encrypt', FALSE) == TRUE) {
              $entity_types[$entity_type_id] = $entity_type_id;
              continue 2;
            }
          }
        }
      }
    }
    return $entity_types;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
StateManager::$entityDefinitionUpdateManager protected property The entity definition update manager.
StateManager::$entitySchemaRepository protected property The entity last installed schema repository.
StateManager::$entityTypeManager protected property The entity type manager service.
StateManager::$moduleHandler protected property The module handler.
StateManager::$queueFactory protected property The queue factory.
StateManager::$state protected property The state key value store.
StateManager::getEncryptedFieldStorageDefinition public static function Gets the field definition for the blob where we store data.
StateManager::getEntityTypes protected function Lists entity types which have encrypted fields.
StateManager::onFieldEncryptSettingsCacheChange public function Reacts to field_encrypt.settings:make_entities_uncacheable changes.
StateManager::removeStorageFields public function Removes storage base fields if possible.
StateManager::setEntityTypeCacheInformation protected function Sets the last installed entity cache information correctly.
StateManager::update public function Figure out which entity types are encrypted.
StateManager::__construct public function Constructs a new ConfigSubscriber object.