1<?php
2
3/*
4 * This file is part of the Stash package.
5 *
6 * (c) Robert Hafner <tedivm@tedivm.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Stash\Driver;
13
14use Stash;
15
16/**
17 * The ephemeral class exists to assist with testing the main Stash class. Since this is a very minimal driver we can
18 * test Stash without having to worry about underlying problems interfering.
19 *
20 * @package Stash
21 * @author  Robert Hafner <tedivm@tedivm.com>
22 */
23class Ephemeral extends AbstractDriver
24{
25    /**
26     * Contains the cached data.
27     *
28     * @var array
29     */
30    protected $store = array();
31
32    protected $maxItems = 0;
33
34    public function getDefaultOptions()
35    {
36        return ['maxItems' => 0];
37    }
38
39    /**
40     * Allows setting maxItems.
41     *
42     * @param array $options
43     *                       If maxItems is 0, infinite items will be cached
44     */
45    protected function setOptions(array $options = array())
46    {
47        $options += $this->getDefaultOptions();
48
49        if (array_key_exists('maxItems', $options)) {
50            $maxItems = $options['maxItems'];
51            if (!is_int($maxItems) || $maxItems < 0) {
52                throw new Stash\Exception\InvalidArgumentException(
53                  'maxItems must be a positive integer.'
54                );
55            }
56            $this->maxItems = $maxItems;
57            if ($this->maxItems > 0 && count($this->store) > $this->maxItems) {
58                $this->evict(count($this->store) - $this->maxItems);
59            }
60        }
61    }
62
63    /**
64     * Evicts the first $count items that were added to the store.
65     *
66     * Subclasses could implement more advanced eviction policies.
67     *
68     * @param int $count
69     */
70    protected function evict($count)
71    {
72        while (
73          $count-- > 0
74          && array_shift($this->store) !== null
75        ) {
76        }
77    }
78
79    /**
80     * {@inheritdoc}
81     */
82    public function getData($key)
83    {
84        $key = $this->getKeyIndex($key);
85
86        return isset($this->store[$key]) ? $this->store[$key] : false;
87    }
88
89    /**
90     * Converts the key array into a passed function
91     *
92     * @param  array  $key
93     * @return string
94     */
95    protected function getKeyIndex($key)
96    {
97        $index = '';
98        foreach ($key as $value) {
99            $index .= str_replace('#', '#:', $value) . '#';
100        }
101
102        return $index;
103    }
104
105    /**
106     * {@inheritdoc}
107     */
108    public function storeData($key, $data, $expiration)
109    {
110        if ($this->maxItems > 0 && count($this->store) >= $this->maxItems) {
111            $this->evict((count($this->store) + 1) - $this->maxItems);
112        }
113
114        $this->store[$this->getKeyIndex($key)] = array('data' => $data, 'expiration' => $expiration);
115
116        return true;
117    }
118
119    /**
120     * {@inheritdoc}
121     */
122    public function clear($key = null)
123    {
124        if (!isset($key)) {
125            $this->store = array();
126        } else {
127            $clearIndex = $this->getKeyIndex($key);
128            foreach ($this->store as $index => $data) {
129                if (strpos($index, $clearIndex) === 0) {
130                    unset($this->store[$index]);
131                }
132            }
133        }
134
135        return true;
136    }
137
138    /**
139     * {@inheritdoc}
140     */
141    public function purge()
142    {
143        $now = time();
144        foreach ($this->store as $index => $data) {
145            if ($data['expiration'] <= $now) {
146                unset($this->store[$index]);
147            }
148        }
149
150        return true;
151    }
152}
153