Replication.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <?php
  2. /**
  3. * Replication helpers
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin;
  7. use PhpMyAdmin\Dbal\ResultInterface;
  8. use PhpMyAdmin\Query\Compatibility;
  9. use function explode;
  10. use function mb_strtoupper;
  11. /**
  12. * PhpMyAdmin\Replication class
  13. */
  14. class Replication
  15. {
  16. /**
  17. * Extracts database or table name from string
  18. *
  19. * @param string $string contains "dbname.tablename"
  20. * @param string $what what to extract (db|table)
  21. *
  22. * @return string the extracted part
  23. */
  24. public function extractDbOrTable($string, $what = 'db')
  25. {
  26. $list = explode('.', $string);
  27. if ($what === 'db') {
  28. return $list[0];
  29. }
  30. return $list[1];
  31. }
  32. /**
  33. * Configures replication replica
  34. *
  35. * @param string $action possible values: START or STOP
  36. * @param string|null $control default: null,
  37. * possible values: SQL_THREAD or IO_THREAD or null.
  38. * If it is set to null, it controls both
  39. * SQL_THREAD and IO_THREAD
  40. * @param int $link mysql link
  41. *
  42. * @return ResultInterface|false|int output of DatabaseInterface::tryQuery
  43. */
  44. public function replicaControl(string $action, ?string $control, int $link)
  45. {
  46. global $dbi;
  47. $action = mb_strtoupper($action);
  48. $control = $control !== null ? mb_strtoupper($control) : '';
  49. if ($action !== 'START' && $action !== 'STOP') {
  50. return -1;
  51. }
  52. if ($control !== 'SQL_THREAD' && $control !== 'IO_THREAD' && $control != null) {
  53. return -1;
  54. }
  55. if ($dbi->isMySql() && $dbi->getVersion() >= 80022 || $dbi->isMariaDB() && $dbi->getVersion() >= 100501) {
  56. return $dbi->tryQuery($action . ' REPLICA ' . $control . ';', $link);
  57. }
  58. return $dbi->tryQuery($action . ' SLAVE ' . $control . ';', $link);
  59. }
  60. /**
  61. * Changes primary for replication replica
  62. *
  63. * @param string $user replication user on primary
  64. * @param string $password password for the user
  65. * @param string $host primary's hostname or IP
  66. * @param int $port port, where mysql is running
  67. * @param array $pos position of mysql replication, array should contain fields File and Position
  68. * @param bool $stop shall we stop replica?
  69. * @param bool $start shall we start replica?
  70. * @param int $link mysql link
  71. *
  72. * @return ResultInterface|false output of CHANGE MASTER mysql command
  73. */
  74. public function replicaChangePrimary(
  75. $user,
  76. $password,
  77. $host,
  78. $port,
  79. array $pos,
  80. bool $stop,
  81. bool $start,
  82. int $link
  83. ) {
  84. global $dbi;
  85. if ($stop) {
  86. $this->replicaControl('STOP', null, $link);
  87. }
  88. if ($dbi->isMySql() && $dbi->getVersion() >= 80023) {
  89. $out = $dbi->tryQuery(
  90. 'CHANGE REPLICATION SOURCE TO ' .
  91. 'SOURCE_HOST=\'' . $host . '\',' .
  92. 'SOURCE_PORT=' . ($port * 1) . ',' .
  93. 'SOURCE_USER=\'' . $user . '\',' .
  94. 'SOURCE_PASSWORD=\'' . $password . '\',' .
  95. 'SOURCE_LOG_FILE=\'' . $pos['File'] . '\',' .
  96. 'SOURCE_LOG_POS=' . $pos['Position'] . ';',
  97. $link
  98. );
  99. } else {
  100. $out = $dbi->tryQuery(
  101. 'CHANGE MASTER TO ' .
  102. 'MASTER_HOST=\'' . $host . '\',' .
  103. 'MASTER_PORT=' . ($port * 1) . ',' .
  104. 'MASTER_USER=\'' . $user . '\',' .
  105. 'MASTER_PASSWORD=\'' . $password . '\',' .
  106. 'MASTER_LOG_FILE=\'' . $pos['File'] . '\',' .
  107. 'MASTER_LOG_POS=' . $pos['Position'] . ';',
  108. $link
  109. );
  110. }
  111. if ($start) {
  112. $this->replicaControl('START', null, $link);
  113. }
  114. return $out;
  115. }
  116. /**
  117. * This function provides connection to remote mysql server
  118. *
  119. * @param string $user mysql username
  120. * @param string $password password for the user
  121. * @param string $host mysql server's hostname or IP
  122. * @param int $port mysql remote port
  123. * @param string $socket path to unix socket
  124. *
  125. * @return mixed mysql link on success
  126. */
  127. public function connectToPrimary(
  128. $user,
  129. $password,
  130. $host = null,
  131. $port = null,
  132. $socket = null
  133. ) {
  134. global $dbi;
  135. $server = [];
  136. $server['user'] = $user;
  137. $server['password'] = $password;
  138. $server['host'] = Core::sanitizeMySQLHost($host);
  139. $server['port'] = $port;
  140. $server['socket'] = $socket;
  141. // 5th parameter set to true means that it's an auxiliary connection
  142. // and we must not go back to login page if it fails
  143. return $dbi->connect(DatabaseInterface::CONNECT_AUXILIARY, $server);
  144. }
  145. /**
  146. * Fetches position and file of current binary log on primary
  147. *
  148. * @param int $link mysql link
  149. *
  150. * @return array an array containing File and Position in MySQL replication
  151. * on primary server, useful for {@see Replication::replicaChangePrimary()}.
  152. * @phpstan-return array{'File'?: string, 'Position'?: string}
  153. */
  154. public function replicaBinLogPrimary(int $link): array
  155. {
  156. global $dbi;
  157. $data = $dbi->fetchResult(Compatibility::getShowBinLogStatusStmt($dbi), null, null, $link);
  158. $output = [];
  159. if (! empty($data)) {
  160. $output['File'] = $data[0]['File'];
  161. $output['Position'] = $data[0]['Position'];
  162. }
  163. return $output;
  164. }
  165. }