BlobStore::getKey()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Onoi\BlobStore;
4
5
use Onoi\Cache\Cache;
6
use Onoi\Cache\CacheFactory;
7
use InvalidArgumentException;
8
9
/**
10
 * Pervasive value blob store that can be used to store and retrieve key values
11
 * from a schema free "fast" data store such as redis. The connection to a back-end
12
 * is handled by the Onoi\Cache interface to support different provider solutions.
13
 *
14
 * @license GNU GPL v2+
15
 * @since 1.0
16
 *
17
 * @author mwjames
18
 */
19
class BlobStore {
20
21
	/**
22
	 * @var string
23
	 */
24
	private $namespace;
25
26
	/**
27
	 * @var string
28
	 */
29
	private $namespacePrefix = 'blobstore';
30
31
	/**
32
	 * @var Cache
33
	 */
34
	private $cache;
35
36
	/**
37
	 * @var Cache
38
	 */
39
	private $internalCache;
40
41
	/**
42
	 * @var boolean
43
	 */
44
	private $usageState = true;
45
46
	/**
47
	 * 0 = stored indefinitely until it is removed or dropped
48
	 *
49
	 * @var integer
50
	 */
51
	private $expiry = 0;
52
53
	/**
54
	 * @since 1.0
55
	 *
56
	 * @param string $namespace
57
	 * @param Cache $cache
58
	 */
59 17
	public function __construct( $namespace, Cache $cache ) {
60
61 17
		if ( !is_string( $namespace ) ) {
62 1
			throw new InvalidArgumentException( "Expected the namespace to be a string" );
63
		}
64
65 16
		$this->namespace = $namespace;
66 16
		$this->cache = $cache;
67
68
		// It is only used internally therefore no injection required as it improves
69
		// performance on long lists as seen in #1
70 16
		$this->internalCache = CacheFactory::getInstance()->newFixedInMemoryLruCache( 500 );
71 16
	}
72
73
	/**
74
	 * @since 1.0
75
	 *
76
	 * @return boolean
77
	 */
78 1
	public function canUse() {
79 1
		return $this->usageState;
80
	}
81
82
	/**
83
	 * Specifies whether the instance can be generally used or not
84
	 *
85
	 * @since 1.0
86
	 *
87
	 * @param boolean $usageState
88
	 */
89 1
	public function setUsageState( $usageState ) {
90 1
		$this->usageState = (bool)$usageState;
91 1
	}
92
93
	/**
94
	 * Specifies the expiry / time to live for stored containers in seconds
95
	 *
96
	 * @since 1.0
97
	 *
98
	 * @param integer $expiry
99
	 */
100 2
	public function setExpiryInSeconds( $expiry ) {
101 2
		$this->expiry = $expiry;
102 2
	}
103
104
	/**
105
	 * @since 1.0
106
	 *
107
	 * @param string $prefix
108
	 */
109 1
	public function setNamespacePrefix( $prefix ) {
110 1
		$this->namespacePrefix = $prefix;
111 1
	}
112
113
	/**
114
	 * @since 1.0
115
	 *
116
	 * @param string $id
117
	 *
118
	 * @return boolean
119
	 */
120 2
	public function exists( $id ) {
121 2
		return $this->cache->contains( $this->getKey( $id ) );
122
	}
123
124
	/**
125
	 * @since 1.0
126
	 *
127
	 * @return array
128
	 */
129 1
	public function getStats() {
130 1
		return $this->cache->getStats() + array(
131 1
			'internalCache' => $this->internalCache->getStats()
132 1
		);
133
	}
134
135
	/**
136
	 * @since 1.0
137
	 *
138
	 * @param string $id
139
	 *
140
	 * @return Container
141
	 */
142 10
	public function read( $id ) {
143
144 10
		$id = $this->getKey( $id );
145
146
		// If possible use the raw data from the internal cache
147
		// without unserialization
148 9
		if ( $this->internalCache->contains( $id ) ) {
149 2
			$data = $this->internalCache->fetch( $id );
150 9
		} elseif ( $this->cache->contains( $id ) ) {
151 3
			$data = unserialize( $this->cache->fetch( $id ) );
152 3
			$this->internalCache->save( $id, $data );
153 3
		} else {
154 6
			$data = array();
155
		}
156
157 9
		$container = new Container( $id, (array)$data );
158 9
		$container->setExpiryInSeconds( $this->expiry );
159
160 9
		return $container;
161
	}
162
163
	/**
164
	 * @since 1.0
165
	 *
166
	 * @param Container $container
167
	 */
168 5
	public function save( Container $container ) {
169
170 5
		$this->internalCache->save(
171 5
			$container->getId(),
172 5
			$container->getData(),
173 5
			$container->getExpiry()
174 5
		);
175
176 5
		$this->cache->save(
177 5
			$container->getId(),
178 5
			serialize( $container->getData() ),
179 5
			$container->getExpiry()
180 5
		);
181
182 5
		unset( $container );
183 5
	}
184
185
	/**
186
	 * @since 1.0
187
	 *
188
	 * @param string $id
189
	 */
190 3
	public function delete( $id ) {
191
192 3
		$container = $this->read( $id );
193
194 3
		foreach ( $container->getLinkedList() as $linkedId ) {
195 1
			$key = $this->getKey( (string)$linkedId );
196 1
			$this->cache->delete( $key );
197 1
			$this->internalCache->delete( $key );
198 3
		}
199
200 3
		$this->cache->delete( $this->getKey( $id ) );
201 3
		$this->internalCache->delete( $this->getKey( $id ) );
202 3
	}
203
204
	/**
205
	 * @since 1.0
206
	 */
207 1
	public function drop() {
208
		// After using the internal list for several production runs it seems
209
		// difficult to keep track of ids especially when using long lists and
210
		// not to increase overhead which can easily produced by 50K+ entries
211
		// which would require to split the internal list
212
		//
213
		// Using an appropriate expiry seems more efficient
214 1
	}
215
216 10
	private function getKey( $id ) {
217
218 10
		if ( !is_string( $id ) ) {
219 1
			throw new InvalidArgumentException( "Expected the id to be a string" );
220
		}
221
222 9
		return  $this->namespacePrefix . ':' . $this->namespace . ':' . $id;
223
	}
224
225
}
226