PrivateTempStore.php

Same filename in other branches
  1. 8.9.x core/modules/user/src/PrivateTempStore.php
  2. 8.9.x core/lib/Drupal/Core/TempStore/PrivateTempStore.php
  3. 10 core/lib/Drupal/Core/TempStore/PrivateTempStore.php
  4. 11.x core/lib/Drupal/Core/TempStore/PrivateTempStore.php

Namespace

Drupal\Core\TempStore

File

core/lib/Drupal/Core/TempStore/PrivateTempStore.php

View source
<?php

namespace Drupal\Core\TempStore;

use Drupal\Component\Utility\Crypt;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Stores and retrieves temporary data for a given owner.
 *
 * A PrivateTempStore can be used to make temporary, non-cache data available
 * across requests. The data for the PrivateTempStore is stored in one
 * key/value collection. PrivateTempStore data expires automatically after a
 * given timeframe.
 *
 * The PrivateTempStore is different from a cache, because the data in it is not
 * yet saved permanently and so it cannot be rebuilt. Typically, the
 * PrivateTempStore might be used to store work in progress that is later saved
 * permanently elsewhere, e.g. autosave data, multistep forms, or in-progress
 * changes to complex configuration that are not ready to be saved.
 *
 * The PrivateTempStore differs from the SharedTempStore in that all keys are
 * ensured to be unique for a particular user and users can never share data. If
 * you want to be able to share data between users or use it for locking, use
 * \Drupal\Core\TempStore\SharedTempStore.
 */
class PrivateTempStore {
    use DependencySerializationTrait;
    
    /**
     * The key/value storage object used for this data.
     *
     * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
     */
    protected $storage;
    
    /**
     * The lock object used for this data.
     *
     * @var \Drupal\Core\Lock\LockBackendInterface
     */
    protected $lockBackend;
    
    /**
     * The current user.
     *
     * @var \Drupal\Core\Session\AccountProxyInterface
     */
    protected $currentUser;
    
    /**
     * The request stack.
     *
     * @var \Symfony\Component\HttpFoundation\RequestStack
     */
    protected $requestStack;
    
    /**
     * The time to live for items in seconds.
     *
     * By default, data is stored for one week (604800 seconds) before expiring.
     *
     * @var int
     */
    protected $expire;
    
