data-migration.php
2 years ago
initiator.php
4 years ago
migration-contract.php
4 years ago
migration.php
2 years ago
data-migration.php
417 lines
| 1 | <?php |
| 2 | |
| 3 | namespace ElementsKit_Lite\Libs\Xs_Migration; |
| 4 | |
| 5 | abstract class Data_Migration implements Migration_Contract { |
| 6 | |
| 7 | |
| 8 | const SUB_ROUTINE_STATUS_INCOMPLETE = '__incomplete'; |
| 9 | const SUB_ROUTINE_STATUS_DONE = '__done'; |
| 10 | |
| 11 | |
| 12 | const GENERIC_STATUS_YES = 'yes'; |
| 13 | const GENERIC_STATUS_NO = 'no'; |
| 14 | |
| 15 | const STATUS_DONE = 'done'; |
| 16 | const STATUS_QUEUED = 'queued'; |
| 17 | const STATUS_RUNNING = 'running'; |
| 18 | |
| 19 | const STATUS_FINISHED = 'finished'; |
| 20 | const STATUS_INITIATED = 'initiated'; |
| 21 | |
| 22 | const STATUS_METHOD_PAUSED = 'paused'; |
| 23 | const STATUS_METHOD_EXECUTED = 'executed'; |
| 24 | const STATUS_METHOD_EXECUTING = 'executing'; |
| 25 | |
| 26 | private $new_text_domain = 'elementskit-lite'; |
| 27 | private $text_domain = 'elementskit-lite'; |
| 28 | private $max_iteration = 10; |
| 29 | |
| 30 | |
| 31 | /** |
| 32 | * @param $txtDomain |
| 33 | * @param $versionFrom |
| 34 | * @param $versionTo |
| 35 | * |
| 36 | * @return mixed |
| 37 | */ |
| 38 | public function input( $txtDomain, $versionFrom, $versionTo ) { |
| 39 | |
| 40 | #$versionFrom = '1.1.9'; |
| 41 | #$versionTo = '1.2.0'; |
| 42 | $optionKey = 'data_migration_' . $txtDomain . '_log'; |
| 43 | |
| 44 | $from = str_replace( '.', '_', trim( $versionFrom ) ); |
| 45 | $to = str_replace( '.', '_', trim( $versionTo ) ); |
| 46 | |
| 47 | $frm = $this->makeFullVersionKey( $from ); |
| 48 | $trm = $this->makeFullVersionKey( $to ); |
| 49 | |
| 50 | $existingOption = get_option( $optionKey ); |
| 51 | |
| 52 | if ( empty( $existingOption ) ) { |
| 53 | |
| 54 | $log = array(); |
| 55 | $log[] = 'Migration never has been done for this domain.'; |
| 56 | $log[] = 'Initiating migration for version ' . $versionFrom . ' to ' . $versionTo . ' at ' . gmdate( 'Y-m-d H:i:s' ) . ' .'; |
| 57 | $log[] = 'Scanning migration file for conversion methods.'; |
| 58 | |
| 59 | $cStack = $this->getCallStacks( array(), $frm, $trm ); |
| 60 | |
| 61 | $fn = array(); |
| 62 | |
| 63 | foreach ( $cStack['stack'] as $item ) { |
| 64 | |
| 65 | $fn[ $item ] = self::STATUS_QUEUED; |
| 66 | } |
| 67 | |
| 68 | $log[] = 'Execution plan prepared.'; |
| 69 | $log[] = 'Execution plan saved into database.'; |
| 70 | |
| 71 | $existingOption['_func'] = $fn; |
| 72 | $existingOption['_log'] = $log; |
| 73 | $existingOption['_status'] = self::STATUS_INITIATED; |
| 74 | |
| 75 | update_option( $optionKey, $existingOption ); |
| 76 | |
| 77 | return array( |
| 78 | 'status' => 'success', |
| 79 | 'log' => $existingOption, |
| 80 | 'log2' => $cStack, |
| 81 | ); |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Now we have something saved into database |
| 86 | * lets check the status first |
| 87 | * |
| 88 | */ |
| 89 | |
| 90 | if ( $existingOption['_status'] == self::STATUS_FINISHED ) { |
| 91 | |
| 92 | $log = $existingOption['_log']; |
| 93 | |
| 94 | /** |
| 95 | * Now we have to check up-to which version this migration is done |
| 96 | */ |
| 97 | |
| 98 | $up_to = $this->makeFullVersionKey( $existingOption['_last_version_scanned'] ); |
| 99 | |
| 100 | if ( $up_to < $trm ) { |
| 101 | |
| 102 | /** |
| 103 | * New version released of this plugin |
| 104 | * check if anything new need to migrate |
| 105 | */ |
| 106 | |
| 107 | $cStack = $this->getCallStacks( array(), $frm, $trm ); |
| 108 | $fn = $existingOption['_func']; |
| 109 | |
| 110 | $log[] = 'A new version update detected.'; |
| 111 | $log[] = 'Scanning for new migration method.'; |
| 112 | |
| 113 | $found = false; |
| 114 | |
| 115 | foreach ( $cStack['stack'] as $item ) { |
| 116 | |
| 117 | if ( isset( $fn[ $item ] ) ) { |
| 118 | continue; |
| 119 | } |
| 120 | |
| 121 | $fn[ $item ] = self::STATUS_QUEUED; |
| 122 | |
| 123 | $found = true; |
| 124 | } |
| 125 | |
| 126 | if ( $found ) { |
| 127 | |
| 128 | $log[] = 'New conversion method detected.'; |
| 129 | $log[] = 'Preparing execution plan.'; |
| 130 | $log[] = 'Execution plan saved into database.'; |
| 131 | |
| 132 | $existingOption['_func'] = $fn; |
| 133 | $existingOption['_log'] = $log; |
| 134 | $existingOption['_status'] = self::STATUS_INITIATED; |
| 135 | |
| 136 | update_option( $optionKey, $existingOption ); |
| 137 | |
| 138 | return array( |
| 139 | 'status' => 'success', |
| 140 | 'log' => $existingOption, |
| 141 | 'log2' => $cStack, |
| 142 | ); |
| 143 | |
| 144 | } else { |
| 145 | |
| 146 | $log[] = 'No new conversion method detected.'; |
| 147 | $log[] = 'Updating the migration plan as finished for version ' . $versionTo . ' at ' . gmdate( 'Y-m-d H:i:s' ) . '.'; |
| 148 | |
| 149 | $existingOption['_func'] = $fn; |
| 150 | $existingOption['_log'] = $log; |
| 151 | $existingOption['_status'] = self::STATUS_FINISHED; |
| 152 | $existingOption['_last_version_scanned'] = $versionTo; |
| 153 | $existingOption['_plan_up_to'] = $trm; |
| 154 | |
| 155 | update_option( $optionKey, $existingOption ); |
| 156 | |
| 157 | return array( |
| 158 | 'status' => 'success', |
| 159 | 'log' => $existingOption, |
| 160 | 'log2' => $cStack, |
| 161 | ); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * As status is finished and last scanned version is same as the current plugin version |
| 167 | * code execution should not come here |
| 168 | * If any case it come to this point we are updating the settings |
| 169 | */ |
| 170 | |
| 171 | $log[] = 'In no scenario, execution pointer should not come here [something is wrong...].'; |
| 172 | |
| 173 | $existingOption['_log'] = $log; |
| 174 | $existingOption['_last_version_scanned'] = $versionTo; |
| 175 | $existingOption['_plan_up_to'] = $trm; |
| 176 | |
| 177 | update_option( $optionKey, $existingOption ); |
| 178 | |
| 179 | return array( |
| 180 | 'status' => 'success', |
| 181 | 'log' => $existingOption, |
| 182 | ); |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * At this point status of the execution plan is not finished |
| 187 | * lets do the work |
| 188 | * |
| 189 | */ |
| 190 | $curExecMethod = ''; |
| 191 | $mtdStat = ''; |
| 192 | |
| 193 | foreach ( $existingOption['_func'] as $mtd => $stat ) { |
| 194 | |
| 195 | if ( $stat == self::STATUS_METHOD_EXECUTED ) { |
| 196 | continue; |
| 197 | } |
| 198 | |
| 199 | $curExecMethod = $mtd; |
| 200 | $mtdStat = $stat; |
| 201 | |
| 202 | break; |
| 203 | } |
| 204 | |
| 205 | if ( empty( $curExecMethod ) ) { |
| 206 | |
| 207 | /** |
| 208 | * All methods has been executed |
| 209 | */ |
| 210 | |
| 211 | $log = $existingOption['_log']; |
| 212 | |
| 213 | $log[] = 'All conversion method has been executed.'; |
| 214 | $log[] = 'Setting the migration plan as finished for version ' . $versionTo . ' at ' . gmdate( 'Y-m-d H:i:s' ) . '.'; |
| 215 | |
| 216 | $existingOption['_log'] = $log; |
| 217 | $existingOption['_status'] = self::STATUS_FINISHED; |
| 218 | $existingOption['_last_version_scanned'] = $versionTo; |
| 219 | $existingOption['_plan_up_to'] = $trm; |
| 220 | |
| 221 | update_option( $optionKey, $existingOption ); |
| 222 | |
| 223 | return array( |
| 224 | 'status' => 'success', |
| 225 | 'log' => $existingOption, |
| 226 | ); |
| 227 | } |
| 228 | |
| 229 | /** |
| 230 | * We have a conversion method to run whose status is not executed |
| 231 | * |
| 232 | */ |
| 233 | |
| 234 | if ( $mtdStat == self::STATUS_QUEUED ) { |
| 235 | |
| 236 | $log = $existingOption['_log']; |
| 237 | |
| 238 | $log[] = 'Conversion method ' . $curExecMethod . ' entered into queue at ' . gmdate( 'Y-m-d H:i:s' ) . '.'; |
| 239 | $log[] = '- Conversion method ' . $curExecMethod . ' has entered into execution phase at ' . gmdate( 'Y-m-d H:i:s' ); |
| 240 | |
| 241 | $fn = $existingOption['_func']; |
| 242 | |
| 243 | $fn[ $curExecMethod ] = self::STATUS_METHOD_EXECUTING; |
| 244 | |
| 245 | $existingOption['_func'] = $fn; |
| 246 | $existingOption['_log'] = $log; |
| 247 | |
| 248 | update_option( $optionKey, $existingOption ); |
| 249 | |
| 250 | return $this->$curExecMethod( $optionKey, $existingOption ); |
| 251 | } |
| 252 | |
| 253 | if ( $mtdStat == self::STATUS_METHOD_EXECUTING ) { |
| 254 | |
| 255 | return array( |
| 256 | 'status' => 'failed', |
| 257 | 'msg' => 'Another person already initiated the execution.', |
| 258 | 'log' => $existingOption['_log'], |
| 259 | ); |
| 260 | } |
| 261 | |
| 262 | if ( $mtdStat == self::STATUS_METHOD_PAUSED ) { |
| 263 | |
| 264 | $log = $existingOption['_log']; |
| 265 | |
| 266 | $log[] = '- Conversion method ' . $curExecMethod . ' has entered into executing phase at ' . gmdate( 'Y-m-d H:i:s' ); |
| 267 | |
| 268 | $fn = $existingOption['_func']; |
| 269 | |
| 270 | $fn[ $curExecMethod ] = self::STATUS_METHOD_EXECUTING; |
| 271 | |
| 272 | $existingOption['_func'] = $fn; |
| 273 | $existingOption['_log'] = $log; |
| 274 | |
| 275 | update_option( $optionKey, $existingOption ); |
| 276 | |
| 277 | return $this->$curExecMethod( $optionKey, $existingOption ); |
| 278 | } |
| 279 | |
| 280 | /** |
| 281 | * This is the scenario that never ever should occur |
| 282 | */ |
| 283 | return array( |
| 284 | 'status' => 'failed', |
| 285 | 'msg' => 'Overflow', |
| 286 | 'log' => array( |
| 287 | 'Exiting...data is corrupted.', |
| 288 | ), |
| 289 | ); |
| 290 | } |
| 291 | |
| 292 | |
| 293 | /** |
| 294 | * |
| 295 | * @param array $data |
| 296 | */ |
| 297 | public function output( array $data ) { |
| 298 | |
| 299 | if ( ! empty( $data['option'] ) ) { |
| 300 | |
| 301 | foreach ( $data['option'] as $opKey => $opVal ) { |
| 302 | |
| 303 | update_option( $opKey, $opVal ); |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | |
| 309 | /** |
| 310 | * |
| 311 | * @param $versionMap |
| 312 | * @param $frm |
| 313 | * @param $trm |
| 314 | * |
| 315 | * @return array |
| 316 | */ |
| 317 | private function getCallStacks( $versionMap, $frm, $trm ) { |
| 318 | |
| 319 | $callStack = array(); |
| 320 | $conversionMethods = array(); |
| 321 | $methods = get_class_methods( $this ); |
| 322 | |
| 323 | foreach ( $methods as $method ) { |
| 324 | |
| 325 | if ( substr( $method, 0, 13 ) === 'convert_from_' ) { |
| 326 | |
| 327 | $conversionMethods[] = $method; |
| 328 | |
| 329 | $tmp = str_replace( 'convert_from_', '', $method ); |
| 330 | $tmp = explode( '_to_', $tmp ); |
| 331 | |
| 332 | $vl = $this->makeFullVersionKey( $tmp[0] ); |
| 333 | $vh = $this->makeFullVersionKey( $tmp[1] ); |
| 334 | |
| 335 | $versionMap[ $vl ] = $tmp[0]; |
| 336 | $versionMap[ $vh ] = $tmp[1]; |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | ksort( $versionMap ); |
| 341 | |
| 342 | foreach ( $versionMap as $k => $v ) { |
| 343 | |
| 344 | if ( $k >= $frm && $k < $trm ) { |
| 345 | |
| 346 | $fnc = ''; |
| 347 | |
| 348 | foreach ( $conversionMethods as $conversionMethod ) { |
| 349 | |
| 350 | if ( strpos( $conversionMethod, 'convert_from_' . $v ) !== false ) { |
| 351 | |
| 352 | $fnc = $conversionMethod; |
| 353 | |
| 354 | break; |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | if ( ! empty( $fnc ) ) { |
| 359 | $callStack[] = $fnc; |
| 360 | } |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | return array( |
| 365 | 'map' => $versionMap, |
| 366 | 'func' => $conversionMethods, |
| 367 | 'stack' => $callStack, |
| 368 | ); |
| 369 | } |
| 370 | |
| 371 | |
| 372 | /** |
| 373 | * |
| 374 | * @param $string |
| 375 | * |
| 376 | * @return string |
| 377 | */ |
| 378 | public function makeFullVersionKey( $string ) { |
| 379 | |
| 380 | $fr = explode( '_', $string ); |
| 381 | |
| 382 | $frm = array_map( |
| 383 | function( $item ) { |
| 384 | return str_pad( $item, 3, '0', STR_PAD_LEFT ); |
| 385 | }, |
| 386 | $fr |
| 387 | ); |
| 388 | |
| 389 | return implode( '', $frm ); |
| 390 | } |
| 391 | |
| 392 | |
| 393 | /** |
| 394 | * @return string |
| 395 | */ |
| 396 | public function getNewTextDomain() { |
| 397 | return $this->new_text_domain; |
| 398 | } |
| 399 | |
| 400 | |
| 401 | /** |
| 402 | * @return string |
| 403 | */ |
| 404 | public function getTextDomain() { |
| 405 | return $this->text_domain; |
| 406 | } |
| 407 | |
| 408 | |
| 409 | /** |
| 410 | * @return int |
| 411 | */ |
| 412 | public function getMaxIteration() { |
| 413 | return $this->max_iteration; |
| 414 | } |
| 415 | |
| 416 | } |
| 417 |