|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* Kotori.php |
|
4
|
|
|
* |
|
5
|
|
|
* A Tiny Model-View-Controller PHP Framework |
|
6
|
|
|
* |
|
7
|
|
|
* This content is released under the Apache 2 License |
|
8
|
|
|
* |
|
9
|
|
|
* Copyright (c) 2015-2017 Kotori Technology. All rights reserved. |
|
10
|
|
|
* |
|
11
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
12
|
|
|
* you may not use this file except in compliance with the License. |
|
13
|
|
|
* You may obtain a copy of the License at |
|
14
|
|
|
* |
|
15
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
16
|
|
|
* |
|
17
|
|
|
* Unless required by applicable law or agreed to in writing, software |
|
18
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
19
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
20
|
|
|
* See the License for the specific language governing permissions and |
|
21
|
|
|
* limitations under the License. |
|
22
|
|
|
*/ |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* Caching Class |
|
26
|
|
|
* |
|
27
|
|
|
* @package Kotori |
|
28
|
|
|
* @subpackage Core |
|
29
|
|
|
* @author Kokororin |
|
30
|
|
|
* @link https://kotori.love |
|
31
|
|
|
*/ |
|
32
|
|
|
namespace Kotori\Core; |
|
33
|
|
|
|
|
34
|
|
|
use Kotori\Core\Container; |
|
35
|
|
|
use Kotori\Debug\Hook; |
|
36
|
|
|
use Psr\SimpleCache\CacheInterface; |
|
37
|
|
|
|
|
38
|
|
|
class Cache implements CacheInterface |
|
39
|
|
|
{ |
|
40
|
|
|
/** |
|
41
|
|
|
* Valid cache drivers |
|
42
|
|
|
* |
|
43
|
|
|
* @var array |
|
44
|
|
|
*/ |
|
45
|
|
|
protected $validDrivers = [ |
|
46
|
|
|
'dummy', |
|
47
|
|
|
'memcached', |
|
48
|
|
|
'redis', |
|
49
|
|
|
]; |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* Reference to the driver |
|
53
|
|
|
* |
|
54
|
|
|
* @var mixed |
|
55
|
|
|
*/ |
|
56
|
|
|
protected $adapter = 'dummy'; |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* Cache key prefix |
|
60
|
|
|
* |
|
61
|
|
|
* @var string |
|
62
|
|
|
*/ |
|
63
|
|
|
public $keyPrefix = ''; |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* Constructor |
|
67
|
|
|
* |
|
68
|
|
|
* Initialize class properties based on the configuration array. |
|
69
|
|
|
*/ |
|
70
|
1 |
|
public function __construct() |
|
71
|
|
|
{ |
|
72
|
1 |
|
$config = Container::get('config')->get('cache'); |
|
73
|
1 |
|
if (isset($config['adapter'])) { |
|
74
|
1 |
|
$this->adapter = $config['adapter']; |
|
75
|
|
|
} |
|
76
|
|
|
|
|
77
|
1 |
|
if (isset($config['prefix'])) { |
|
78
|
1 |
|
$this->keyPrefix = $config['prefix']; |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
1 |
|
$className = '\\Kotori\\Core\\Cache\\' . ucfirst($this->adapter); |
|
82
|
1 |
|
$this->{$this->adapter} = new $className(); |
|
83
|
|
|
|
|
84
|
1 |
|
if (!$this->isSupported($this->adapter)) { |
|
85
|
|
|
Container::get('logger')->error('Cache adapter "{adapter}" is unavailable. Cache is now using "Dummy" adapter.', ['adapter' => $this->adapter]); |
|
86
|
|
|
$this->adapter = 'dummy'; |
|
87
|
|
|
} |
|
88
|
|
|
|
|
89
|
1 |
|
Hook::listen(__CLASS__); |
|
90
|
1 |
|
} |
|
91
|
|
|
|
|
92
|
|
|
/** |
|
93
|
|
|
* Fetches a value from the cache. |
|
94
|
|
|
* |
|
95
|
|
|
* @param string $key |
|
96
|
|
|
* @param mixed $default |
|
97
|
|
|
* @return mixed |
|
98
|
|
|
*/ |
|
99
|
6 |
|
public function get($key, $default = null) |
|
100
|
|
|
{ |
|
101
|
6 |
|
$value = $this->{$this->adapter}->get($this->keyPrefix . $key); |
|
102
|
6 |
|
return $value ? $value : $default; |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
|
|
/** |
|
106
|
|
|
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time. |
|
107
|
|
|
* |
|
108
|
|
|
* @param string $key |
|
109
|
|
|
* @param mixed $value |
|
110
|
|
|
* @param int $ttl |
|
111
|
|
|
* @return boolean |
|
112
|
|
|
*/ |
|
113
|
6 |
|
public function set($key, $value, $ttl = null) |
|
114
|
|
|
{ |
|
115
|
6 |
|
return $this->{$this->adapter}->set($this->keyPrefix . $key, $value, $ttl); |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
/** |
|
119
|
|
|
* Delete an item from the cache by its unique key. |
|
120
|
|
|
* |
|
121
|
|
|
* @param string $key |
|
122
|
|
|
* @return boolean |
|
123
|
|
|
*/ |
|
124
|
3 |
|
public function delete($key) |
|
125
|
|
|
{ |
|
126
|
3 |
|
return $this->{$this->adapter}->delete($this->keyPrefix . $key); |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
/** |
|
130
|
|
|
* Wipes clean the entire cache's keys. |
|
131
|
|
|
* |
|
132
|
|
|
* @return boolean |
|
133
|
|
|
*/ |
|
134
|
1 |
|
public function clear() |
|
135
|
|
|
{ |
|
136
|
1 |
|
return $this->{$this->adapter}->clear(); |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* Obtains multiple cache items by their unique keys. |
|
141
|
|
|
* |
|
142
|
|
|
* @param iterable $keys |
|
143
|
|
|
* @param mixed $default |
|
144
|
|
|
* @return iterable |
|
145
|
|
|
*/ |
|
146
|
2 |
|
public function getMultiple($keys, $default = null) |
|
147
|
|
|
{ |
|
148
|
2 |
|
$values = []; |
|
149
|
2 |
|
foreach ($keys as $key) { |
|
150
|
2 |
|
$values[$key] = $this->get($key, $default); |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
2 |
|
return $values; |
|
|
|
|
|
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
/** |
|
157
|
|
|
* Persists a set of key => value pairs in the cache, with an optional TTL. |
|
158
|
|
|
* |
|
159
|
|
|
* @param iterable $values |
|
160
|
|
|
* @param int $ttl |
|
161
|
|
|
* @return boolean |
|
162
|
|
|
*/ |
|
163
|
2 |
|
public function setMultiple($values, $ttl = null) |
|
164
|
|
|
{ |
|
165
|
2 |
|
$failTimes = 0; |
|
166
|
2 |
|
foreach ($values as $key => $value) { |
|
167
|
2 |
|
if (!$this->set($key, $value, $ttl)) { |
|
168
|
2 |
|
$failTimes++; |
|
169
|
|
|
} |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
2 |
|
return $failTimes == 0; |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
/** |
|
176
|
|
|
* Deletes multiple cache items in a single operation. |
|
177
|
|
|
* |
|
178
|
|
|
* @param iterable $keys |
|
179
|
|
|
* @return boolean |
|
180
|
|
|
*/ |
|
181
|
1 |
|
public function deleteMultiple($keys) |
|
182
|
|
|
{ |
|
183
|
1 |
|
$failTimes = 0; |
|
184
|
1 |
|
foreach ($keys as $key) { |
|
185
|
1 |
|
if (!$this->delete($key)) { |
|
186
|
1 |
|
$failTimes++; |
|
187
|
|
|
} |
|
188
|
|
|
} |
|
189
|
|
|
|
|
190
|
1 |
|
return $failTimes == 0; |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
/** |
|
194
|
|
|
* Determines whether an item is present in the cache. |
|
195
|
|
|
* |
|
196
|
|
|
* @param string $key |
|
197
|
|
|
* @return boolean |
|
198
|
|
|
*/ |
|
199
|
1 |
|
public function has($key) |
|
200
|
|
|
{ |
|
201
|
1 |
|
return (bool) $this->get($key); |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
/** |
|
205
|
|
|
* Is the requested driver supported in this environment? |
|
206
|
|
|
* |
|
207
|
|
|
* @param string $driver |
|
208
|
|
|
* @return array |
|
209
|
|
|
*/ |
|
210
|
1 |
|
public function isSupported($driver) |
|
211
|
|
|
{ |
|
212
|
1 |
|
static $support; |
|
213
|
|
|
|
|
214
|
1 |
|
if (!isset($support, $support[$driver])) { |
|
215
|
1 |
|
$support[$driver] = $this->{$driver}->isSupported(); |
|
216
|
|
|
} |
|
217
|
|
|
|
|
218
|
1 |
|
return $support[$driver]; |
|
219
|
|
|
} |
|
220
|
|
|
} |
|
221
|
|
|
|
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:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.