    /**
     * Constructs a new object for accessing data from a key/value store.
     *
     * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $storage
     *   The key/value storage object used for this data. Each storage object
     *   represents a particular collection of data and will contain any number
     *   of key/value pairs.
     * @param \Drupal\Core\Lock\LockBackendInterface $lock_backend
     *   The lock object used for this data.
     * @param \Drupal\Core\Session\AccountProxyInterface $current_user
     *   The current user account.
     * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
     *   The request stack.
     * @param int $expire
     *   The time to live for items, in seconds.
     */
    public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lock_backend, AccountProxyInterface $current_user, RequestStack $request_stack, $expire = 604800) {
        $this->storage = $storage;
        $this->lockBackend = $lock_backend;
        $this->currentUser = $current_user;
        $this->requestStack = $request_stack;
        $this->expire = $expire;
    }
    
    /**
     * Retrieves a value from this PrivateTempStore for a given key.
     *
     * @param string $key
     *   The key of the data to retrieve.
     *
     * @return mixed
     *   The data associated with the key, or NULL if the key does not exist.
     */
    public function get($key) {
        $key = $this->createkey($key);
        if (($object = $this->storage
            ->get($key)) && $object->owner == $this->getOwner()) {
            return $object->data;
        }
    }
    
    /**
     * Stores a particular key/value pair in this PrivateTempStore.
     *
     * @param string $key
     *   The key of the data to store.
     * @param mixed $value
     *   The data to store.
     *
     * @throws \Drupal\Core\TempStore\TempStoreException
     *   Thrown when a lock for the backend storage could not be acquired.
     */
    public function set($key, $value) {
        if ($this->currentUser
            ->isAnonymous()) {
            // Ensure that an anonymous user has a session created for them, as
            // otherwise subsequent page loads will not be able to retrieve their
            // tempstore data. Note this has to be done before the key is created as
            // the owner is used in key creation.
            $this->startSession();
            $session = $this->requestStack
                ->getCurrentRequest()
                ->getSession();
            if (!$session->has('core.tempstore.private.owner')) {
                $session->set('core.tempstore.private.owner', Crypt::randomBytesBase64());
            }
        }
        $key = $this->createkey($key);
        if (!$this->lockBackend
            ->acquire($key)) {
            $this->lockBackend
                ->wait($key);
            if (!$this->lockBackend
                ->acquire($key)) {
                throw new TempStoreException("Couldn't acquire lock to update item '{$key}' in '{$this->storage->getCollectionName()}' temporary storage.");
            }
        }
        $value = (object) [
            'owner' => $this->getOwner(),
            'data' => $value,
            'updated' => (int) $this->requestStack
                ->getMainRequest()->server
                ->get('REQUEST_TIME'),
        ];
        $this->storage
            ->setWithExpire($key, $value, $this->expire);
        $this->lockBackend
            ->release($key);
    }
    
    /**
     * Returns the metadata associated with a particular key/value pair.
     *
     * @param string $key
     *   The key of the data to store.
     *
     * @return \Drupal\Core\TempStore\Lock|null
     *   An object with the owner and updated time if the key has a value, or
     *   NULL otherwise.
     */
    public function getMetadata($key) {
        $key = $this->createkey($key);
        // Fetch the key/value pair and its metadata.
        $object = $this->storage
            ->get($key);
        if ($object) {
            // Don't keep the data itself in memory.
            unset($object->data);
            return new Lock($object->owner, $object->updated);
        }
    }
    
    /**
     * Deletes data from the store for a given key and releases the lock on it.
     *
     * @param string $key
     *   The key of the data to delete.
     *
     * @return bool
     *   TRUE if the object was deleted or does not exist, FALSE if it exists but
     *   is not owned by $this->owner.
     *
     * @throws \Drupal\Core\TempStore\TempStoreException
     *   Thrown when a lock for the backend storage could not be acquired.
     */
    public function delete($key) {
        $key = $this->createkey($key);
        if (!($object = $this->storage
            ->get($key))) {
            return TRUE;
        }
        elseif ($object->owner != $this->getOwner()) {
            return FALSE;
        }
        if (!$this->lockBackend
            ->acquire($key)) {
            $this->lockBackend
                ->wait($key);
            if (!$this->lockBackend
                ->acquire($key)) {
                throw new TempStoreException("Couldn't acquire lock to delete item '{$key}' from '{$this->storage->getCollectionName()}' temporary storage.");
            }
        }
        $this->storage
            ->delete($key);
        $this->lockBackend
            ->release($key);
        return TRUE;
    }
    
    /**
     * Ensures that the key is unique for a user.
     *
     * @param string $key
     *   The key.
     *
     * @return string
     *   The unique key for the user.
     */
    protected function createkey($key) {
        return $this->getOwner() . ':' . $key;
    }
    
    /**
     * Gets the current owner based on the current user or the session ID.
     *
     * @return string
     *   The owner.
     */
    protected function getOwner() {
        $owner = $this->currentUser
            ->id();
        if ($this->currentUser
            ->isAnonymous()) {
            // Check to see if an owner key exists in the session.
            $this->startSession();
            $session = $this->requestStack
                ->getCurrentRequest()
                ->getSession();
            $owner = $session->get('core.tempstore.private.owner');
        }
        return $owner;
    }
    
    /**
     * Start session because it is required for a private temp store.
     *
     * Ensures that an anonymous user has a session created for them, as
     * otherwise subsequent page loads will not be able to retrieve their
     * tempstore data.
     *
     * @todo when https://www.drupal.org/node/2865991 is resolved, use force
     * start session API.
     */
    protected function startSession() {
        $has_session = $this->requestStack
            ->getCurrentRequest()
            ->hasSession();
        if (!$has_session) {
            
            /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
            $session = \Drupal::service('session');
            $this->requestStack
                ->getCurrentRequest()
                ->setSession($session);
            $session->start();
        }
    }

}

Classes

Title Deprecated Summary
PrivateTempStore Stores and retrieves temporary data for a given owner.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.