| 1 |
<?php |
| 2 |
|
| 3 |
declare(strict_types=1); |
| 4 |
|
| 5 |
namespace Yatra\Migration; |
| 6 |
|
| 7 |
/** |
| 8 |
* Detects active Yatra Pro by plugin headers (not install folder slug) and requires version 3.0+ for migration. |
| 9 |
* |
| 10 |
* @package Yatra\Migration |
| 11 |
*/ |
| 12 |
final class ProMigrationReadiness |
| 13 |
{ |
| 14 |
public const MINIMUM_PRO_VERSION = '3.0.0'; |
| 15 |
|
| 16 |
/** |
| 17 |
* Normalize semver for PHP's version_compare. Short forms like "3.0" compare as *older* than "3.0.0" |
| 18 |
* unless padded (see PHP version_compare behavior). |
| 19 |
*/ |
| 20 |
private static function normalizeVersionForCompare(string $version): string |
| 21 |
{ |
| 22 |
$version = preg_replace('/^[vV]+/', '', trim($version)); |
| 23 |
$core = preg_split('/[-+]/', $version, 2)[0]; |
| 24 |
$segments = explode('.', $core); |
| 25 |
$segments = array_pad($segments, 3, '0'); |
| 26 |
|
| 27 |
return implode('.', array_slice(array_map('intval', $segments), 0, 3)); |
| 28 |
} |
| 29 |
|
| 30 |
/** |
| 31 |
* Plugins are considered Yatra Pro when Text Domain is `yatra-pro` or the plugin name contains "Yatra Pro" |
| 32 |
* (covers renamed install directories). |
| 33 |
* |
| 34 |
* @return list<array{file: string, name: string, version: string|null}> |
| 35 |
*/ |
| 36 |
public static function findActiveYatraProPlugins(): array |
| 37 |
{ |
| 38 |
if (!function_exists('get_plugin_data')) { |
| 39 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 40 |
} |
| 41 |
|
| 42 |
$active = (array) get_option('active_plugins', []); |
| 43 |
if (is_multisite()) { |
| 44 |
$network = get_site_option('active_sitewide_plugins', []); |
| 45 |
if (is_array($network)) { |
| 46 |
$active = array_merge($active, array_keys($network)); |
| 47 |
} |
| 48 |
$active = array_values(array_unique($active)); |
| 49 |
} |
| 50 |
|
| 51 |
$found = []; |
| 52 |
foreach ($active as $pluginFile) { |
| 53 |
$pluginFile = (string) $pluginFile; |
| 54 |
if ($pluginFile === '') { |
| 55 |
continue; |
| 56 |
} |
| 57 |
$path = WP_PLUGIN_DIR . '/' . $pluginFile; |
| 58 |
if (!is_readable($path)) { |
| 59 |
continue; |
| 60 |
} |
| 61 |
$data = get_plugin_data($path, false, false); |
| 62 |
$name = isset($data['Name']) ? (string) $data['Name'] : ''; |
| 63 |
$textDomain = isset($data['TextDomain']) ? (string) $data['TextDomain'] : ''; |
| 64 |
$rawVersion = isset($data['Version']) ? trim((string) $data['Version']) : ''; |
| 65 |
$version = $rawVersion !== '' ? $rawVersion : null; |
| 66 |
|
| 67 |
// Do not rely on install folder slug. Match official headers only (name may be "Yatra Pro", "Yatra Pro OLD", etc.). |
| 68 |
$isYatraPro = ($textDomain === 'yatra-pro') |
| 69 |
|| (stripos($name, 'Yatra Pro') === 0); |
| 70 |
if (!$isYatraPro) { |
| 71 |
continue; |
| 72 |
} |
| 73 |
|
| 74 |
$found[] = [ |
| 75 |
'file' => $pluginFile, |
| 76 |
'name' => $name, |
| 77 |
'version' => $version, |
| 78 |
]; |
| 79 |
} |
| 80 |
|
| 81 |
return $found; |
| 82 |
} |
| 83 |
|
| 84 |
/** |
| 85 |
* @return array{ |
| 86 |
* ready: bool, |
| 87 |
* pro_plugin_active: bool, |
| 88 |
* multiple_pro_plugins: bool, |
| 89 |
* pro_plugin_file: string|null, |
| 90 |
* pro_plugin_name: string|null, |
| 91 |
* pro_version: string|null, |
| 92 |
* active_pro_plugins: list<array{file: string, name: string, version: string|null}>, |
| 93 |
* minimum_pro_version: string, |
| 94 |
* warning_message: string |
| 95 |
* } |
| 96 |
*/ |
| 97 |
public static function getState(): array |
| 98 |
{ |
| 99 |
$plugins = self::findActiveYatraProPlugins(); |
| 100 |
$count = count($plugins); |
| 101 |
|
| 102 |
$multiple = $count > 1; |
| 103 |
$single = $count === 1 ? $plugins[0] : null; |
| 104 |
$version = $single['version'] ?? null; |
| 105 |
$versionOk = $version !== null |
| 106 |
&& version_compare( |
| 107 |
self::normalizeVersionForCompare($version), |
| 108 |
self::normalizeVersionForCompare(self::MINIMUM_PRO_VERSION), |
| 109 |
'>=' |
| 110 |
); |
| 111 |
$ready = !$multiple && $single !== null && $versionOk; |
| 112 |
|
| 113 |
$warning = ''; |
| 114 |
if (!$ready) { |
| 115 |
if ($multiple) { |
| 116 |
$list = implode(', ', array_map(static fn (array $p): string => $p['file'], $plugins)); |
| 117 |
$warning = sprintf( |
| 118 |
/* translators: %s: comma-separated list of plugin paths */ |
| 119 |
__('More than one Yatra Pro plugin is active (%s). Deactivate duplicates so only Yatra Pro 3.0 or newer remains. Pro-specific migration will not run correctly with multiple copies.', 'yatra'), |
| 120 |
$list |
| 121 |
); |
| 122 |
} elseif ($count === 0) { |
| 123 |
$warning = __('No Yatra Pro plugin is active. If you used Yatra Pro before, install and activate Yatra Pro 3.0 or newer first — Pro-related migration steps (additional services, some payment settings, Pro tables) will be skipped or incomplete.', 'yatra'); |
| 124 |
} else { |
| 125 |
$warning = sprintf( |
| 126 |
/* translators: %1$s: detected Yatra Pro version, %2$s: minimum required version */ |
| 127 |
__('Your active Yatra Pro version is %1$s. Update to Yatra Pro %2$s or newer before migrating. Pro-specific migration steps will not succeed on older versions.', 'yatra'), |
| 128 |
$version !== null ? $version : __('unknown', 'yatra'), |
| 129 |
'3.0' |
| 130 |
); |
| 131 |
} |
| 132 |
} |
| 133 |
|
| 134 |
return [ |
| 135 |
'ready' => $ready, |
| 136 |
'pro_plugin_active' => $count >= 1, |
| 137 |
'multiple_pro_plugins' => $multiple, |
| 138 |
'pro_plugin_file' => $single['file'] ?? null, |
| 139 |
'pro_plugin_name' => $single['name'] ?? null, |
| 140 |
'pro_version' => $version, |
| 141 |
'active_pro_plugins' => $plugins, |
| 142 |
'minimum_pro_version' => self::MINIMUM_PRO_VERSION, |
| 143 |
'warning_message' => $warning, |
| 144 |
]; |
| 145 |
} |
| 146 |
} |
| 147 |
|