Skip to content
Snippets Groups Projects
Select Git revision
  • 40bdfaf4415ff9f46e63435fc5e61049649802f2
  • main default protected
  • studip-rector
  • ci-opt
  • course-members-export-as-word
  • data-vue-app
  • pipeline-improvements
  • webpack-optimizations
  • rector
  • icon-renewal
  • http-client-and-factories
  • jsonapi-atomic-operations
  • vueify-messages
  • tic-2341
  • 135-translatable-study-areas
  • extensible-sorm-action-parameters
  • sorm-configuration-trait
  • jsonapi-mvv-routes
  • docblocks-for-magic-methods
19 results

StudipCachedArray.php

Blame
  • Forked from Stud.IP / Stud.IP
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    StudipCachedArray.php 5.07 KiB
    <?php
    /**
     * This class represents an array in cache and removes the neccessity to
     * encode/decode and store the data after every change.
     *
     * @author  Jan-Hendrik Willms <tleilax+studip@gmail.com>
     * @license GPL2 or any later version
     * @since   Stud.IP 5.0
     */
    class StudipCachedArray implements ArrayAccess
    {
        protected $key;
        protected $cache;
        protected $duration;
    
        protected $data = [];
    
        protected $hash;
    
        /**
         * Constructs the cached array
         *
         * @param string $key      Cache key where the array is/should be stored
         *                         an int which will be length of the substring
         *                         of the given chache offset or a callable which
         *                         will return the partition key.
         * @param int    $duration Duration in seconds for which the item shall be
         *                         stored
         */
        public function __construct(string $key, int $duration = \Studip\Cache\Cache::DEFAULT_EXPIRATION)
        {
            $this->key = self::class . "/{$key}";
            $this->cache = \Studip\Cache\Factory::getCache();
            $this->duration = $duration;
            $this->hash = $this->getHash();
    
            $this->reset();
        }
    
        /**
         * Clears cached values from memory, but does not remove them from the cache.
         */
        public function reset(): void
        {
            $this->data = [];
        }
    
        /**
         * Removes all values from the cache.
         */
        public function expire(): void
        {
            $this->hash = $this->getHash(true);
            $this->reset();
        }
    
        /**
         * Determines whether an offset exists in the array.
         *
         * @param string $offset Offset
         *
         * @return bool
         */
        public function offsetExists($offset): bool
        {
            $this->loadData($offset);
            return isset($this->data[$offset]);
        }
    
        /**
         * Returns the value at given offset or null if it doesn't exist.
         *
         * @param string $offset Offset
         */
        public function offsetGet($offset): mixed
        {
            $this->loadData($offset);
            return $this->data[$offset];
        }
    
        /**
         * Sets the value for a given offset.
         *
         * @param string $offset Offset
         * @param mixed  $value  Value
         */
        public function offsetSet($offset, $value): void
        {
            if ($offset === null) {
                throw new Exception('Cannot push to cached array, use correct offset instead');
            }
    
            if (!isset($this->data[$offset]) || $this->data[$offset] !== $value) {
                $this->data[$offset] = $value;
    
                $this->storeData($offset);
            }
        }
    
        /**
         * Unsets the value at a given offset
         *
         * @param string $offset Offset
         */
        public function offsetUnset($offset): void
        {
            $this->cache->expire($this->getCacheKey($offset));
            unset($this->data[$offset]);
        }
    
        /**
         * Loads the data from cache.
         *
         * @param string $offset Offset to load
         */
        protected function loadData(string $offset)
        {
            if (!array_key_exists($offset, $this->data)) {
                // Get the cache item from the cache:
                $item = $this->cache->getItem($this->getCacheKey($offset));
                if ($item->isHit()) {
                    $this->data[$offset] = $this->swapNullAndFalse($item->get());
                }
            }
    
            return $this->data[$offset] ?? null;
        }
    
        /**
         * Stores the data back to the cache.
         * Data needs to be wrapped in another array so that we can correctly read
         * back a value of "false".
         *
         * @param string $offset Offset to store
         */
        protected function storeData(string $offset): void
        {
            $item = new \Studip\Cache\Item(
                $this->getCacheKey($offset),
                $this->swapNullAndFalse($this->data[$offset]),
                $this->duration
            );
            $this->cache->save($item);
        }
    
        /**
         * Returns the cache key for a specific offset.
         *
         * @param string $offset Offset of the cached item
         *
         * @return string
         */
        private function getCacheKey(string $offset): string
        {
            $key = rtrim($this->key, '/');
            if ($this->hash) {
                $key .= "/{$this->hash}";
            }
            $key .= "/{$offset}";
    
            return $key;
        }
    
        /**
         * Swaps null and false for a value because the Stud.IP cache will return
         * false if a cached item is not found instead of null.
         *
         * @param mixed $value Value to swap if appropriate
         *
         * @return mixed
         */
        private function swapNullAndFalse($value)
        {
            if ($value === null) {
                return false;
            }
    
            if ($value === false) {
                return null;
            }
    
            return $value;
        }
    
        /**
         * Loads or creates and stores a hash for this cached array.
         *
         * @return string
         */
        private function getHash(bool $recreate = false): string
        {
            if (!$recreate) {
                $hash = $this->cache->read($this->key);
                return $hash === false ? '' : $hash;
            }
    
            $hash = md5(uniqid(__CLASS__, true));
            $this->cache->write($this->key, $hash);
            return $hash;
        }
    }