PostgreSQL::unserialize()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 9
rs 10
1
<?php
2
3
namespace MatthiasMullie\Scrapbook\Adapters;
4
5
/**
6
 * PostgreSQL adapter. Basically just a wrapper over \PDO, but in an
7
 * exchangeable (KeyValueStore) interface.
8
 *
9
 * @author Matthias Mullie <[email protected]>
10
 * @copyright Copyright (c) 2014, Matthias Mullie. All rights reserved
11
 * @license LICENSE MIT
12
 */
13
class PostgreSQL extends SQL
14
{
15
    /**
16
     * @var bool
17
     */
18
    protected $conflictSupport = true;
19
20
    /**
21
     * {@inheritdoc}
22
     */
23
    public function flush()
24
    {
25
        return false !== $this->client->exec("TRUNCATE TABLE $this->table");
26
    }
27
28
    /**
29
     * {@inheritdoc}
30
     */
31
    public function set($key, $value, $expire = 0)
32
    {
33
        if (!$this->conflictSupport) {
34
            return parent::set($key, $value, $expire);
35
        }
36
37
        $this->clearExpired();
38
39
        $serialized = $this->serialize($value);
40
        $expiration = $this->expire($expire);
41
42
        $statement = $this->client->prepare(
43
            "INSERT INTO $this->table (k, v, e)
44
            VALUES (:key, :value, :expire) 
45
            ON CONFLICT (k) DO UPDATE SET v=EXCLUDED.v, e=EXCLUDED.e"
46
        );
47
48
        $statement->bindParam(':key', $key);
49
        $statement->bindParam(':value', $serialized, \PDO::PARAM_LOB, strlen($serialized));
50
        $statement->bindParam(':expire', $expiration);
51
        $statement->execute();
52
53
        // ON CONFLICT is not supported in versions < 9.5, in which case we'll
54
        // have to fall back on add/replace
55
        if ('42601' === $statement->errorCode()) {
56
            $this->conflictSupport = false;
57
58
            return $this->set($key, $value, $expire);
59
        }
60
61
        return 1 === $statement->rowCount();
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function get($key, &$token = null)
68
    {
69
        $return = parent::get($key, $token);
70
71
        if (null !== $token) {
72
            // BYTEA data return streams - we actually need the data in
73
            // serialized format, not some silly stream
74
            $token = $this->serialize($return);
75
        }
76
77
        return $return;
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function getMulti(array $keys, array &$tokens = null)
84
    {
85
        $return = parent::getMulti($keys, $tokens);
86
87
        foreach ($return as $key => $value) {
88
            // BYTEA data return streams - we actually need the data in
89
            // serialized format, not some silly stream
90
            $tokens[$key] = $this->serialize($value);
91
        }
92
93
        return $return;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    protected function init()
100
    {
101
        $this->client->exec(
102
            "CREATE TABLE IF NOT EXISTS $this->table (
103
                k VARCHAR NOT NULL PRIMARY KEY,
104
                v BYTEA,
105
                e TIMESTAMP NULL DEFAULT NULL
106
            )"
107
        );
108
        $this->client->exec("CREATE INDEX IF NOT EXISTS e_index ON $this->table (e)");
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    protected function unserialize($value)
115
    {
116
        // BYTEA data return streams. Even though it's not how init() will
117
        // configure the DB by default, it could be used instead!
118
        if (is_resource($value)) {
119
            $value = stream_get_contents($value);
120
        }
121
122
        return parent::unserialize($value);
123
    }
124
}
125