Passed
Push — master ( ccb02c...6468b9 )
by Charlotte
02:16
created

PlasmaProvider::__wakeup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 2
rs 10
1
<?php
2
/**
3
 * Livia
4
 * Copyright 2017-2019 Charlotte Dunois, All Rights Reserved
5
 *
6
 * Website: https://charuru.moe
7
 * License: https://github.com/CharlotteDunois/Livia/blob/master/LICENSE
8
*/
9
10
namespace CharlotteDunois\Livia\Providers;
11
12
/**
13
 * Loads and stores settings associated with guilds in a database using Plasma. Requires the composer package `plasma/core` **and** a driver of your choice.
14
 */
15
class PlasmaProvider extends SettingProvider {
16
    /**
17
     * The DB connection.
18
     * @var \Plasma\ClientInterface
19
     */
20
    protected $db;
21
    
22
    /**
23
     * A collection of a guild's settings, mapped by guild ID.
24
     * @var \CharlotteDunois\Collect\Collection
25
     */
26
    protected $settings;
27
    
28
    /**
29
     * Constructs a new instance.
30
     * @param \Plasma\ClientInterface  $db
31
     */
32
    function __construct(\Plasma\ClientInterface $db) {
33
        $this->db = $db;
34
        $this->providerState = \CharlotteDunois\Livia\Providers\SettingProvider::STATE_READY;
35
        
36
        $this->settings = new \CharlotteDunois\Collect\Collection();
37
    }
38
    
39
    /**
40
     * Resets the state.
41
     * @return void
42
     * @internal
43
     */
44
    function __wakeup() {
45
        $this->providerState = \CharlotteDunois\Livia\Providers\SettingProvider::STATE_IDLE;
46
    }
47
    
48
    /**
49
     * Returns the Plasma client.
50
     * @return \Plasma\ClientInterface
51
     */
52
    function getClient() {
53
        return $this->db;
54
    }
55
    
56
    /**
57
     * {@inheritdoc}
58
     * @return \React\Promise\ExtendedPromiseInterface
59
     */
60
    function destroy() {
61
        $this->removeListeners();
62
        
63
        return $this->db->close();
64
    }
65
    
66
    /**
67
     * {@inheritdoc}
68
     * @return \React\Promise\ExtendedPromiseInterface
69
     */
70
    function init(\CharlotteDunois\Livia\Client $client): \React\Promise\ExtendedPromiseInterface {
71
        $this->client = $client;
72
        $this->attachListeners();
73
        
74
        return $this->db->execute('CREATE TABLE IF NOT EXISTS settings (guild VARCHAR(20) NOT NULL, settings TEXT NOT NULL, PRIMARY KEY (guild))')
75
            ->then(function () {
76
                return $this->db->execute('SELECT * FROM settings')->then(function (\Plasma\StreamQueryResultInterface $result) {
77
                    return $result->all()->then(function (\Plasma\QueryResultInterface $result) {
78
                        $rows = $result->getRows();
79
                        
80
                        foreach($rows as $row) {
81
                            $this->loadRow($row);
82
                        }
83
                        
84
                        if($this->settings->has('global')) {
85
                            return null;
86
                        }
87
                        
88
                        return $this->create('global')->then(function () {
89
                            return null;
90
                        });
91
                    });
92
                });
93
            });
94
    }
95
    
96
    /**
97
     * Creates a new table row in the db for the guild, if it doesn't exist already - otherwise loads the row.
98
     * @param string|\CharlotteDunois\Yasmin\Models\Guild  $guild
99
     * @param array|\ArrayObject                           $settings
100
     * @return \React\Promise\ExtendedPromiseInterface
101
     * @throws \InvalidArgumentException
102
     */
103
    function create($guild, $settings = array()): \React\Promise\ExtendedPromiseInterface {
104
        $guild = $this->getGuildID($guild);
105
        
106
        return $this->db->execute('SELECT * FROM settings WHERE guild = ?', array($guild))
107
            ->then(function ($result) use ($guild, &$settings) {
108
                return $result->all()->then(function (\Plasma\QueryResultInterface $result) use ($guild, $settings) {
109
                    $rows = $result->getRows();
110
                    
111
                    if(empty($rows)) {
112
                        if(!($settings instanceof \ArrayObject)) {
113
                            $settings = new \ArrayObject($settings, \ArrayObject::ARRAY_AS_PROPS);
114
                        }
115
                        
116
                        $this->settings->set($guild, $settings);
117
                        
118
                        return $this->db->execute(
119
                            'INSERT INTO settings (guild, settings) VALUES (?, ?)',
120
                            array($guild, \json_encode($settings))
121
                        );
122
                    } else {
123
                        $this->loadRow($rows[0]);
124
                    }
125
                });
126
            });
127
    }
128
    
129
    /**
130
     * {@inheritdoc}
131
     * @return mixed
132
     */
133
    function get($guild, string $key, $defaultValue = null) {
134
        $guild = $this->getGuildID($guild);
135
        
136
        if($this->settings->get($guild) === null) {
137
            $this->client->emit('warn', 'Settings of specified guild is not loaded - loading row - returning default value');
138
            
139
            $this->create($guild);
140
            return $defaultValue;
141
        }
142
        
143
        $settings = $this->settings->get($guild);
144
        if(\array_key_exists($key, $settings)) {
145
            return $settings[$key];
146
        }
147
        
148
        return $defaultValue;
149
    }
150
    
151
    /**
152
     * {@inheritdoc}
153
     * @return \React\Promise\ExtendedPromiseInterface
154
     */
155
    function set($guild, string $key, $value) {
156
        $guild = $this->getGuildID($guild);
157
        
158
        if($this->settings->get($guild) === null) {
159
            return $this->create($guild)->then(function () use ($guild, $key, $value) {
160
                $settings = $this->settings->get($guild);
161
                $settings[$key] = $value;
162
            
163
                return $this->db->execute('UPDATE settings SET settings = ? WHERE guild = ?', array(\json_encode($settings), $guild));
164
            });
165
        }
166
        
167
        $settings = $this->settings->get($guild);
168
        $settings[$key] = $value;
169
        
170
        return $this->db->execute('UPDATE settings SET settings = ? WHERE guild = ?', array(\json_encode($settings), $guild));
171
    }
172
    
173
    /**
174
     * {@inheritdoc}
175
     * @return \React\Promise\ExtendedPromiseInterface
176
     */
177
    function remove($guild, string $key) {
178
        $guild = $this->getGuildID($guild);
179
        
180
        if($this->settings->get($guild) === null) {
181
            $this->client->emit('warn', 'Settings of specified guild is not loaded - loading row');
182
            
183
            return $this->create($guild)->then(function () use ($guild, $key) {
184
                $settings = $this->settings->get($guild);
185
                unset($settings[$key]);
186
            
187
                return $this->db->execute('UPDATE settings SET settings = ? WHERE guild = ?', array(\json_encode($settings), $guild));
188
            });
189
        }
190
        
191
        $settings = $this->settings->get($guild);
192
        unset($settings[$key]);
193
        
194
        return $this->db->execute('UPDATE settings SET settings = ? WHERE guild = ?', array(\json_encode($settings), $guild));
195
    }
196
    
197
    /**
198
     * {@inheritdoc}
199
     * @return \React\Promise\ExtendedPromiseInterface
200
     */
201
    function clear($guild) {
202
        $guild = $this->getGuildID($guild);
203
        
204
        $this->settings->delete($guild);
205
        return $this->db->execute('DELETE FROM settings WHERE guild = ?', array($guild));
206
    }
207
    
208
    /**
209
     * Processes a database row.
210
     * @param array  $row
211
     * @return void
212
     */
213
    protected function loadRow(array $row) {
214
        $settings = \json_decode($row['settings'], true);
215
        if($settings === null) {
216
            $this->client->emit('warn', 'PlasmaProvider couldn\'t parse the settings stored for guild "'.$row['guild'].'". Error: '.\json_last_error_msg());
217
            return;
218
        }
219
        
220
        $settings = new \ArrayObject($settings, \ArrayObject::ARRAY_AS_PROPS);
221
        $this->settings->set($row['guild'], $settings);
222
        
223
        try {
224
            $this->setupGuild($row['guild']);
225
        } catch (\InvalidArgumentException $e) {
226
            $this->settings->delete($row['guild']);
227
            
228
            $this->db->execute('DELETE FROM settings WHERE guild = ?', array($row['guild']))
229
                ->done(null, array($this->client, 'handlePromiseRejection'));
230
        }
231
    }
232
}
233