aboutsummaryrefslogtreecommitdiff
path: root/ratatoeskr/sys/models/KVStorage.php
blob: 88c00e22b1e26a32edacd57cfdb48e554f33d7b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<?php

namespace r7r\cms\sys\models;

use DoesNotExistError;
use ArrayAccess;
use Iterator;
use Countable;
use r7r\cms\sys\Database;

/**
 * An abstract class for a KVStorage
 */
abstract class KVStorage implements Countable, ArrayAccess, Iterator
{
    private $keybuffer;
    private $counter;
    private $silent_mode;

    private $common_vals;

    private $stmt_get;
    private $stmt_unset;
    private $stmt_update;
    private $stmt_create;

    final protected function init(string $sqltable, array $common, Database $db)
    {
        $this->silent_mode = false;
        $this->keybuffer = [];

        $selector = "WHERE ";
        $fields = "";
        foreach ($common as $field => $val) {
            $selector .= "`$field` = ? AND ";
            $fields .= ", `$field`";
            $this->common_vals[] = $val;
        }

        $this->stmt_get = $db->prepStmt("SELECT `value` FROM `$sqltable` $selector `key` = ?");
        $this->stmt_unset = $db->prepStmt("DELETE FROM `$sqltable` $selector `key` = ?");
        $this->stmt_update = $db->prepStmt("UPDATE `$sqltable` SET `value` = ? $selector `key` = ?");
        $this->stmt_create = $db->prepStmt("INSERT INTO `$sqltable` (`key`, `value` $fields) VALUES (?,?" . str_repeat(",?", count($common)) . ")");

        $get_keys = $db->prepStmt("SELECT `key` FROM `$sqltable` $selector 1");
        $get_keys->execute($this->common_vals);
        while ($sqlrow = $get_keys->fetch()) {
            $this->keybuffer[] = $sqlrow["key"];
        }

        $this->counter = 0;
    }

    /**
     * Enable silent mode.
     *
     * If the silent mode is enabled, the KVStorage behaves even more like a PHP array, i.e. it just returns NULL,
     * if an unknown key was requested and does not throw an DoesNotExistError Exception.
     *
     * See also {@see KVStorage::disable_silent_mode()}
     */
    final public function enable_silent_mode()
    {
        $this->silent_mode = true;
    }

    /**
     * Disable silent mode.
     *
     * See also {@see KVStorage::enable_silent_mode()}
     */
    final public function disable_silent_mode()
    {
        $this->silent_mode = false;
    }

    /* Countable interface implementation */
    final public function count()
    {
        return count($this->keybuffer);
    }

    /* ArrayAccess interface implementation */
    final public function offsetExists($offset)
    {
        return in_array($offset, $this->keybuffer);
    }

    final public function offsetGet($offset)
    {
        if ($this->offsetExists($offset)) {
            $this->stmt_get->execute(array_merge($this->common_vals, [$offset]));
            $sqlrow = $this->stmt_get->fetch();
            $this->stmt_get->closeCursor();
            return unserialize(base64_decode($sqlrow["value"]));
        } elseif ($this->silent_mode) {
            return null;
        } else {
            throw new DoesNotExistError();
        }
    }

    final public function offsetUnset($offset)
    {
        if ($this->offsetExists($offset)) {
            unset($this->keybuffer[array_search($offset, $this->keybuffer)]);
            $this->keybuffer = array_merge($this->keybuffer);
            $this->stmt_unset->execute(array_merge($this->common_vals, [$offset]));
            $this->stmt_unset->closeCursor();
        }
    }

    final public function offsetSet($offset, $value)
    {
        if ($this->offsetExists($offset)) {
            $this->stmt_update->execute(array_merge([base64_encode(serialize($value))], $this->common_vals, [$offset]));
            $this->stmt_update->closeCursor();
        } else {
            $this->stmt_create->execute(array_merge([$offset, base64_encode(serialize($value))], $this->common_vals));
            $this->stmt_create->closeCursor();
            $this->keybuffer[] = $offset;
        }
    }

    /* Iterator interface implementation */
    final public function rewind()
    {
        return $this->counter = 0;
    }

    final public function current()
    {
        return $this->offsetGet($this->keybuffer[$this->counter]);
    }

    final public function key()
    {
        return $this->keybuffer[$this->counter];
    }

    final public function next()
    {
        ++$this->counter;
    }

    final public function valid()
    {
        return isset($this->keybuffer[$this->counter]);
    }
}