Completed
Push — master ( 53b21a...cc70b5 )
by Robbie
01:33
created

DatabaseStore::binaryDataJsonDecode()   A

Complexity

Conditions 6
Paths 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.2222
c 0
b 0
f 0
cc 6
nc 3
nop 1
1
<?php
2
3
namespace SilverStripe\HybridSessions\Store;
4
5
use SilverStripe\ORM\DB;
6
use SilverStripe\ORM\Connect\MySQLDatabase;
7
use SilverStripe\Core\ClassInfo;
8
use SilverStripe\Core\Convert;
9
use SilverStripe\HybridSessions\Store\BaseStore;
10
use Exception;
11
12
class DatabaseStore extends BaseStore
13
{
14
15
    /**
16
     * Determine if the DB is ready to use.
17
     *
18
     * @return bool
19
     * @throws Exception
20
     */
21
    protected function isDatabaseReady()
22
    {
23
        // Such as during setup of testsession prior to DB connection.
24
        if (!DB::is_active()) {
25
            return false;
26
        }
27
28
        // If we have a DB of the wrong type then complain
29
        if (!(DB::get_conn() instanceof MySQLDatabase)) {
30
            throw new Exception('HybridSessions\Store\DatabaseStore currently only works with MySQL databases');
31
        }
32
33
        // Prevent freakout during dev/build
34
        return ClassInfo::hasTable('HybridSessionDataObject');
35
    }
36
37
    public function open($save_path, $name)
38
    {
39
        // no-op
40
    }
41
42
    public function close()
43
    {
44
        // no-op
45
    }
46
47
    public function read($session_id)
48
    {
49
        if (!$this->isDatabaseReady()) {
50
            return null;
51
        }
52
53
        $query = sprintf(
54
            'SELECT "Data" FROM "HybridSessionDataObject"
55
            WHERE "SessionID" = \'%s\' AND "Expiry" >= %s',
56
            Convert::raw2sql($session_id),
57
            $this->getNow()
58
        );
59
60
        $result = DB::query($query);
61
62
        if ($result && $result->numRecords()) {
63
            $data = $result->first();
64
            $decoded = $this->binaryDataJsonDecode($data['Data']);
65
            return is_null($decoded) ? $data['Data'] : $decoded;
66
        }
67
    }
68
69
    public function write($session_id, $session_data)
70
    {
71
        if (!$this->isDatabaseReady()) {
72
            return false;
73
        }
74
75
        $expiry = $this->getNow() + $this->getLifetime();
76
77
        DB::query($str = sprintf(
78
            'INSERT INTO "HybridSessionDataObject" ("SessionID", "Expiry", "Data")
79
            VALUES (\'%1$s\', %2$u, \'%3$s\')
80
            ON DUPLICATE KEY UPDATE "Expiry" = %2$u, "Data" = \'%3$s\'',
81
            Convert::raw2sql($session_id),
82
            $expiry,
83
            Convert::raw2sql($this->binaryDataJsonEncode($session_data))
84
        ));
85
86
        return true;
87
    }
88
89
    public function destroy($session_id)
90
    {
91
        // NOP
92
    }
93
94
    public function gc($maxlifetime)
95
    {
96
        if (!$this->isDatabaseReady()) {
97
            return;
98
        }
99
100
        DB::query(sprintf(
101
            'DELETE FROM "HybridSessionDataObject" WHERE "Expiry" < %u',
102
            $this->getNow()
103
        ));
104
    }
105
106
    /**
107
    * Encode binary data into ASCII string (a subset of UTF-8)
108
    *
109
    * Silverstripe <= 4.4 does not have a binary db field implementation, so we have to store
110
    * binary data as text
111
    *
112
    * @param string $data This is a binary blob
113
    *
114
    * @return string
115
    */
116
    private function binaryDataJsonEncode($data)
117
    {
118
        return json_encode([
119
            self::class,
120
            base64_encode($data)
121
        ]);
122
    }
123
124
    /**
125
     * Decode ASCII string into original binary data (a php string)
126
     *
127
     * Silverstripe <= 4.4 does not have a binary db field implementation, so we have to store
128
     * binary data as text
129
     *
130
     * @param string $text
131
     *
132
     * @param null|string
133
     */
134
    private function binaryDataJsonDecode($text)
135
    {
136
        $struct = json_decode($text, true, 2);
137
138
        if (!is_array($struct) || count($struct) !== 2) {
139
            return null;
140
        }
141
142
        if (!isset($struct[0]) || !isset($struct[1]) || $struct[0] !== self::class) {
143
            return null;
144
        }
145
146
        return base64_decode($struct[1]);
147
    }
148
}
149