1<?php
2
3declare(strict_types=1);
4
5namespace PhpMyAdmin;
6
7use function count;
8use function explode;
9use function sprintf;
10
11final class ReplicationInfo
12{
13    /** @var string[] */
14    public $primaryVariables = [
15        'File',
16        'Position',
17        'Binlog_Do_DB',
18        'Binlog_Ignore_DB',
19    ];
20
21    /** @var string[] */
22    public $replicaVariables = [
23        'Slave_IO_State',
24        'Master_Host',
25        'Master_User',
26        'Master_Port',
27        'Connect_Retry',
28        'Master_Log_File',
29        'Read_Master_Log_Pos',
30        'Relay_Log_File',
31        'Relay_Log_Pos',
32        'Relay_Master_Log_File',
33        'Slave_IO_Running',
34        'Slave_SQL_Running',
35        'Replicate_Do_DB',
36        'Replicate_Ignore_DB',
37        'Replicate_Do_Table',
38        'Replicate_Ignore_Table',
39        'Replicate_Wild_Do_Table',
40        'Replicate_Wild_Ignore_Table',
41        'Last_Errno',
42        'Last_Error',
43        'Skip_Counter',
44        'Exec_Master_Log_Pos',
45        'Relay_Log_Space',
46        'Until_Condition',
47        'Until_Log_File',
48        'Until_Log_Pos',
49        'Master_SSL_Allowed',
50        'Master_SSL_CA_File',
51        'Master_SSL_CA_Path',
52        'Master_SSL_Cert',
53        'Master_SSL_Cipher',
54        'Master_SSL_Key',
55        'Seconds_Behind_Master',
56    ];
57
58    /** @var array */
59    private $primaryStatus;
60
61    /** @var array */
62    private $replicaStatus;
63
64    /** @var array */
65    private $multiPrimaryStatus;
66
67    /** @var array */
68    private $primaryInfo;
69
70    /** @var array */
71    private $replicaInfo;
72
73    /** @var DatabaseInterface */
74    private $dbi;
75
76    public function __construct(DatabaseInterface $dbi)
77    {
78        $this->dbi = $dbi;
79    }
80
81    public function load(?string $connection = null): void
82    {
83        global $url_params;
84
85        $this->setPrimaryStatus();
86
87        if (! empty($connection)) {
88            $this->setMultiPrimaryStatus();
89
90            if ($this->multiPrimaryStatus) {
91                $this->setDefaultPrimaryConnection($connection);
92                $url_params['master_connection'] = $connection;
93            }
94        }
95
96        $this->setReplicaStatus();
97        $this->setPrimaryInfo();
98        $this->setReplicaInfo();
99    }
100
101    private function setPrimaryStatus(): void
102    {
103        $this->primaryStatus = $this->dbi->fetchResult('SHOW MASTER STATUS');
104    }
105
106    /**
107     * @return array
108     */
109    public function getPrimaryStatus()
110    {
111        return $this->primaryStatus;
112    }
113
114    private function setReplicaStatus(): void
115    {
116        $this->replicaStatus = $this->dbi->fetchResult('SHOW SLAVE STATUS');
117    }
118
119    /**
120     * @return array
121     */
122    public function getReplicaStatus()
123    {
124        return $this->replicaStatus;
125    }
126
127    private function setMultiPrimaryStatus(): void
128    {
129        $this->multiPrimaryStatus = $this->dbi->fetchResult('SHOW ALL SLAVES STATUS');
130    }
131
132    private function setDefaultPrimaryConnection(string $connection): void
133    {
134        $this->dbi->query(sprintf('SET @@default_master_connection = \'%s\'', $this->dbi->escapeString($connection)));
135    }
136
137    private static function fill(array $status, string $key): array
138    {
139        if (empty($status[0][$key])) {
140            return [];
141        }
142
143        return explode(',', $status[0][$key]);
144    }
145
146    private function setPrimaryInfo(): void
147    {
148        $this->primaryInfo = ['status' => false];
149
150        if (count($this->primaryStatus) > 0) {
151            $this->primaryInfo['status'] = true;
152        }
153
154        if (! $this->primaryInfo['status']) {
155            return;
156        }
157
158        $this->primaryInfo['Do_DB'] = self::fill($this->primaryStatus, 'Binlog_Do_DB');
159        $this->primaryInfo['Ignore_DB'] = self::fill($this->primaryStatus, 'Binlog_Ignore_DB');
160    }
161
162    /**
163     * @return array
164     */
165    public function getPrimaryInfo(): array
166    {
167        return $this->primaryInfo;
168    }
169
170    private function setReplicaInfo(): void
171    {
172        $this->replicaInfo = ['status' => false];
173
174        if (count($this->replicaStatus) > 0) {
175            $this->replicaInfo['status'] = true;
176        }
177
178        if (! $this->replicaInfo['status']) {
179            return;
180        }
181
182        $this->replicaInfo['Do_DB'] = self::fill($this->replicaStatus, 'Replicate_Do_DB');
183        $this->replicaInfo['Ignore_DB'] = self::fill($this->replicaStatus, 'Replicate_Ignore_DB');
184        $this->replicaInfo['Do_Table'] = self::fill($this->replicaStatus, 'Replicate_Do_Table');
185        $this->replicaInfo['Ignore_Table'] = self::fill($this->replicaStatus, 'Replicate_Ignore_Table');
186        $this->replicaInfo['Wild_Do_Table'] = self::fill($this->replicaStatus, 'Replicate_Wild_Do_Table');
187        $this->replicaInfo['Wild_Ignore_Table'] = self::fill($this->replicaStatus, 'Replicate_Wild_Ignore_Table');
188    }
189
190    /**
191     * @return array
192     */
193    public function getReplicaInfo(): array
194    {
195        return $this->replicaInfo;
196    }
197}
198