* @copyright Copyright (c) 2002-2026, Oliver Georgi * @license http://opensource.org/licenses/GPL-2.0 GNU GPL-2 **/ declare(strict_types=1); @ini_set('display_errors', '1'); @error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); @set_time_limit(0); @ini_set('memory_limit', '512M'); if (function_exists('ignore_user_abort')) { @ignore_user_abort(true); } const UPGRADE_VERSION = '2.0.0'; const UPGRADE_REPO = 'slackero/phpwcms'; const MIN_PHP_VERSION = '8.4.0'; const MIN_INSTALLED_REVISION = 401; function phpwcms_logo_svg(int $height = 30): string { return ''; } function upgrade_css(): string { return <<<'CSS' :root { --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --color-primary: #0d6efd; --color-primary-hover: #0b5ed7; --color-secondary: #6c757d; --color-secondary-hover: #5c636a; --color-success: #198754; --color-danger: #dc3545; --color-warning: #ffc107; --color-light: #f8f9fa; --color-border: #dee2e6; } *, *::before, *::after { box-sizing: border-box; } body { background-color: #f4f6f8; font-family: var(--font-sans); color: #1e293b; line-height: 1.5; margin: 0; padding: 0; } a { color: var(--color-primary); text-decoration: none; } a:hover { text-decoration: underline; } .upgrade-card { max-width: 840px; margin: 40px auto; border-radius: 8px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); background: #fff; overflow: hidden; } .upgrade-card-sm { max-width: 600px; } .upgrade-header { background-color: #4B6F92; color: #fff; padding: 16px 24px; } .upgrade-header.danger { background-color: var(--color-danger); } .upgrade-body { padding: 28px; } h4, h5 { margin-top: 0; font-weight: 600; } p { margin-top: 0; margin-bottom: 1rem; } .log-terminal { background: #1e1e1e; color: #e0e0e0; font-family: var(--font-mono); font-size: 13px; border-radius: 6px; padding: 15px; max-height: 280px; overflow-y: auto; margin-top: 20px; line-height: 1.6; } code { font-family: var(--font-mono); background: #f1f5f9; padding: 2px 6px; border-radius: 4px; font-size: 0.9em; color: #0f172a; } .form-label { display: inline-block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.95rem; } .form-control { display: block; width: 100%; padding: 0.5rem 0.75rem; font-size: 1rem; font-family: inherit; line-height: 1.5; color: #212529; background-color: #fff; border: 1px solid var(--color-border); border-radius: 6px; transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; } .form-control:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-check { display: block; min-height: 1.5rem; padding-left: 1.6em; margin-bottom: 0.5rem; position: relative; } .form-check-input { float: left; margin-left: -1.6em; width: 1.15em; height: 1.15em; margin-top: 0.3em; vertical-align: top; background-color: #fff; border: 1px solid #adb5bd; border-radius: 0.25em; cursor: pointer; } .form-check-input:checked { background-color: var(--color-primary); border-color: var(--color-primary); } .form-check-label { cursor: pointer; } .btn { display: inline-block; font-family: inherit; font-weight: 500; line-height: 1.5; text-align: center; text-decoration: none; vertical-align: middle; cursor: pointer; user-select: none; border: 1px solid transparent; padding: 0.45rem 1rem; font-size: 0.95rem; border-radius: 6px; transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out; } .btn-primary { color: #fff; background-color: var(--color-primary); border-color: var(--color-primary); } .btn-primary:hover { color: #fff; background-color: var(--color-primary-hover); border-color: var(--color-primary-hover); text-decoration: none; } .btn-success { color: #fff; background-color: var(--color-success); border-color: var(--color-success); } .btn-success:hover { color: #fff; background-color: #157347; border-color: #146c43; text-decoration: none; } .btn-outline-light { color: #f8f9fa; border-color: rgba(255, 255, 255, 0.75); background-color: transparent; } .btn-outline-light:hover { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; text-decoration: none; } .btn-outline-secondary { color: #5c636a; border-color: #6c757d; background-color: transparent; } .btn-outline-secondary:hover { color: #fff; background-color: #6c757d; border-color: #6c757d; text-decoration: none; } .btn-link { font-weight: 400; color: var(--color-primary); background-color: transparent; border: none; padding: 0; } .btn-link:hover { text-decoration: underline; } .btn-sm { padding: 0.25rem 0.5rem; font-size: 0.825rem; border-radius: 4px; } .btn-lg { padding: 0.65rem 1.4rem; font-size: 1.05rem; border-radius: 6px; } .btn:disabled, .btn[disabled] { opacity: 0.55; cursor: not-allowed; pointer-events: none; } .alert { position: relative; padding: 0.85rem 1.15rem; margin-bottom: 1.25rem; border: 1px solid transparent; border-radius: 6px; font-size: 0.95rem; } .alert-danger { color: #842029; background-color: #f8d7da; border-color: #f5c2c7; } .alert-warning { color: #664d03; background-color: #fff3cd; border-color: #ffecb5; } .badge { display: inline-block; padding: 0.35em 0.65em; font-size: 0.75em; font-weight: 600; line-height: 1; text-align: center; white-space: nowrap; vertical-align: middle; border-radius: 0.375rem; } .text-bg-warning { color: #000; background-color: var(--color-warning); } .bg-success { background-color: var(--color-success); color: #fff; } .bg-light { background-color: var(--color-light); } .border { border: 1px solid var(--color-border); } .border-start { border-left: 1px solid var(--color-border); } .rounded { border-radius: 6px; } .card { position: relative; display: flex; flex-direction: column; min-width: 0; word-wrap: break-word; background-color: #fff; background-clip: border-box; border-radius: 6px; overflow: hidden; } .card-header { padding: 0.75rem 1.15rem; margin-bottom: 0; border-bottom: 1px solid var(--color-border); } .card-body { padding: 1.15rem; } .d-flex { display: flex; } .d-inline { display: inline; } .d-inline-block { display: inline-block; } .align-items-center { align-items: center; } .justify-content-between { justify-content: space-between; } .text-end { text-align: right; } .text-muted { color: #64748b; } .text-primary { color: var(--color-primary); } .text-danger { color: var(--color-danger); } .text-white { color: #fff; } .text-decoration-none { text-decoration: none; } .font-weight-bold, .fw-bold { font-weight: 700; } .small { font-size: 0.85em; } .fs-6 { font-size: 0.9rem; } .m-0 { margin: 0; } .mb-0 { margin-bottom: 0; } .mb-2 { margin-bottom: 0.5rem; } .mb-3 { margin-bottom: 1rem; } .mb-4 { margin-bottom: 1.5rem; } .me-2 { margin-right: 0.5rem; } .me-3 { margin-right: 1rem; } .ms-2 { margin-left: 0.5rem; } .ms-4 { margin-left: 1.5rem; } .mt-2 { margin-top: 0.5rem; } .mt-3 { margin-top: 1rem; } .mt-4 { margin-top: 1.5rem; } .p-3 { padding: 1rem; } .ps-3 { padding-left: 1rem; } .px-0 { padding-left: 0; padding-right: 0; } .px-4 { padding-left: 1.5rem; padding-right: 1.5rem; } .py-0 { padding-top: 0; padding-bottom: 0; } .w-100 { width: 100%; } CSS; } if (version_compare(PHP_VERSION, MIN_PHP_VERSION, '<')) { if (PHP_SAPI === 'cli') { fwrite(STDERR, "\033[0;31mError: phpwcms requires PHP " . MIN_PHP_VERSION . ' or higher. Your current PHP version is ' . PHP_VERSION . ".\033[0m\n"); } else { http_response_code(500); echo '
'; echo 'phpwcms requires PHP ' . htmlspecialchars(MIN_PHP_VERSION) . ' or higher.
'; echo 'Your server is currently running PHP ' . htmlspecialchars(PHP_VERSION) . '.
Please upgrade your PHP environment before running the updater.
The upgrade process requires archive support to create file backups and extract the new release.
'; echo 'Neither the PHP ZipArchive extension nor system zip / unzip / tar commands are available on this server.
Please enable the PHP zip extension in your PHP configuration before running the updater.
The upgrade process requires the following PHP extensions to be installed and enabled:
'; echo '' . htmlspecialchars(implode(', ', $missingExtensions)) . '
Please enable the missing extensions in your PHP configuration before running the updater. The upgrade has not been started, no files were changed.
Could not locate the phpwcms configuration file.
'; echo 'Expected location: include/config/conf.inc.php
' . htmlspecialchars(basename($cmsgoBackupFile)) . ' in the same directory.';
if ($isCli) {
cli_print("Legacy cmsgo config migrated to phpwcms format in $configFile\n", 'green');
if (is_file($cmsgoBackupFile)) {
cli_print("Original cmsgo config preserved as $cmsgoBackupFile\n", 'green');
}
}
} else {
$cmsgoMigrationNotice = 'Your legacy cmsGo! configuration file could not be updated automatically. '
. 'The upgrade continues with migrated settings in memory, but please update '
. 'include/config/conf.inc.php manually: rename $cmsgo to '
. '$phpwcms and replace $_SERVER[\'SERVER_NAME\'] / '
. '$_SERVER[\'DOCUMENT_ROOT\'] with static values.';
// Non-fatal: keep running with the in-memory mapping
if ($isCli) {
cli_print("Warning: Could not rewrite legacy cmsgo config $configFile - using mapped values in memory only.\n", 'yellow');
}
}
}
if (empty($phpwcms['db_table']) || empty($phpwcms['db_user'])) {
$err = 'Invalid configuration in ' . $configFile;
if ($isCli) {
cli_print("Error: $err\n", 'red');
exit(1);
}
http_response_code(500);
echo '';
echo '' . htmlspecialchars($err) . '
Two-Factor Authentication is active for account = htmlspecialchars($_SESSION['upgrade_2fa_pending']['usr_name'] ?: $_SESSION['upgrade_2fa_pending']['usr_login']) ?>. Enter your 6-digit authenticator code or a backup recovery code to complete login.
Enter the administrator username and password for this phpwcms installation to continue.
sodium extension is not available on this server.
Whitelabel license validation requires libsodium (PHP 7.2+, usually bundled).
Please enable the extension to use this feature.
The table prefix configured in conf.inc.php does not match the tables found in the database.
Proceeding would back up and migrate the wrong (or no) tables.
| Expected prefix (from conf.inc.php) | = htmlspecialchars($prepend_check['expected']) ?> |
| Detected prefix (from DB tables) | = htmlspecialchars((string)$prepend_check['detected']) ?> |
To resolve:
brand_table_prefix in the card above and save.$phpwcms['brand_table_prefix'] in conf.inc.php to = htmlspecialchars(rtrim((string)$prepend_check['detected'], '_')) ?>.Automated database migrations require the revision system introduced in phpwcms r401. To upgrade an older installation, please follow the manual upgrade instructions. Downgrading to an older version of phpwcms is disallowed to protect your installation and database schema integrity.
The target release (v= htmlspecialchars($releaseInfo['version']) ?>) matches your currently installed version. You can proceed to reinstall and overwrite files to refresh or repair your installation.
The target release (v= htmlspecialchars($releaseInfo['version']) ?>) was released prior to phpwcms 2.0.0 and does not contain an update manifest (.update-manifest) with SHA-256 checksums.
Consequences & Risks: