Export::dump()   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 28
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.439
c 0
b 0
f 0
cc 6
eloc 22
nc 6
nop 1
1
<?php
2
3
namespace BrainExe\Core\Redis\Command;
4
5
use BrainExe\Core\Traits\RedisTrait;
6
use BrainExe\Core\Annotations\Command as CommandAnnotation;
7
use Exception;
8
use Symfony\Component\Console\Command\Command as SymfonyCommand;
9
use Symfony\Component\Console\Input\InputArgument;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Output\OutputInterface;
12
13
/**
14
 * @CommandAnnotation
15
 * @codeCoverageIgnore
16
 */
17
class Export extends SymfonyCommand
18
{
19
20
    use RedisTrait;
21
22
    const BULK_SIZE = 100;
23
24
    /**
25
     * {@inheritdoc}
26
     */
27
    protected function configure()
28
    {
29
        $this->setName('redis:export')
30
            ->setDescription('Export Redis database')
31
            ->addArgument('file', InputArgument::OPTIONAL, 'File to export');
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     */
37
    protected function execute(InputInterface $input, OutputInterface $output)
38
    {
39
        $keys = $this->redis->keys('*');
40
41
        $parts = [];
42
        foreach ($keys as $key) {
43
            $parts = array_merge($parts, $this->dump($key));
44
        }
45
46
        $content = implode("\n\n", $parts);
47
48
        $output->writeln($content);
49
50
        $file = $input->getArgument('file');
51
        if ($file) {
52
            file_put_contents($file, $content);
53
        }
54
    }
55
56
    /**
57
     * @param string $key
58
     * @return string[]
59
     * @throws Exception
60
     */
61
    protected function dump($key) : array
62
    {
63
        $parts = [];
64
65
        $type = $this->redis->type($key);
66
67
        switch ($type) {
68
            case 'string':
69
                $this->processString($key, $parts);
70
                break;
71
            case 'hash':
72
                $this->processHash($key, $parts);
73
                break;
74
            case 'set':
75
                $this->processSet($key, $parts);
76
                break;
77
            case 'zset':
78
                $this->processZset($key, $parts);
79
                break;
80
            case 'list':
81
                $this->processList($key, $parts);
82
                break;
83
            default:
84
                throw new Exception(sprintf('Unsupported type "%s" for key %s', $type, $key));
85
        }
86
87
        return $parts;
88
    }
89
90
    /**
91
     * @param string $value
92
     * @return string
93
     */
94
    private function escape($value)
95
    {
96
        $value = str_replace('"', '\\"', $value);
97
98
        return sprintf('"%s"', $value);
99
    }
100
101
    /**
102
     * @param string $key
103
     * @param array $parts
104
     */
105
    protected function processZset($key, array &$parts)
106
    {
107
        $zsets = $this->redis->zrange($key, 0, -1, 'WITHSCORES');
108
        if (!$zsets) {
109
            return;
110
        }
111
112 View Code Duplication
        foreach (array_chunk($zsets, self::BULK_SIZE, true) as $zset) {
113
            $part = [];
114
            $part[] = 'ZADD';
115
            $part[] = $this->escape($key);
116
117
            foreach ($zset as $value => $score) {
118
                $part[] = $this->escape($score);
119
                $part[] = $this->escape($value);
120
            }
121
            $parts[] = implode(' ', $part);
122
        }
123
    }
124
125
    /**
126
     * @param string $key
127
     * @param array $parts
128
     */
129
    protected function processSet($key, array &$parts)
130
    {
131
        $sets = $this->redis->smembers($key);
132
        if (!$sets) {
133
            return;
134
        }
135
136
        foreach (array_chunk($sets, self::BULK_SIZE) as $set) {
137
            $set = array_map([$this, 'escape'], $set);
138
            $parts[] = sprintf('SADD %s %s', $key, implode(' ', $set));
139
        }
140
    }
141
    /**
142
     * @param string $key
143
     * @param array $parts
144
     */
145
    protected function processList($key, array &$parts)
146
    {
147
        $members = $this->redis->lrange($key, 0, 100000);
148
        if (!$members) {
149
            return;
150
        }
151
152
        foreach ($members as $member) {
153
            $parts[] = sprintf('LPUSH %s %s', $key, $this->escape($member));
154
        }
155
    }
156
157
    /**
158
     * @param string $key
159
     * @param array $parts
160
     */
161
    private function processHash($key, array &$parts)
162
    {
163
        $hashes = $this->redis->hgetall($key);
164
        if (!$hashes) {
165
            return;
166
        }
167
168 View Code Duplication
        foreach (array_chunk($hashes, self::BULK_SIZE, true) as $hash) {
169
            $part = [];
170
            $part[] = 'HMSET';
171
            $part[] = $this->escape($key);
172
            foreach ($hash as $k => $val) {
173
                $part[] = $this->escape($k);
174
                $part[] = $this->escape($val);
175
            }
176
            $parts[] = implode(' ', $part);
177
        }
178
    }
179
180
    /**
181
     * @param string $key
182
     * @param array $parts
183
     */
184
    private function processString($key, array &$parts)
185
    {
186
        $parts[] = sprintf('SET %s %s', $key, $this->escape($this->redis->get($key)));
187
    }
188
}
189