/htdocs/wp-content/plugins/redirection/database/database-status.php

https://gitlab.com/VTTE/sitios-vtte · PHP · 371 lines · 249 code · 70 blank · 52 comment · 27 complexity · 00ca69351df5ff1a0b580480ec5581f0 MD5 · raw file

  1. <?php
  2. class Red_Database_Status {
  3. // Used in < 3.7 versions of Redirection, but since migrated to general settings
  4. const OLD_DB_VERSION = 'redirection_version';
  5. const DB_UPGRADE_STAGE = 'redirection_database_stage';
  6. const RESULT_OK = 'ok';
  7. const RESULT_ERROR = 'error';
  8. const STATUS_OK = 'ok';
  9. const STATUS_NEED_INSTALL = 'need-install';
  10. const STATUS_NEED_UPDATING = 'need-update';
  11. const STATUS_FINISHED_INSTALL = 'finish-install';
  12. const STATUS_FINISHED_UPDATING = 'finish-update';
  13. private $stage = false;
  14. private $stages = [];
  15. private $status = false;
  16. private $result = false;
  17. private $reason = false;
  18. private $debug = [];
  19. public function __construct() {
  20. $this->status = self::STATUS_OK;
  21. if ( $this->needs_installing() ) {
  22. $this->status = self::STATUS_NEED_INSTALL;
  23. } elseif ( $this->needs_updating() ) {
  24. $this->status = self::STATUS_NEED_UPDATING;
  25. }
  26. $info = get_option( self::DB_UPGRADE_STAGE );
  27. if ( $info ) {
  28. $this->stage = isset( $info['stage'] ) ? $info['stage'] : false;
  29. $this->stages = isset( $info['stages'] ) ? $info['stages'] : [];
  30. $this->status = isset( $info['status'] ) ? $info['status'] : false;
  31. }
  32. }
  33. /**
  34. * Does the database need install
  35. *
  36. * @return bool true if needs installing, false otherwise
  37. */
  38. public function needs_installing() {
  39. $settings = red_get_options();
  40. if ( $settings['database'] === '' && $this->get_old_version() === false ) {
  41. return true;
  42. }
  43. return false;
  44. }
  45. /**
  46. * Does the current database need updating to the target
  47. *
  48. * @return bool true if needs updating, false otherwise
  49. */
  50. public function needs_updating() {
  51. // We need updating if we don't need to install, and the current version is less than target version
  52. if ( $this->needs_installing() === false && version_compare( $this->get_current_version(), REDIRECTION_DB_VERSION, '<' ) ) {
  53. return true;
  54. }
  55. // Also if we're still in the process of upgrading
  56. if ( $this->get_current_stage() ) {
  57. return true;
  58. }
  59. return false;
  60. }
  61. /**
  62. * Get current database version
  63. *
  64. * @return string Current database version
  65. */
  66. public function get_current_version() {
  67. $settings = red_get_options();
  68. if ( $settings['database'] !== '' ) {
  69. return $settings['database'];
  70. } elseif ( $this->get_old_version() !== false ) {
  71. $version = $this->get_old_version();
  72. // Upgrade the old value
  73. red_set_options( array( 'database' => $version ) );
  74. delete_option( self::OLD_DB_VERSION );
  75. $this->clear_cache();
  76. return $version;
  77. }
  78. return '';
  79. }
  80. private function get_old_version() {
  81. return get_option( self::OLD_DB_VERSION );
  82. }
  83. public function check_tables_exist() {
  84. $latest = Red_Database::get_latest_database();
  85. $missing = $latest->get_missing_tables();
  86. // No tables installed - do a fresh install
  87. if ( count( $missing ) === count( $latest->get_all_tables() ) ) {
  88. delete_option( Red_Database_Status::OLD_DB_VERSION );
  89. red_set_options( [ 'database' => '' ] );
  90. $this->clear_cache();
  91. $this->status = self::STATUS_NEED_INSTALL;
  92. $this->stop_update();
  93. } elseif ( count( $missing ) > 0 && version_compare( $this->get_current_version(), '2.3.3', 'ge' ) ) {
  94. // Some tables are missing - try and fill them in
  95. $latest->install();
  96. }
  97. }
  98. /**
  99. * Does the current database support a particular version
  100. *
  101. * @param string $version Target version
  102. * @return bool true if supported, false otherwise
  103. */
  104. public function does_support( $version ) {
  105. return version_compare( $this->get_current_version(), $version, 'ge' );
  106. }
  107. public function is_error() {
  108. return $this->result === self::RESULT_ERROR;
  109. }
  110. public function set_error( $error ) {
  111. global $wpdb;
  112. $this->result = self::RESULT_ERROR;
  113. $this->reason = str_replace( "\t", ' ', $error );
  114. if ( $wpdb->last_error ) {
  115. $this->debug[] = $wpdb->last_error;
  116. if ( strpos( $wpdb->last_error, 'command denied to user' ) !== false ) {
  117. $this->reason .= ' - ' . __( 'Insufficient database permissions detected. Please give your database user appropriate permissions.', 'redirection' );
  118. }
  119. }
  120. $latest = Red_Database::get_latest_database();
  121. $this->debug = array_merge( $this->debug, $latest->get_table_schema() );
  122. $this->debug[] = 'Stage: ' . $this->get_current_stage();
  123. }
  124. public function set_ok( $reason ) {
  125. $this->reason = $reason;
  126. $this->result = self::RESULT_OK;
  127. $this->debug = [];
  128. }
  129. /**
  130. * Stop current upgrade
  131. */
  132. public function stop_update() {
  133. $this->stage = false;
  134. $this->stages = [];
  135. $this->debug = [];
  136. delete_option( self::DB_UPGRADE_STAGE );
  137. $this->clear_cache();
  138. }
  139. public function finish() {
  140. $this->stop_update();
  141. if ( $this->status === self::STATUS_NEED_INSTALL ) {
  142. $this->status = self::STATUS_FINISHED_INSTALL;
  143. } elseif ( $this->status === self::STATUS_NEED_UPDATING ) {
  144. $this->status = self::STATUS_FINISHED_UPDATING;
  145. }
  146. }
  147. /**
  148. * Get current upgrade stage
  149. * @return string|bool Current stage name, or false if not upgrading
  150. */
  151. public function get_current_stage() {
  152. return $this->stage;
  153. }
  154. /**
  155. * Move current stage on to the next
  156. */
  157. public function set_next_stage() {
  158. $stage = $this->get_current_stage();
  159. if ( $stage ) {
  160. $stage = $this->get_next_stage( $stage );
  161. // Save next position
  162. if ( $stage ) {
  163. $this->set_stage( $stage );
  164. } else {
  165. $this->finish();
  166. }
  167. }
  168. }
  169. /**
  170. * Get current upgrade status
  171. *
  172. * @return array Database status array
  173. */
  174. public function get_json() {
  175. // Base information
  176. $result = [
  177. 'status' => $this->status,
  178. 'inProgress' => $this->stage !== false,
  179. ];
  180. // Add on version status
  181. if ( $this->status === self::STATUS_NEED_INSTALL || $this->status === self::STATUS_NEED_UPDATING ) {
  182. $result = array_merge(
  183. $result,
  184. $this->get_version_upgrade(),
  185. [ 'manual' => $this->get_manual_upgrade() ]
  186. );
  187. }
  188. // Add on upgrade status
  189. if ( $this->is_error() ) {
  190. $result = array_merge( $result, $this->get_version_upgrade(), $this->get_progress_status(), $this->get_error_status() );
  191. } elseif ( $result['inProgress'] ) {
  192. $result = array_merge( $result, $this->get_progress_status() );
  193. } elseif ( $this->status === self::STATUS_FINISHED_INSTALL || $this->status === self::STATUS_FINISHED_UPDATING ) {
  194. $result['complete'] = 100;
  195. $result['reason'] = $this->reason;
  196. }
  197. return $result;
  198. }
  199. private function get_error_status() {
  200. return [
  201. 'reason' => $this->reason,
  202. 'result' => self::RESULT_ERROR,
  203. 'debug' => $this->debug,
  204. ];
  205. }
  206. private function get_progress_status() {
  207. $complete = 0;
  208. if ( $this->stage ) {
  209. $complete = round( ( array_search( $this->stage, $this->stages, true ) / count( $this->stages ) ) * 100, 1 );
  210. }
  211. return [
  212. 'complete' => $complete,
  213. 'result' => self::RESULT_OK,
  214. 'reason' => $this->reason,
  215. ];
  216. }
  217. private function get_version_upgrade() {
  218. return [
  219. 'current' => $this->get_current_version() ? $this->get_current_version() : '-',
  220. 'next' => REDIRECTION_DB_VERSION,
  221. 'time' => microtime( true ),
  222. ];
  223. }
  224. /**
  225. * Set the status information for a database upgrade
  226. */
  227. public function start_install( array $upgrades ) {
  228. $this->set_stages( $upgrades );
  229. $this->status = self::STATUS_NEED_INSTALL;
  230. }
  231. public function start_upgrade( array $upgrades ) {
  232. $this->set_stages( $upgrades );
  233. $this->status = self::STATUS_NEED_UPDATING;
  234. }
  235. private function set_stages( array $upgrades ) {
  236. $this->stages = [];
  237. foreach ( $upgrades as $upgrade ) {
  238. $upgrader = Red_Database_Upgrader::get( $upgrade );
  239. $this->stages = array_merge( $this->stages, array_keys( $upgrader->get_stages() ) );
  240. }
  241. if ( count( $this->stages ) > 0 ) {
  242. $this->set_stage( $this->stages[0] );
  243. }
  244. }
  245. public function set_stage( $stage ) {
  246. $this->stage = $stage;
  247. $this->save_details();
  248. }
  249. private function save_details() {
  250. update_option( self::DB_UPGRADE_STAGE, [
  251. 'stage' => $this->stage,
  252. 'stages' => $this->stages,
  253. 'status' => $this->status,
  254. ] );
  255. $this->clear_cache();
  256. }
  257. private function get_manual_upgrade() {
  258. $queries = [];
  259. $database = new Red_Database();
  260. $upgraders = $database->get_upgrades_for_version( $this->get_current_version(), false );
  261. foreach ( $upgraders as $upgrade ) {
  262. $upgrade = Red_Database_Upgrader::get( $upgrade );
  263. $stages = $upgrade->get_stages();
  264. foreach ( array_keys( $stages ) as $stage ) {
  265. $queries = array_merge( $queries, $upgrade->get_queries_for_stage( $stage ) );
  266. }
  267. }
  268. return $queries;
  269. }
  270. private function get_next_stage( $stage ) {
  271. $database = new Red_Database();
  272. $upgraders = $database->get_upgrades_for_version( $this->get_current_version(), $this->get_current_stage() );
  273. if ( count( $upgraders ) === 0 ) {
  274. $upgraders = $database->get_upgrades_for_version( $this->get_current_version(), false );
  275. }
  276. $upgrader = Red_Database_Upgrader::get( $upgraders[0] );
  277. // Where are we in this?
  278. $pos = array_search( $this->stage, $this->stages, true );
  279. if ( $pos === count( $this->stages ) - 1 ) {
  280. $this->save_db_version( REDIRECTION_DB_VERSION );
  281. return false;
  282. }
  283. // Set current DB version
  284. $current_stages = array_keys( $upgrader->get_stages() );
  285. if ( array_search( $this->stage, $current_stages, true ) === count( $current_stages ) - 1 ) {
  286. $this->save_db_version( $upgraders[1]['version'] );
  287. }
  288. // Move on to next in current version
  289. return $this->stages[ $pos + 1 ];
  290. }
  291. public function save_db_version( $version ) {
  292. red_set_options( array( 'database' => $version ) );
  293. delete_option( self::OLD_DB_VERSION );
  294. $this->clear_cache();
  295. }
  296. private function clear_cache() {
  297. if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) && function_exists( 'wp_cache_flush' ) ) {
  298. wp_cache_flush();
  299. }
  300. }
  301. }