Completed
Pull Request — master (#1)
by
unknown
08:26
created

MockCache   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 29
c 0
b 0
f 0
lcom 1
cbo 1
dl 0
loc 154
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A skipTime() 0 4 1
A get() 0 8 3
A set() 0 19 4
A delete() 0 15 3
A clear() 0 5 1
B getMultiple() 0 15 5
A setMultiple() 0 13 4
A deleteMultiple() 0 11 4
A has() 0 4 1
A validateKey() 0 6 2
1
<?php
2
3
namespace Kodus\Cache;
4
5
use DateInterval;
6
use Psr\SimpleCache\CacheInterface;
7
use Traversable;
8
9
/**
10
 * WARNING! Don't use in production, only use for automated testing!
11
 *
12
 * This is a mock PSR-16 simple cache implementation - it's internal state survives only for the
13
 * lifetime of the object itself.
14
 */
15
class MockCache implements CacheInterface
16
{
17
    /**
18
     * @var int
19
     */
20
    const DEFAULT_TTL = 86400;
21
22
    /**
23
     * @var string control characters for keys, reserved by PSR-16
24
     */
25
    const PSR16_RESERVED = '/\{|\}|\(|\)|\/|\\\\|\@|\:/u';
26
27
    /**
28
     * @var array map where cache key => serialized value
29
     */
30
    protected $cache = [];
31
32
    /**
33
     * @var int[] map where cache key => expiration timestamp
34
     */
35
    protected $cache_expiration = [];
36
37
    /**
38
     * @var int current frozen timestamp
39
     */
40
    protected $time;
41
42
    /**
43
     * @var int
44
     */
45
    protected $default_ttl;
46
47
    /**
48
     * @param int $default_ttl default time-to-live (in seconds)
49
     */
50
    public function __construct($default_ttl = self::DEFAULT_TTL)
51
    {
52
        $this->default_ttl = $default_ttl;
53
        $this->time = 0;
54
    }
55
56
    /**
57
     * @param int $seconds
58
     */
59
    public function skipTime($seconds)
60
    {
61
        $this->time += $seconds;
62
    }
63
64
    public function get($key, $default = null)
65
    {
66
        $this->validateKey($key);
67
68
        return isset($this->cache_expiration[$key]) && ($this->time < $this->cache_expiration[$key])
69
            ? unserialize($this->cache[$key])
70
            : $default;
71
    }
72
73
    public function set($key, $value, $ttl = null)
74
    {
75
        $this->validateKey($key);
76
77
        if (is_int($ttl)) {
78
            $expires_at = $this->time + $ttl;
79
        } elseif ($ttl instanceof DateInterval) {
80
            $expires_at = date_create_from_format("U", $this->time)->add($ttl)->getTimestamp();
81
        } elseif ($ttl === null) {
82
            $expires_at = $this->time + $this->default_ttl;
83
        } else {
84
            throw new InvalidArgumentException("invalid TTL: " . print_r($ttl, true));
85
        }
86
87
        $this->cache[$key] = serialize($value);
88
        $this->cache_expiration[$key] = $expires_at;
89
90
        return true;
91
    }
92
93
    public function delete($key)
94
    {
95
        $this->validateKey($key);
96
97
        $success = true;
98
99
        if (! isset($this->cache[$key]) || ! isset($this->cache_expiration[$key])) {
100
            $success = false;
101
        }
102
103
        unset($this->cache[$key]);
104
        unset($this->cache_expiration[$key]);
105
106
        return $success;
107
    }
108
109
    public function clear()
110
    {
111
        $this->cache = [];
112
        $this->cache_expiration = [];
113
    }
114
115
    public function getMultiple($keys, $default = null)
116
    {
117
        if (! is_array($keys) && ! $keys instanceof Traversable) {
118
            throw new InvalidArgumentException("keys must be either of type array or Traversable");
119
        }
120
121
        $values = [];
122
123
        foreach ($keys as $key) {
124
            $this->validateKey($key);
125
            $values[$key] = $this->get($key) ?: $default;
126
        }
127
128
        return $values;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $values; (array) is incompatible with the return type declared by the interface Psr\SimpleCache\CacheInterface::getMultiple of type Psr\SimpleCache\iterable.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
129
    }
130
131
    public function setMultiple($values, $ttl = null)
132
    {
133
        if (! is_array($values) && ! $values instanceof Traversable) {
134
            throw new InvalidArgumentException("keys must be either of type array or Traversable");
135
        }
136
137
        foreach ($values as $key => $value) {
138
            $this->validateKey($key);
139
            $this->set($key, $value, $ttl);
140
        }
141
142
        return true;
143
    }
144
145
    public function deleteMultiple($keys)
146
    {
147
        if (! is_array($keys) && ! $keys instanceof Traversable) {
148
            throw new InvalidArgumentException("keys must be either of type array or Traversable");
149
        }
150
151
        foreach ($keys as $key) {
152
            $this->validateKey($key);
153
            $this->delete($key);
154
        }
155
    }
156
157
    public function has($key)
158
    {
159
        return $this->get($key, $this) !== $this;
160
    }
161
162
    protected function validateKey($key)
163
    {
164
        if (preg_match(self::PSR16_RESERVED, $key, $match) === 1) {
165
            throw new InvalidArgumentException("invalid character in key: {$match[0]}");
166
        }
167
    }
168
}
169