aimeos /
aimeos-base
| 1 | <?php |
||||
| 2 | |||||
| 3 | /** |
||||
| 4 | * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0 |
||||
| 5 | * @copyright Aimeos (aimeos.org), 2015-2025 |
||||
| 6 | * @package Base |
||||
| 7 | * @subpackage Cache |
||||
| 8 | */ |
||||
| 9 | |||||
| 10 | |||||
| 11 | namespace Aimeos\Base\Cache; |
||||
| 12 | |||||
| 13 | |||||
| 14 | /** |
||||
| 15 | * Database cache class. |
||||
| 16 | * |
||||
| 17 | * @package Base |
||||
| 18 | * @subpackage Cache |
||||
| 19 | */ |
||||
| 20 | class DB |
||||
| 21 | extends \Aimeos\Base\Cache\Base |
||||
| 22 | implements \Aimeos\Base\Cache\Iface |
||||
| 23 | { |
||||
| 24 | private \Aimeos\Base\DB\Connection\Iface $conn; |
||||
| 25 | private array $sql; |
||||
| 26 | |||||
| 27 | |||||
| 28 | /** |
||||
| 29 | * Initializes the object instance. |
||||
| 30 | * |
||||
| 31 | * The config array must contain these statement: |
||||
| 32 | * [delete] => |
||||
| 33 | * DELETE FROM cachetable WHERE :cond |
||||
| 34 | * [deletebytag] => |
||||
| 35 | * DELETE FROM cachetable WHERE id IN ( |
||||
| 36 | * SELECT tid FROM cachetagtable WHERE :cond |
||||
| 37 | * ) |
||||
| 38 | * [get] => |
||||
| 39 | * SELECT id, value, expire FROM cachetable WHERE :cond |
||||
| 40 | * [set] => |
||||
| 41 | * INSERT INTO cachetable ( id, expire, value ) VALUES ( ?, ?, ? ) |
||||
| 42 | * [settag] => |
||||
| 43 | * INSERT INTO cachetagtable ( tid, tname ) VALUES ( ?, ? ) |
||||
| 44 | * |
||||
| 45 | * For using a different database connection, the name of the database connection |
||||
| 46 | * can be also given in the "config" parameter. In this case, use e.g. |
||||
| 47 | * config['dbname'] = 'db-cache' |
||||
| 48 | * |
||||
| 49 | * @param array $config Associative list with SQL statements |
||||
| 50 | * @param \Aimeos\Base\DB\Connection\Iface $conn Database connection |
||||
| 51 | */ |
||||
| 52 | public function __construct( array $config, \Aimeos\Base\DB\Connection\Iface $conn ) |
||||
| 53 | { |
||||
| 54 | $this->sql = $config; |
||||
| 55 | $this->conn = $conn; |
||||
| 56 | } |
||||
| 57 | |||||
| 58 | |||||
| 59 | /** |
||||
| 60 | * Removes all expired cache entries. |
||||
| 61 | * |
||||
| 62 | * @inheritDoc |
||||
| 63 | * |
||||
| 64 | * @return bool True on success and false on failure |
||||
| 65 | */ |
||||
| 66 | public function cleanup() : bool |
||||
| 67 | { |
||||
| 68 | try |
||||
| 69 | { |
||||
| 70 | $this->conn->create( $this->sql( 'cleanup' ) ) |
||||
| 71 | ->bind( 1, date( 'Y-m-d H:i:00' ) ) |
||||
| 72 | ->execute()->finish(); |
||||
| 73 | } |
||||
| 74 | catch( \Exception $e ) |
||||
| 75 | { |
||||
| 76 | return false; |
||||
| 77 | } |
||||
| 78 | |||||
| 79 | return true; |
||||
| 80 | } |
||||
| 81 | |||||
| 82 | |||||
| 83 | /** |
||||
| 84 | * Removes all entries of the site from the cache. |
||||
| 85 | * |
||||
| 86 | * @inheritDoc |
||||
| 87 | * |
||||
| 88 | * @return bool True on success and false on failure |
||||
| 89 | */ |
||||
| 90 | public function clear() : bool |
||||
| 91 | { |
||||
| 92 | try { |
||||
| 93 | $this->conn->create( $this->sql( 'clear' ) )->execute()->finish(); |
||||
| 94 | } catch( \Exception $e ) { |
||||
| 95 | return false; |
||||
| 96 | } |
||||
| 97 | |||||
| 98 | return true; |
||||
| 99 | } |
||||
| 100 | |||||
| 101 | |||||
| 102 | /** |
||||
| 103 | * Removes the cache entries identified by the given keys. |
||||
| 104 | * |
||||
| 105 | * @inheritDoc |
||||
| 106 | * |
||||
| 107 | * @param iterable $keys List of key strings that identify the cache entries that should be removed |
||||
| 108 | * @return bool True if the items were successfully removed. False if there was an error. |
||||
| 109 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||||
| 110 | */ |
||||
| 111 | public function deleteMultiple( iterable $keys ) : bool |
||||
| 112 | { |
||||
| 113 | try |
||||
| 114 | { |
||||
| 115 | if( ( $cnt = count( $keys ) ) === 0 ) { |
||||
| 116 | return true; |
||||
| 117 | } |
||||
| 118 | |||||
| 119 | $pos = 1; |
||||
| 120 | $sql = $this->sql( 'delete' ); |
||||
| 121 | $sql = substr_replace( $sql, str_repeat( ',?', $cnt - 1 ), strrpos( $sql, '?' ) + 1, 0 ); |
||||
| 122 | |||||
| 123 | $stmt = $this->conn->create( $sql ); |
||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 124 | |||||
| 125 | foreach( $keys as $key ) { |
||||
| 126 | $stmt->bind( $pos++, $key ); |
||||
| 127 | } |
||||
| 128 | |||||
| 129 | $stmt->execute()->finish(); |
||||
| 130 | } |
||||
| 131 | catch( \Exception $e ) |
||||
| 132 | { |
||||
| 133 | return false; |
||||
| 134 | } |
||||
| 135 | |||||
| 136 | return true; |
||||
| 137 | } |
||||
| 138 | |||||
| 139 | |||||
| 140 | /** |
||||
| 141 | * Removes the cache entries identified by the given tags. |
||||
| 142 | * |
||||
| 143 | * @inheritDoc |
||||
| 144 | * |
||||
| 145 | * @param iterable $tags List of tag strings that are associated to one or |
||||
| 146 | * more cache entries that should be removed |
||||
| 147 | * @return bool True if the items were successfully removed. False if there was an error. |
||||
| 148 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||||
| 149 | */ |
||||
| 150 | public function deleteByTags( iterable $tags ) : bool |
||||
| 151 | { |
||||
| 152 | try |
||||
| 153 | { |
||||
| 154 | if( ( $cnt = count( $tags ) ) === 0 ) { |
||||
| 155 | return true; |
||||
| 156 | } |
||||
| 157 | |||||
| 158 | $pos = 1; |
||||
| 159 | $sql = $this->sql( 'deletebytag' ); |
||||
| 160 | $sql = substr_replace( $sql, str_repeat( ',?', $cnt - 1 ), strrpos( $sql, '?' ) + 1, 0 ); |
||||
| 161 | |||||
| 162 | $stmt = $this->conn->create( $sql ); |
||||
|
0 ignored issues
–
show
It seems like
$sql can also be of type array; however, parameter $sql of Aimeos\Base\DB\Connection\Iface::create() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 163 | |||||
| 164 | foreach( $tags as $tag ) { |
||||
| 165 | $stmt->bind( $pos++, $tag ); |
||||
| 166 | } |
||||
| 167 | |||||
| 168 | $stmt->execute()->finish(); |
||||
| 169 | } |
||||
| 170 | catch( \Exception $e ) |
||||
| 171 | { |
||||
| 172 | return false; |
||||
| 173 | } |
||||
| 174 | |||||
| 175 | return true; |
||||
| 176 | } |
||||
| 177 | |||||
| 178 | |||||
| 179 | /** |
||||
| 180 | * Returns the cached values for the given cache keys if available. |
||||
| 181 | * |
||||
| 182 | * @inheritDoc |
||||
| 183 | * |
||||
| 184 | * @param string[] $keys List of key strings for the requested cache entries |
||||
| 185 | * @param mixed $default Default value to return for keys that do not exist |
||||
| 186 | * @return iterable Associative list of key/value pairs for the requested cache |
||||
| 187 | * entries. If a cache entry doesn't exist, neither its key nor a value |
||||
| 188 | * will be in the result list |
||||
| 189 | * @throws \Aimeos\Base\Cache\Exception If the cache server doesn't respond |
||||
| 190 | */ |
||||
| 191 | public function getMultiple( iterable $keys, $default = null ) : iterable |
||||
| 192 | { |
||||
| 193 | try |
||||
| 194 | { |
||||
| 195 | if( ( $cnt = count( $keys ) ) === 0 ) { |
||||
| 196 | return true; |
||||
| 197 | } |
||||
| 198 | |||||
| 199 | $pos = 2; |
||||
| 200 | $list = []; |
||||
| 201 | |||||
| 202 | $sql = $this->sql( 'get' ); |
||||
| 203 | $sql = substr_replace( $sql, str_repeat( ',?', $cnt - 1 ), strrpos( $sql, '?' ) + 1, 0 ); |
||||
| 204 | |||||
| 205 | $stmt = $this->conn->create( $sql ) |
||||
|
0 ignored issues
–
show
It seems like
$sql can also be of type array; however, parameter $sql of Aimeos\Base\DB\Connection\Iface::create() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 206 | ->bind( 1, date( 'Y-m-d H:i:00' ) ); |
||||
| 207 | |||||
| 208 | foreach( $keys as $key ) { |
||||
| 209 | $stmt->bind( $pos++, $key ); |
||||
| 210 | } |
||||
| 211 | |||||
| 212 | $result = $stmt->execute(); |
||||
| 213 | |||||
| 214 | while( ( $row = $result->fetch() ) !== null ) { |
||||
| 215 | $list[$row['id']] = (string) $row['value']; |
||||
| 216 | } |
||||
| 217 | |||||
| 218 | foreach( $keys as $key ) |
||||
| 219 | { |
||||
| 220 | if( !isset( $list[$key] ) ) { |
||||
| 221 | $list[$key] = $default; |
||||
| 222 | } |
||||
| 223 | } |
||||
| 224 | } |
||||
| 225 | catch( \Exception $e ) |
||||
| 226 | { |
||||
| 227 | return []; |
||||
| 228 | } |
||||
| 229 | |||||
| 230 | return $list; |
||||
| 231 | } |
||||
| 232 | |||||
| 233 | |||||
| 234 | /** |
||||
| 235 | * Determines whether an item is present in the cache. |
||||
| 236 | * |
||||
| 237 | * @inheritDoc |
||||
| 238 | * |
||||
| 239 | * @param string $key The cache item key |
||||
| 240 | * @return bool True if cache entry is available, false if not |
||||
| 241 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||||
| 242 | */ |
||||
| 243 | public function has( string $key ) : bool |
||||
| 244 | { |
||||
| 245 | try |
||||
| 246 | { |
||||
| 247 | $return = false; |
||||
| 248 | $result = $this->conn->create( $this->sql( 'get' ) ) |
||||
| 249 | ->bind( 1, date( 'Y-m-d H:i:00' ) ) |
||||
| 250 | ->bind( 2, $key ) |
||||
| 251 | ->execute(); |
||||
| 252 | |||||
| 253 | while( $result->fetch() ) { |
||||
| 254 | $return = true; |
||||
| 255 | } |
||||
| 256 | } |
||||
| 257 | catch( \Exception $e ) |
||||
| 258 | { |
||||
| 259 | return false; |
||||
| 260 | } |
||||
| 261 | |||||
| 262 | return $return; |
||||
| 263 | } |
||||
| 264 | |||||
| 265 | |||||
| 266 | /** |
||||
| 267 | * Adds or overwrites the given key/value pairs in the cache, which is much |
||||
| 268 | * more efficient than setting them one by one using the set() method. |
||||
| 269 | * |
||||
| 270 | * @inheritDoc |
||||
| 271 | * |
||||
| 272 | * @param iterable $pairs Associative list of key/value pairs. Both must be a string |
||||
| 273 | * @param \DateInterval|int|string|null $expires Date interval object, |
||||
| 274 | * date/time string in "YYYY-MM-DD HH:mm:ss" format or as integer TTL value |
||||
| 275 | * when the cache entry will expiry |
||||
| 276 | * @param iterable $tags List of tags that should be associated to the cache entries |
||||
| 277 | * @return bool True on success and false on failure. |
||||
| 278 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||||
| 279 | */ |
||||
| 280 | public function setMultiple( iterable $pairs, $expires = null, iterable $tags = [] ) : bool |
||||
| 281 | { |
||||
| 282 | $keys = []; |
||||
| 283 | foreach( $pairs as $key => $v ) { |
||||
| 284 | $keys[] = $key; |
||||
| 285 | } |
||||
| 286 | |||||
| 287 | // Remove existing entries first to avoid duplicate key conflicts |
||||
| 288 | $this->deleteMultiple( $keys ); |
||||
| 289 | |||||
| 290 | try |
||||
| 291 | { |
||||
| 292 | $this->conn->begin(); |
||||
| 293 | $stmt = $this->conn->create( $this->sql( 'set' ) ); |
||||
| 294 | $stmtTag = $this->conn->create( $this->sql( 'settag' ) ); |
||||
| 295 | |||||
| 296 | foreach( $pairs as $key => $value ) |
||||
| 297 | { |
||||
| 298 | if( $expires instanceof \DateInterval ) { |
||||
| 299 | $expires = date_create()->add( $expires )->format( 'Y-m-d H:i:s' ); |
||||
| 300 | } elseif( is_int( $expires ) ) { |
||||
| 301 | $expires = date( 'Y-m-d H:i:s', time() + $expires ); |
||||
| 302 | } |
||||
| 303 | |||||
| 304 | $stmt->bind( 1, (string) $key ); |
||||
| 305 | $stmt->bind( 2, $expires ); |
||||
| 306 | $stmt->bind( 3, (string) $value ); |
||||
| 307 | $stmt->execute()->finish(); |
||||
| 308 | |||||
| 309 | foreach( $tags as $name ) |
||||
| 310 | { |
||||
| 311 | $stmtTag->bind( 1, (string) $key ); |
||||
| 312 | $stmtTag->bind( 2, (string) $name ); |
||||
| 313 | $stmtTag->execute()->finish(); |
||||
| 314 | } |
||||
| 315 | } |
||||
| 316 | |||||
| 317 | $this->conn->commit(); |
||||
| 318 | } |
||||
| 319 | catch( \Exception $e ) |
||||
| 320 | { |
||||
| 321 | $this->conn->rollback(); |
||||
| 322 | return false; |
||||
| 323 | } |
||||
| 324 | |||||
| 325 | return true; |
||||
| 326 | } |
||||
| 327 | |||||
| 328 | |||||
| 329 | /** |
||||
| 330 | * Retturns the SQL statement for the given name. |
||||
| 331 | * |
||||
| 332 | * @param string $name SQL statement |
||||
| 333 | * @throws \Aimeos\Base\Cache\Exception If SQL statement is not available |
||||
| 334 | */ |
||||
| 335 | protected function sql( string $name ) : string |
||||
| 336 | { |
||||
| 337 | if( isset( $this->sql[$name] ) ) { |
||||
| 338 | return $this->sql[$name]; |
||||
| 339 | } |
||||
| 340 | |||||
| 341 | throw new \Aimeos\Base\Cache\Exception( "SQL statement for $name is missing" ); |
||||
| 342 | } |
||||
| 343 | } |
||||
| 344 |