Common::doLoad()
last analyzed

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
c 0
b 0
f 0
nc 1
1
<?php
2
/**
3
 * For the full copyright and license information, please view the LICENSE
4
 * file that was distributed with this source code.
5
 *
6
 * @author Nikita Vershinin <[email protected]>
7
 * @license MIT
8
 */
9
namespace Endeveit\Cache\Abstracts;
10
11
use Endeveit\Cache\Interfaces\Driver;
12
use Endeveit\Cache\Interfaces\Serializer;
13
14
/**
15
 * Base class for drivers that use max lifetime limit.
16
 */
17
abstract class Common implements Driver
18
{
19
    /**
20
     * Default options for all drivers.
21
     *
22
     * @var array
23
     */
24
    protected $defaultOptions = array(
25
        'lock_suffix'      => '.lock',
26
        'prefix_id'        => '',
27
        'prefix_tag'       => 'tag.',
28
        'serializer'       => 'BuiltIn',
29
        'throw_exceptions' => true,
30
    );
31
32
    /**
33
     * Driver options.
34
     *
35
     * @var array
36
     */
37
    protected $options = array();
38
39
    /**
40
     * Instance of serializer.
41
     *
42
     * @var \Endeveit\Cache\Interfaces\Serializer
43
     */
44
    protected $serializer = null;
45
46
    /**
47
     * Class constructor.
48
     * Available options:
49
     *  "lock_suffix"      => suffix for read lock key
50
     *  "prefix_id"        => prefix for cache keys
51
     *  "prefix_tag"       => prefix for cache tags
52
     *  "serializer"       => one of predefined serializer objects: BuiltIn or Igbinary
53
     *  "throw_exceptions" => exception will be thrown on read/write errors
54
     *
55
     * @codeCoverageIgnore
56
     * @param array $options
57
     */
58
    public function __construct(array $options = array())
59
    {
60
        $this->options = array_merge($this->defaultOptions, $options);
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     *
66
     * @param \Endeveit\Cache\Interfaces\Serializer $serializer
67
     */
68
    public function setSerializer(Serializer $serializer)
69
    {
70
        $this->serializer = $serializer;
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     *
76
     * @return \Endeveit\Cache\Interfaces\Serializer
77
     */
78
    public function getSerializer()
79
    {
80
        if (null === $this->serializer) {
81
            $className        = 'Endeveit\Cache\Serializers\\' . $this->getOption('serializer');
82
            $this->serializer = new $className();
83
        }
84
85
        return $this->serializer;
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     *
91
     * @param  string       $id
92
     * @param  integer|null $lockTimeout
93
     * @return mixed|false  Data on success, false on failure
94
     * @throws \Exception
95
     */
96
    public function load($id, $lockTimeout = null)
97
    {
98
        $result = false;
99
        $source = $this->doLoad($this->getPrefixedIdentifier($id));
100
101
        if (false !== $source) {
102
            $result = $source['data'];
103
104
            if (array_key_exists('expiresAt', $source) && ($source['expiresAt'] < time())) {
105
                $result = false;
106
107
                if (null !== $lockTimeout) {
108
                    $lockId = $this->getPrefixedIdentifier($id . $this->getOption('lock_suffix'));
109
110
                    try {
111
                        $exists = $this->doLoadRaw($lockId);
112
113
                        if (!$exists) {
114
                            // Set the lock and return false
115
                            $this->doSaveScalar(1, $lockId, intval($lockTimeout));
116
117
                            $result = false;
118
                        } else {
119
                            $result = $source['data'];
120
                        }
121
                    } catch (\Exception $e) {
122
                        if (true === $this->getOption('throw_exceptions', true)) {
123
                            throw $e;
124
                        } else {
125
                            return false;
126
                        }
127
                    }
128
                }
129
            }
130
        }
131
132
        return $result;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     *
138
     * @param  array $identifiers
139
     * @return array
140
     */
141
    public function loadMany(array $identifiers)
142
    {
143
        return $this->doLoadMany(array_map(array($this, 'getPrefixedIdentifier'), array_values($identifiers)));
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     *
149
     * @param  mixed           $data
150
     * @param  string          $id
151
     * @param  array           $tags
152
     * @param  integer|boolean $lifetime
153
     * @return boolean
154
     * @throws \Exception
155
     */
156
    public function save($data, $id, array $tags = array(), $lifetime = false)
157
    {
158
        $source = array('data' => $data);
159
160
        if (false !== $lifetime) {
161
            $source['expiresAt'] = time() + intval($lifetime);
162
        }
163
164
        try {
165
            $result = $this->doSave(
166
                $source,
167
                $this->getPrefixedIdentifier($id),
168
                array_map(array($this, 'getPrefixedTag'), $tags)
169
            );
170
        } catch (\Exception $e) {
171
            if (true === $this->getOption('throw_exceptions', true)) {
172
                throw $e;
173
            } else {
174
                $result = false;
175
            }
176
        }
177
178
        return $result;
179
    }
180
181
    /**
182
     * {@inheritdoc}
183
     *
184
     * @param  string  $id
185
     * @return boolean
186
     */
187
    public function remove($id)
188
    {
189
        return $this->save(false, $id, array(), -1);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     *
195
     * @param  array   $tags
196
     * @return boolean
197
     */
198
    public function removeByTags(array $tags)
199
    {
200
        return $this->doRemoveByTags($tags);
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     *
206
     * @param  string  $id
207
     * @param  integer $extraLifetime
208
     * @return boolean
209
     */
210
    public function touch($id, $extraLifetime)
211
    {
212
        $result = false;
213
        $data   = $this->load($id);
214
215
        if (false !== $data) {
216
            $result = $this->save($data, $id, array(), $extraLifetime);
217
        }
218
219
        return $result;
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     *
225
     * @param  string  $id
226
     * @return boolean
227
     */
228
    public function contains($id)
229
    {
230
        return (false !== $this->load($id)) ? true : false;
231
    }
232
233
    /**
234
     * {@inheritdoc}
235
     *
236
     * @return boolean
237
     */
238
    public function flush()
239
    {
240
        return $this->doFlush();
241
    }
242
243
    /**
244
     * Returns configuration option.
245
     *
246
     * @param  string $name
247
     * @param  mixed  $default
248
     * @return mixed
249
     */
250
    protected function getOption($name, $default = null)
251
    {
252
        return array_key_exists($name, $this->options) ? $this->options[$name] : $default;
253
    }
254
255
    /**
256
     * Returns prefixed identifier.
257
     *
258
     * @param  string $id
259
     * @return string
260
     */
261
    protected function getPrefixedIdentifier($id)
262
    {
263
        return $this->getOption('prefix_id') . $id;
264
    }
265
266
    /**
267
     * Returns identifier without prefix.
268
     *
269
     * @param  string $id
270
     * @return string
271
     */
272
    protected function getIdentifierWithoutPrefix($id)
273
    {
274
        return substr($id, strlen($this->getOption('prefix_id')));
275
    }
276
277
    /**
278
     * Returns prefixed tag.
279
     *
280
     * @param  string $tag
281
     * @return string
282
     */
283
    protected function getPrefixedTag($tag)
284
    {
285
        return $this->getOption('prefix_id') . $this->getOption('prefix_tag') . $tag;
286
    }
287
288
    /**
289
     * Fills the not found keys with false in «loadMany» method.
290
     *
291
     * @param array $result
292
     * @param array $identifiers
293
     */
294
    protected function fillNotFoundKeys(array &$result, array &$identifiers)
295
    {
296
        $tmp = array();
297
        $ids = array_map(array($this, 'getIdentifierWithoutPrefix'), $identifiers);
298
299
        if (count($result) != count($identifiers)) {
300
            foreach (array_diff($ids, array_keys($result)) as $notExist) {
301
                $result[$notExist] = false;
302
            }
303
        }
304
305
        // Sort the results according to the order in $identifiers variable
306
        foreach ($ids as $id) {
307
            $tmp[$id] = $result[$id];
308
        }
309
310
        $result = $tmp;
311
    }
312
313
    /**
314
     * Returns processed value from loaded data.
315
     *
316
     * @param  mixed         $source
317
     * @return array|boolean
318
     */
319
    protected function getProcessedLoadedValue($source)
320
    {
321
        if (false !== $source) {
322
            if (is_array($source) && array_key_exists('data', $source)) {
323
                return $source;
324
            } elseif (is_scalar($source)) {
325
                return array(
326
                    'data' => $source,
327
                );
328
            }
329
        }
330
331
        return false;
332
    }
333
334
    /**
335
     * Returns an item through selected driver.
336
     *
337
     * @param  string      $id
338
     * @return array|false
339
     */
340
    abstract protected function doLoad($id);
341
342
    /**
343
     * Returns many items at once through selected driver.
344
     *
345
     * @param  array $identifiers
346
     * @return array
347
     */
348
    abstract protected function doLoadMany(array $identifiers);
349
350
    /**
351
     * Returns raw value from drivers.
352
     *
353
     * @param  string      $id
354
     * @return mixed|false
355
     */
356
    abstract protected function doLoadRaw($id);
357
358
    /**
359
     * Store an item through selected driver.
360
     *
361
     * @param  mixed   $data
362
     * @param  string  $id
363
     * @param  array   $tags
364
     * @return boolean
365
     */
366
    abstract protected function doSave($data, $id, array $tags = array());
367
368
    /**
369
     * Store raw value in selected driver.
370
     *
371
     * @param  mixed           $data
372
     * @param  string          $id
373
     * @param  integer|boolean $lifetime
374
     * @return boolean
375
     */
376
    abstract protected function doSaveScalar($data, $id, $lifetime = false);
377
378
    /**
379
     * Remove an items by cache tags through selected driver.
380
     *
381
     * @param  array   $tags
382
     * @return boolean
383
     */
384
    abstract protected function doRemoveByTags(array $tags);
385
386
    /**
387
     * Drops all items from cache through selected driver.
388
     *
389
     * @return boolean
390
     */
391
    abstract protected function doFlush();
392
}
393