markup
3 years ago
data-migration.php
3 years ago
migration-contract.php
5 years ago
migration.php
3 years ago
data-migration.php
457 lines
| 1 | <?php |
| 2 | |
| 3 | namespace WP_Social\Xs_Migration; |
| 4 | |
| 5 | |
| 6 | /** |
| 7 | * Class Data_Migration |
| 8 | * |
| 9 | * @package WP_Social\Xs_Migration |
| 10 | */ |
| 11 | abstract class Data_Migration implements Migration_Contract { |
| 12 | |
| 13 | const STATUS_DONE = 'done'; |
| 14 | const STATUS_QUEUED = 'queued'; |
| 15 | const STATUS_RUNNING = 'running'; |
| 16 | |
| 17 | const STATUS_FINISHED = 'finished'; |
| 18 | const STATUS_INITIATED = 'initiated'; |
| 19 | |
| 20 | const STATUS_METHOD_PAUSED = 'paused'; |
| 21 | const STATUS_METHOD_EXECUTED = 'executed'; |
| 22 | const STATUS_METHOD_EXECUTING = 'executing'; |
| 23 | |
| 24 | /** |
| 25 | * @param $txtDomain |
| 26 | * @param $versionFrom |
| 27 | * @param $versionTo |
| 28 | * |
| 29 | * @return mixed |
| 30 | */ |
| 31 | public function input($txtDomain, $versionFrom, $versionTo) { |
| 32 | |
| 33 | #$versionFrom = '1.1.9'; |
| 34 | #$versionTo = '1.2.0'; |
| 35 | $optionKey = 'data_migration_'.$txtDomain.'_log'; |
| 36 | |
| 37 | $from = str_replace('.', '_', trim($versionFrom)); |
| 38 | $to = str_replace('.', '_', trim($versionTo)); |
| 39 | |
| 40 | $frm = $this->makeFullVersionKey($from); |
| 41 | $trm = $this->makeFullVersionKey($to); |
| 42 | |
| 43 | $log = []; |
| 44 | $verCache = []; |
| 45 | $exPKey = $frm.'_'.$trm; |
| 46 | |
| 47 | $verCache['o_from'] = $versionFrom; |
| 48 | $verCache['u_from'] = $from; |
| 49 | $verCache['p_from'] = $frm; |
| 50 | $verCache['o_to'] = $versionTo; |
| 51 | $verCache['u_to'] = $to; |
| 52 | $verCache['p_to'] = $trm; |
| 53 | |
| 54 | $existingOption = get_option($optionKey); |
| 55 | |
| 56 | if(!$existingOption) { |
| 57 | |
| 58 | $existingOption = []; |
| 59 | $existingOption['status'] = self::STATUS_INITIATED; |
| 60 | $existingOption['plan_key'] = $exPKey; |
| 61 | |
| 62 | $cStack = $this->getCallStacks([], $frm, $trm); |
| 63 | |
| 64 | $existingOption['map'] = $cStack['map']; |
| 65 | |
| 66 | $existingOption['execution_plan'][$exPKey]['status'] = self::STATUS_RUNNING; |
| 67 | $existingOption['execution_plan'][$exPKey]['stack'] = $cStack['stack']; |
| 68 | $existingOption['execution_plan'][$exPKey]['available'] = $cStack['func']; |
| 69 | $existingOption['execution_plan'][$exPKey]['executing'] = ''; |
| 70 | $existingOption['execution_plan'][$exPKey]['executed'] = []; |
| 71 | $existingOption['execution_plan'][$exPKey]['failed'] = []; |
| 72 | $existingOption['execution_plan'][$exPKey]['progress'] = []; |
| 73 | $existingOption['execution_plan'][$exPKey]['log'] = []; |
| 74 | $existingOption['execution_plan'][$exPKey]['version'] = $verCache; |
| 75 | |
| 76 | $existingOption['plan_log'][$exPKey] = 'Execution plan '.$exPKey. ' initiated at '.date('Y-m-d H:i:s'). ' .' . PHP_EOL; |
| 77 | |
| 78 | $log[] = 'Conversion initiated for version '. $versionFrom .' to '. $versionTo. ' at '.date('Y-m-d H:i:s').'.'; |
| 79 | $log[] = 'Execution plan prepared.'; |
| 80 | $log[] = 'Execution plan saved into database.'; |
| 81 | |
| 82 | |
| 83 | if(empty($existingOption['execution_plan'][$exPKey]['stack'])) { |
| 84 | |
| 85 | $log[] = '- No Conversion method found for '. $versionFrom .' to '. $versionTo .''; |
| 86 | $log[] = '-- exiting....'; |
| 87 | $log[] = 'Execution plan executed at '.date('Y-m-d H:i:s').'.'; |
| 88 | $log[] = '----------------------------------------------------------'; |
| 89 | |
| 90 | |
| 91 | $existingOption['execution_plan'][$exPKey]['status'] = self::STATUS_FINISHED; |
| 92 | $existingOption['execution_plan'][$exPKey]['log'] = $log; |
| 93 | $existingOption['plan_log'][$exPKey] .= 'Execution plan '.$exPKey. ' finished at '.date('Y-m-d H:i:s'). ' .' . PHP_EOL; |
| 94 | $existingOption['status'] = self::STATUS_FINISHED; |
| 95 | |
| 96 | //update_option($optionKey, $existingOption); |
| 97 | |
| 98 | return [ |
| 99 | 'status' => 'success', |
| 100 | 'log' => $log, |
| 101 | ]; |
| 102 | } |
| 103 | |
| 104 | $curExecMethod = array_shift($existingOption['execution_plan'][$exPKey]['stack']); |
| 105 | |
| 106 | $existingOption['execution_plan'][$exPKey]['executing'] = $curExecMethod; |
| 107 | $existingOption['execution_plan'][$exPKey]['progress'][$curExecMethod] = [ |
| 108 | 'status' => self::STATUS_QUEUED, |
| 109 | 'check_list' => [], |
| 110 | ]; |
| 111 | |
| 112 | |
| 113 | $log[] = 'Execution plan '.$exPKey.' has started at '.date('Y-m-d H:i:s').'.'; |
| 114 | $log[] = '- Conversion method '. $curExecMethod . ' entered into queue at '.date('Y-m-d H:i:s').'.'; |
| 115 | $log[] = '-- Method '.$curExecMethod.' is executing....'; |
| 116 | |
| 117 | |
| 118 | $existingOption['execution_plan'][$exPKey]['log'] = $log; |
| 119 | $existingOption['execution_plan'][$exPKey]['log'][] = '-- ....'; |
| 120 | $existingOption['execution_plan'][$exPKey]['progress'][$curExecMethod]['status'] = self::STATUS_METHOD_EXECUTING; |
| 121 | |
| 122 | //update_option($optionKey, $existingOption); |
| 123 | |
| 124 | return $this->$curExecMethod($optionKey, $exPKey, $existingOption); |
| 125 | //return $existingOption; |
| 126 | } |
| 127 | |
| 128 | #We have reached this point, |
| 129 | #this means we do not have previously saved progress into database |
| 130 | #Now we are checking the status of the execution plan.... |
| 131 | |
| 132 | if($existingOption['status'] === self::STATUS_INITIATED) { |
| 133 | |
| 134 | #this is the initiated plan key, it must be defined by the programmer when status is set to initiated. |
| 135 | $planKey = $existingOption['plan_key']; |
| 136 | $planStat = $existingOption['execution_plan'][$planKey]['status']; |
| 137 | $log = $existingOption['execution_plan'][$planKey]['log']; |
| 138 | |
| 139 | if($planStat === self::STATUS_DONE) { |
| 140 | |
| 141 | #the execution plan has been executed but some how the status of the migration status is not finished |
| 142 | #If a plan is done then migration status must be finished............................................ |
| 143 | #So we will do nothing but change the migration status finished...................................... |
| 144 | #though this should never happened................................................................... |
| 145 | |
| 146 | $existingOption['status'] = self::STATUS_FINISHED; |
| 147 | |
| 148 | $log[] = '-- ! Migration status is initiated but execution plan ' .$planKey. ' is finished. This is a potential bug, we are finishing the execution plan at '.date('Y-m-d H:i:s'); |
| 149 | $log[] = '------------------------------------------------------------------------------'; |
| 150 | |
| 151 | $existingOption['execution_plan'][$planKey]['log'] = $log; |
| 152 | |
| 153 | //update_option($optionKey, $existingOption); |
| 154 | |
| 155 | return [ |
| 156 | 'status' => 'success', |
| 157 | 'log' => $log, |
| 158 | ]; |
| 159 | } |
| 160 | |
| 161 | #Execution plan is not finished................ |
| 162 | #We will execute all the methods from stack.... |
| 163 | $mtdKey = $existingOption['execution_plan'][$planKey]['executing']; |
| 164 | $mtdStat = $existingOption['execution_plan'][$planKey]['progress'][$mtdKey]['status']; |
| 165 | |
| 166 | if($mtdStat == self::STATUS_QUEUED || $mtdStat == self::STATUS_METHOD_PAUSED) { |
| 167 | |
| 168 | $existingOption['execution_plan'][$planKey]['progress'][$mtdKey] = [ |
| 169 | 'status' => self::STATUS_METHOD_EXECUTING, |
| 170 | ]; |
| 171 | |
| 172 | //update_option($optionKey, $existingOption); |
| 173 | |
| 174 | return $this->$mtdKey($optionKey, $planKey, $existingOption); |
| 175 | } |
| 176 | |
| 177 | if($mtdStat == self::STATUS_METHOD_EXECUTING) { |
| 178 | |
| 179 | #Someone initiated the method execution, so in this interval we will do nothing. |
| 180 | return $log; |
| 181 | } |
| 182 | |
| 183 | if($mtdStat == self::STATUS_METHOD_EXECUTED) { |
| 184 | |
| 185 | #the method we were executing and it was finished, now we will enter new method in execution queue. |
| 186 | #this method will enter into executed stack................................................... |
| 187 | |
| 188 | $existingOption['execution_plan'][$planKey]['executed'][] = $mtdKey; |
| 189 | $existingOption['execution_plan'][$planKey]['executing'] = ''; |
| 190 | |
| 191 | if(empty($existingOption['execution_plan'][$planKey]['stack'])) { |
| 192 | |
| 193 | #All the method from call stack has been executed |
| 194 | #Now we will finish the plan and the migration... |
| 195 | |
| 196 | $existingOption['status'] = self::STATUS_FINISHED; |
| 197 | $existingOption['execution_plan'][$planKey]['status'] = self::STATUS_DONE; |
| 198 | //$existingOption['execution_plan'][$planKey]['version'] = $verCache; |
| 199 | $existingOption['plan_key'] = ''; |
| 200 | |
| 201 | |
| 202 | $log[] = 'Conversion method '.$mtdKey.' has finished all its conversion and check list.'; |
| 203 | $log[] = 'Calling stack is finished.'; |
| 204 | $log[] = 'Execution plan executed and data conversion from '.$verCache['o_from']. ' to '.$verCache['o_to'].' at '.date('Y-m-d H:i:s').'.'; |
| 205 | $log[] = '...................................................................................'; |
| 206 | |
| 207 | $existingOption['execution_plan'][$planKey]['log'] = $log; |
| 208 | |
| 209 | //update_option($optionKey, $existingOption); |
| 210 | |
| 211 | return [ |
| 212 | 'status' => 'success', |
| 213 | 'log' => $log, |
| 214 | ]; |
| 215 | } |
| 216 | |
| 217 | #Call stack is not finished, |
| 218 | #So we will queued the next method from stack |
| 219 | |
| 220 | $nx = array_pop($existingOption['execution_plan'][$planKey]['stack']); |
| 221 | |
| 222 | $existingOption['execution_plan'][$planKey]['executing'] = $nx; |
| 223 | |
| 224 | $existingOption['execution_plan'][$planKey]['progress'][$nx] = [ |
| 225 | 'status' => self::STATUS_METHOD_EXECUTING, |
| 226 | 'check_list' => [], |
| 227 | ]; |
| 228 | |
| 229 | $log[] = '- Conversion method '. $nx . ' entered into queue at '.date('Y-m-d H:i:s').'.'; |
| 230 | $log[] = '-- Method '. $nx. ' is executing...'; |
| 231 | |
| 232 | $existingOption['execution_plan'][$planKey]['log'] = $log; |
| 233 | $existingOption['execution_plan'][$planKey]['log'][] = '-- ....'; |
| 234 | |
| 235 | //update_option($optionKey, $existingOption); |
| 236 | |
| 237 | return $this->$nx($optionKey, $planKey, $existingOption); |
| 238 | } |
| 239 | |
| 240 | return [ |
| 241 | 'status' => 'error', |
| 242 | 'log' => [ |
| 243 | 'Execution mtdStat error, method undefined.' |
| 244 | ], |
| 245 | ]; |
| 246 | } |
| 247 | |
| 248 | if($existingOption['status'] === self::STATUS_FINISHED) { |
| 249 | |
| 250 | #In scenario could happen like someone initiated plan 001003003_001003004 |
| 251 | #and now they want to migrate/initiate plan 001003004_001003005.......... |
| 252 | #........................................................................ |
| 253 | |
| 254 | if(empty($existingOption['execution_plan'][$exPKey])) { |
| 255 | |
| 256 | $existingOption['status'] = self::STATUS_INITIATED; |
| 257 | $existingOption['plan_key'] = $exPKey; |
| 258 | |
| 259 | $cStack = $this->getCallStacks([], $frm, $trm); |
| 260 | |
| 261 | $existingOption['map'] = $cStack['map']; |
| 262 | |
| 263 | $existingOption['execution_plan'][$exPKey]['status'] = self::STATUS_RUNNING; |
| 264 | $existingOption['execution_plan'][$exPKey]['stack'] = $cStack['stack']; |
| 265 | $existingOption['execution_plan'][$exPKey]['available'] = $cStack['func']; |
| 266 | $existingOption['execution_plan'][$exPKey]['executing'] = ''; |
| 267 | $existingOption['execution_plan'][$exPKey]['executed'] = []; |
| 268 | $existingOption['execution_plan'][$exPKey]['failed'] = []; |
| 269 | $existingOption['execution_plan'][$exPKey]['progress'] = []; |
| 270 | $existingOption['execution_plan'][$exPKey]['log'] = []; |
| 271 | $existingOption['execution_plan'][$exPKey]['version'] = $verCache; |
| 272 | |
| 273 | $existingOption['plan_log'][$exPKey] = 'Execution plan '.$exPKey. ' initiated at '.date('Y-m-d H:i:s'). ' .' . PHP_EOL; |
| 274 | |
| 275 | $log[] = 'Conversion initiated for version '. $versionFrom .' to '. $versionTo. ' at '.date('Y-m-d H:i:s').'.'; |
| 276 | $log[] = 'Execution plan prepared.'; |
| 277 | $log[] = 'Execution plan saved into database.'; |
| 278 | |
| 279 | |
| 280 | if(empty($existingOption['execution_plan'][$exPKey]['stack'])) { |
| 281 | |
| 282 | $log[] = '- No Conversion method found for '. $versionFrom .' to '. $versionTo .''; |
| 283 | $log[] = '-- exiting the execution plan....'; |
| 284 | $log[] = 'Calling stack is finished.'; |
| 285 | $log[] = 'Execution plan executed and data conversion from '.$versionFrom. ' to '.$versionTo.' at '.date('Y-m-d H:i:s').'.'; |
| 286 | $log[] = '...................................................................................'; |
| 287 | |
| 288 | $existingOption['execution_plan'][$exPKey]['status'] = self::STATUS_FINISHED; |
| 289 | $existingOption['execution_plan'][$exPKey]['log'] = $log; |
| 290 | $existingOption['plan_log'][$exPKey] .= 'Execution plan '.$exPKey. ' finished at '.date('Y-m-d H:i:s'). ' .' . PHP_EOL; |
| 291 | $existingOption['status'] = self::STATUS_FINISHED; |
| 292 | |
| 293 | //update_option($optionKey, $existingOption); |
| 294 | |
| 295 | return [ |
| 296 | 'status' => 'success', |
| 297 | 'log' => $log, |
| 298 | ]; |
| 299 | } |
| 300 | |
| 301 | $curExecMethod = array_shift($existingOption['execution_plan'][$exPKey]['stack']); |
| 302 | |
| 303 | $existingOption['execution_plan'][$exPKey]['executing'] = $curExecMethod; |
| 304 | $existingOption['execution_plan'][$exPKey]['progress'][$curExecMethod] = [ |
| 305 | 'status' => self::STATUS_QUEUED, |
| 306 | 'check_list' => [], |
| 307 | ]; |
| 308 | |
| 309 | |
| 310 | $log[] = 'Execution plan '.$exPKey.' has started at '.date('Y-m-d H:i:s').'.'; |
| 311 | $log[] = '- Conversion method '. $curExecMethod . ' entered into queue at '.date('Y-m-d H:i:s').'.'; |
| 312 | $log[] = '-- Method '.$curExecMethod.' is executing....'; |
| 313 | |
| 314 | $existingOption['execution_plan'][$exPKey]['log'] = $log; |
| 315 | $existingOption['execution_plan'][$exPKey]['log'][] = '-- ....'; |
| 316 | $existingOption['execution_plan'][$exPKey]['progress'][$curExecMethod]['status'] = self::STATUS_METHOD_EXECUTING; |
| 317 | |
| 318 | //update_option($optionKey, $existingOption); |
| 319 | |
| 320 | return $this->$curExecMethod($optionKey, $exPKey, $existingOption); |
| 321 | //return $existingOption; |
| 322 | } |
| 323 | |
| 324 | |
| 325 | #This is the scenario where migration status is finished |
| 326 | #and there are also configuration with execution plan |
| 327 | #....................................................... |
| 328 | |
| 329 | #Now execution plan could be finished |
| 330 | #that means user already migrated version $from to $to [e.g 1.3.4 to 1.3.5] |
| 331 | #and they again trying to migrate same version to version |
| 332 | #.......................................................................... |
| 333 | if($existingOption['execution_plan'][$exPKey]['status'] === self::STATUS_DONE) { |
| 334 | |
| 335 | return [ |
| 336 | 'status' => 'success', |
| 337 | 'log' => $existingOption['execution_plan'][$exPKey]['log'], |
| 338 | ]; |
| 339 | } |
| 340 | |
| 341 | #In no scenario this should happened, only when there is a mistake from programmer or corrupt data. |
| 342 | |
| 343 | return [ |
| 344 | 'status' => 'error', |
| 345 | 'log' => [ |
| 346 | 'Could not act on given request. Logical flow detected.' |
| 347 | ], |
| 348 | ]; |
| 349 | } |
| 350 | |
| 351 | |
| 352 | return [ |
| 353 | 'status' => 'error', |
| 354 | 'log' => [ |
| 355 | 'Exiting...data is corrupted.' |
| 356 | ], |
| 357 | ]; |
| 358 | } |
| 359 | |
| 360 | |
| 361 | /** |
| 362 | * |
| 363 | * @param array $data |
| 364 | */ |
| 365 | public function output(array $data) { |
| 366 | |
| 367 | if(!empty($data['option'])) { |
| 368 | |
| 369 | foreach($data['option'] as $opKey => $opVal) { |
| 370 | |
| 371 | update_option($opKey, $opVal); |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | |
| 377 | /** |
| 378 | * |
| 379 | * @param $versionMap |
| 380 | * @param $frm |
| 381 | * @param $trm |
| 382 | * |
| 383 | * @return array |
| 384 | */ |
| 385 | private function getCallStacks($versionMap, $frm, $trm) { |
| 386 | |
| 387 | $callStack = []; |
| 388 | $conversionMethods = []; |
| 389 | $methods = get_class_methods($this); |
| 390 | |
| 391 | foreach($methods as $method) { |
| 392 | |
| 393 | if(substr($method, 0, 13) === 'convert_from_') { |
| 394 | |
| 395 | $conversionMethods[] = $method; |
| 396 | |
| 397 | $tmp = str_replace('convert_from_', '', $method); |
| 398 | $tmp = explode('_to_', $tmp); |
| 399 | |
| 400 | $vl = $this->makeFullVersionKey($tmp[0]); |
| 401 | $vh = $this->makeFullVersionKey($tmp[1]); |
| 402 | |
| 403 | $versionMap[$vl] = $tmp[0]; |
| 404 | $versionMap[$vh] = $tmp[1]; |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | |
| 409 | ksort($versionMap); |
| 410 | |
| 411 | foreach($versionMap as $k => $v) { |
| 412 | |
| 413 | if($k >= $frm && $k < $trm) { |
| 414 | |
| 415 | $fnc = ''; |
| 416 | |
| 417 | foreach($conversionMethods as $conversionMethod) { |
| 418 | |
| 419 | if(strpos($conversionMethod, 'convert_from_'.$v) !== false) { |
| 420 | |
| 421 | $fnc = $conversionMethod; |
| 422 | |
| 423 | break; |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | if(!empty($fnc)) { |
| 428 | $callStack[] = $fnc; |
| 429 | } |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | return [ |
| 434 | 'map' => $versionMap, |
| 435 | 'func' => $conversionMethods, |
| 436 | 'stack' => $callStack, |
| 437 | ]; |
| 438 | } |
| 439 | |
| 440 | /** |
| 441 | * |
| 442 | * @param $string |
| 443 | * |
| 444 | * @return string |
| 445 | */ |
| 446 | public function makeFullVersionKey($string) { |
| 447 | |
| 448 | $fr = explode('_', $string); |
| 449 | |
| 450 | $frm = array_map(function($item){ |
| 451 | return str_pad($item, 3, '0', STR_PAD_LEFT); |
| 452 | }, $fr); |
| 453 | |
| 454 | return implode('', $frm); |
| 455 | } |
| 456 | |
| 457 | } |