mirror of
https://github.com/getgrav/grav.git
synced 2026-07-07 01:18:31 +02:00
Merge release/1.8.0 fixes: YamlUpdater undefine, upgrade resilience, PHP 8.3 guard, standalone upgrade script
- Fix YamlUpdater.undefine() to actually remove lines from YAML files - Add maintenance mode (503) during core upgrades via .upgrading flag - Add opcache_reset in sophisticatedInstall() for reliable file operations - Add early PHP 8.3 version check in bin/grav - Add PHP CLI vs web version mismatch hint in Install error messages - Update 1.7 bridge version references from 1.7.50 to 1.7.51 - Disable recovery_mode by default until stability confirmed - Add standalone upgrade.php script (CLI + web) - Update markdowndocs to ^3.0
This commit is contained in:
8
bin/grav
8
bin/grav
@@ -13,6 +13,14 @@ use Grav\Console\Application\GravApplication;
|
||||
\define('GRAV_CLI', true);
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
|
||||
// Early PHP version check before loading any Grav code
|
||||
if (PHP_VERSION_ID < 80300) {
|
||||
echo "Grav 1.8 requires PHP 8.3.0+. You are running PHP " . PHP_VERSION . "\n";
|
||||
echo "Your web server may use a different PHP version.\n";
|
||||
echo "Check your hosting control panel for the correct CLI PHP path.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor/autoload.php')){
|
||||
// Before we can even start, we need to run composer first
|
||||
require_once __DIR__ . '/../system/src/Grav/Common/Composer.php';
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"getgrav/markdowndocs": "^2.0",
|
||||
"getgrav/markdowndocs": "^3.0",
|
||||
"codeception/module-asserts": "*",
|
||||
"codeception/module-phpbrowser": "*",
|
||||
"rector/rector": "^2.1"
|
||||
@@ -134,7 +134,7 @@
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"api": "vendor/bin/phpdoc-md generate system/src > user/pages/17/14.api/default.md",
|
||||
"api": "vendor/bin/phpdoc-md generate --html system/src > ~/workspace/grav-learn/user/pages/api/default.md",
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"rector": "vendor/bin/rector",
|
||||
"rector:php-compat": "@php vendor/bin/rector process --config=system/rector.php --ansi --no-progress-bar",
|
||||
|
||||
17
composer.lock
generated
17
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b39160710f90a14d6be86b6c7237404a",
|
||||
"content-hash": "853eaabfa780ccf3609b909cc345eb0b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "antoligy/dom-string-iterators",
|
||||
@@ -4902,20 +4902,21 @@
|
||||
},
|
||||
{
|
||||
"name": "getgrav/markdowndocs",
|
||||
"version": "2.0.1",
|
||||
"version": "3.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getgrav/PHP-Markdown-Documentation-Generator.git",
|
||||
"reference": "4a24d1b64a88da17e8f1696dc64969f5ca769064"
|
||||
"reference": "b441b225709cf08a62c21c7c5dd775c59b7aaffe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getgrav/PHP-Markdown-Documentation-Generator/zipball/4a24d1b64a88da17e8f1696dc64969f5ca769064",
|
||||
"reference": "4a24d1b64a88da17e8f1696dc64969f5ca769064",
|
||||
"url": "https://api.github.com/repos/getgrav/PHP-Markdown-Documentation-Generator/zipball/b441b225709cf08a62c21c7c5dd775c59b7aaffe",
|
||||
"reference": "b441b225709cf08a62c21c7c5dd775c59b7aaffe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.0",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"php": ">=8.0",
|
||||
"symfony/console": ">=2.6"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -4948,9 +4949,9 @@
|
||||
"description": "Command line tool for generating markdown-formatted class documentation",
|
||||
"homepage": "https://github.com/victorjonsson/PHP-Markdown-Documentation-Generator",
|
||||
"support": {
|
||||
"source": "https://github.com/getgrav/PHP-Markdown-Documentation-Generator/tree/2.0.1"
|
||||
"source": "https://github.com/getgrav/PHP-Markdown-Documentation-Generator/tree/3.0.1"
|
||||
},
|
||||
"time": "2021-04-20T06:04:42+00:00"
|
||||
"time": "2026-02-17T01:09:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
|
||||
13
index.php
13
index.php
@@ -61,6 +61,19 @@ if (PHP_SAPI !== 'cli') {
|
||||
}
|
||||
}
|
||||
|
||||
// Maintenance mode during core upgrade
|
||||
if (file_exists(__DIR__ . '/.upgrading')) {
|
||||
if (time() - filemtime(__DIR__ . '/.upgrading') > 300) {
|
||||
@unlink(__DIR__ . '/.upgrading'); // Stale flag (>5 min), remove it
|
||||
} else {
|
||||
http_response_code(503);
|
||||
header('Retry-After: 60');
|
||||
echo '<!DOCTYPE html><html><head><title>Upgrading</title></head>';
|
||||
echo '<body><h1>Site Upgrading</h1><p>Please try again in a moment.</p></body></html>';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure vendor libraries exist
|
||||
$autoload = __DIR__ . '/vendor/autoload.php';
|
||||
if (!is_file($autoload)) {
|
||||
|
||||
@@ -206,7 +206,7 @@ gpm:
|
||||
updates:
|
||||
safe_upgrade: true # Enable guarded staging+rollback pipeline for Grav self-updates
|
||||
safe_upgrade_snapshot_limit: 5 # Maximum number of safe-upgrade snapshots to retain (0 = unlimited)
|
||||
recovery_mode: true # Enable recovery mode when fatal errors occur during upgrades
|
||||
recovery_mode: false # Enable recovery mode when fatal errors occur during upgrades
|
||||
|
||||
http:
|
||||
method: auto # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
|
||||
|
||||
@@ -298,6 +298,12 @@ class Installer
|
||||
*/
|
||||
public static function sophisticatedInstall($source_path, $install_path, $ignores = [], $keep_source = false)
|
||||
{
|
||||
// Set maintenance mode flag and clear opcache before file operations
|
||||
@file_put_contents(GRAV_ROOT . '/.upgrading', date('Y-m-d H:i:s'));
|
||||
if (function_exists('opcache_reset')) {
|
||||
@opcache_reset();
|
||||
}
|
||||
|
||||
foreach (new DirectoryIterator($source_path) as $file) {
|
||||
if ($file->isLink() || $file->isDot() || in_array($file->getFilename(), $ignores, true)) {
|
||||
continue;
|
||||
@@ -325,6 +331,13 @@ class Installer
|
||||
}
|
||||
}
|
||||
|
||||
// Remove maintenance mode flag and clear opcache after file operations
|
||||
@unlink(GRAV_ROOT . '/.upgrading');
|
||||
clearstatcache(true);
|
||||
if (function_exists('opcache_reset')) {
|
||||
@opcache_reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,8 +83,8 @@ final class Install
|
||||
'grav' => [
|
||||
'name' => 'Grav',
|
||||
'versions' => [
|
||||
'1.7' => '1.7.50',
|
||||
'' => '1.7.50'
|
||||
'1.7' => '1.7.51',
|
||||
'' => '1.7.51'
|
||||
]
|
||||
],
|
||||
'plugins' => [
|
||||
@@ -249,12 +249,25 @@ final class Install
|
||||
$errors = implode("<br />\n", $error);
|
||||
if (\defined('GRAV_CLI') && GRAV_CLI) {
|
||||
$errors = "\n\n" . strip_tags($errors) . "\n\n";
|
||||
$errors .= <<<ERR
|
||||
Please install Grav 1.7.50 first by running following commands:
|
||||
|
||||
wget -q https://getgrav.org/download/core/grav-update/1.7.50 -O tmp/grav-update-v1.7.50.zip
|
||||
bin/gpm direct-install -y tmp/grav-update-v1.7.50.zip
|
||||
rm tmp/grav-update-v1.7.50.zip
|
||||
// Check for PHP CLI vs web mismatch
|
||||
$phpFailed = isset($failedRequirements['php']);
|
||||
if ($phpFailed) {
|
||||
$errors .= <<<ERR
|
||||
Note: Your CLI PHP version may differ from your web server's PHP version.
|
||||
If your web server runs PHP 8.3+, upgrade via the Admin panel instead.
|
||||
Or configure your hosting to use PHP 8.3 for CLI (check your hosting control panel).
|
||||
You can also try: php8.3 bin/gpm self-upgrade
|
||||
|
||||
ERR;
|
||||
}
|
||||
|
||||
$errors .= <<<ERR
|
||||
Please install Grav 1.7.51 first by running following commands:
|
||||
|
||||
wget -q https://getgrav.org/download/core/grav-update/1.7.51 -O tmp/grav-update-v1.7.51.zip
|
||||
bin/gpm direct-install -y tmp/grav-update-v1.7.51.zip
|
||||
rm tmp/grav-update-v1.7.51.zip
|
||||
ERR;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,10 @@ final class YamlUpdater
|
||||
|
||||
$items = Yaml::parse($yaml);
|
||||
if ($items !== $this->items) {
|
||||
throw new \RuntimeException('Failed saving the content');
|
||||
// Lines and items are out of sync — fall back to dumping
|
||||
// from items directly. Loses original formatting but
|
||||
// guarantees the file content matches the intended state.
|
||||
$yaml = Yaml::dump($this->items, 5, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +189,46 @@ final class YamlUpdater
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: support also removing property from handwritten configuration file.
|
||||
$parts = explode('.', $variable);
|
||||
$lineNos = $this->findPath($this->lines, $parts);
|
||||
|
||||
// Get the line number of the target property itself (last in the path).
|
||||
$targetLine = end($lineNos);
|
||||
|
||||
// Negative value means the property was not found in the lines.
|
||||
if ($targetLine === false || $targetLine < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine how many lines to remove: the target line plus any
|
||||
// more-deeply-indented child lines that follow it.
|
||||
$targetIndent = $this->getLineIndentation($this->lines[$targetLine]);
|
||||
$endLine = $targetLine + 1;
|
||||
$total = count($this->lines);
|
||||
|
||||
while ($endLine < $total) {
|
||||
$line = $this->lines[$endLine];
|
||||
if ($this->isLineEmpty($line)) {
|
||||
// Blank/comment lines — tentatively include them; they may
|
||||
// belong to the block being removed or to a following sibling.
|
||||
$endLine++;
|
||||
continue;
|
||||
}
|
||||
if ($this->getLineIndentation($line) > $targetIndent) {
|
||||
// Child line — part of the value being removed.
|
||||
$endLine++;
|
||||
continue;
|
||||
}
|
||||
// Reached a sibling or parent — stop.
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't swallow trailing blank lines that belong to the next section.
|
||||
while ($endLine > $targetLine + 1 && $this->isLineBlank($this->lines[$endLine - 1])) {
|
||||
$endLine--;
|
||||
}
|
||||
|
||||
array_splice($this->lines, $targetLine, $endLine - $targetLine);
|
||||
}
|
||||
|
||||
private function __construct(string $filename)
|
||||
|
||||
387
upgrade.php
Normal file
387
upgrade.php
Normal file
@@ -0,0 +1,387 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Grav Standalone Upgrade Script
|
||||
*
|
||||
* Self-contained PHP file to upgrade any Grav 1.7.x installation to 1.8.0.
|
||||
* No Grav dependencies required — drop into GRAV_ROOT and run via CLI or browser.
|
||||
*
|
||||
* Usage (CLI): php upgrade.php
|
||||
* Usage (Web): Navigate to http://yoursite.com/upgrade.php
|
||||
*
|
||||
* @package Grav\Upgrade
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
// Configuration
|
||||
$gravDownloadUrl = 'https://getgrav.org/download/core/grav-update/latest';
|
||||
$minPhpVersion = '8.3.0';
|
||||
|
||||
// Detect environment
|
||||
$isCli = PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg';
|
||||
$gravRoot = $isCli ? getcwd() : dirname(__FILE__);
|
||||
|
||||
// Output helpers
|
||||
function out($msg, $isCli) {
|
||||
if ($isCli) {
|
||||
echo $msg . "\n";
|
||||
} else {
|
||||
echo htmlspecialchars($msg) . "<br>\n";
|
||||
if (ob_get_level()) { ob_flush(); }
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
function outError($msg, $isCli) {
|
||||
if ($isCli) {
|
||||
fwrite(STDERR, "ERROR: " . $msg . "\n");
|
||||
} else {
|
||||
echo "<strong style='color:red'>ERROR: " . htmlspecialchars($msg) . "</strong><br>\n";
|
||||
if (ob_get_level()) { ob_flush(); }
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
function outSuccess($msg, $isCli) {
|
||||
if ($isCli) {
|
||||
echo "\033[32m" . $msg . "\033[0m\n";
|
||||
} else {
|
||||
echo "<strong style='color:green'>" . htmlspecialchars($msg) . "</strong><br>\n";
|
||||
if (ob_get_level()) { ob_flush(); }
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
// Start output
|
||||
if (!$isCli) {
|
||||
echo '<!DOCTYPE html><html><head><title>Grav Upgrade</title>';
|
||||
echo '<style>body{font-family:monospace;padding:20px;max-width:800px;margin:0 auto;}</style>';
|
||||
echo '</head><body>';
|
||||
echo '<h1>Grav Standalone Upgrade</h1><pre>';
|
||||
}
|
||||
|
||||
out("Grav Standalone Upgrade Script", $isCli);
|
||||
out("==============================", $isCli);
|
||||
out("", $isCli);
|
||||
|
||||
// Step 1: Validate environment
|
||||
out("[1/7] Validating environment...", $isCli);
|
||||
|
||||
// Check PHP version
|
||||
if (version_compare(PHP_VERSION, $minPhpVersion, '<')) {
|
||||
outError("PHP {$minPhpVersion}+ required. You are running PHP " . PHP_VERSION, $isCli);
|
||||
if (!$isCli) {
|
||||
out("If your web server shows a different PHP version, try running this script via CLI:", $isCli);
|
||||
out(" php8.3 upgrade.php", $isCli);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
out(" PHP " . PHP_VERSION . " — OK", $isCli);
|
||||
|
||||
// Verify this is a Grav installation
|
||||
if (!is_file($gravRoot . '/index.php') || !is_dir($gravRoot . '/system') || !is_dir($gravRoot . '/bin')) {
|
||||
outError("This does not appear to be a Grav installation.", $isCli);
|
||||
outError("Place this script in your Grav root directory.", $isCli);
|
||||
exit(1);
|
||||
}
|
||||
out(" Grav root: {$gravRoot} — OK", $isCli);
|
||||
|
||||
// Check write permissions
|
||||
if (!is_writable($gravRoot . '/system')) {
|
||||
outError("The system/ directory is not writable.", $isCli);
|
||||
exit(1);
|
||||
}
|
||||
out(" Write permissions — OK", $isCli);
|
||||
|
||||
// Read current version
|
||||
$currentVersion = 'unknown';
|
||||
$definesFile = $gravRoot . '/system/defines.php';
|
||||
if (is_file($definesFile)) {
|
||||
$content = file_get_contents($definesFile);
|
||||
if (preg_match("/define\('GRAV_VERSION',\s*'([^']+)'\)/", $content, $m)) {
|
||||
$currentVersion = $m[1];
|
||||
}
|
||||
}
|
||||
out(" Current Grav version: {$currentVersion}", $isCli);
|
||||
|
||||
// Check if ZipArchive is available
|
||||
if (!class_exists('ZipArchive')) {
|
||||
outError("PHP ZipArchive extension is required but not available.", $isCli);
|
||||
exit(1);
|
||||
}
|
||||
out(" ZipArchive — OK", $isCli);
|
||||
out("", $isCli);
|
||||
|
||||
// Step 2: Confirm upgrade
|
||||
if ($isCli) {
|
||||
out("[2/7] About to upgrade Grav from {$currentVersion} to latest 1.8.x", $isCli);
|
||||
out(" Press Enter to continue or Ctrl+C to abort...", $isCli);
|
||||
if (defined('STDIN')) {
|
||||
fgets(STDIN);
|
||||
}
|
||||
} else {
|
||||
// In browser mode, check for confirmation parameter
|
||||
if (!isset($_GET['confirm']) || $_GET['confirm'] !== 'yes') {
|
||||
echo "</pre>";
|
||||
echo "<p>Ready to upgrade Grav from <strong>{$currentVersion}</strong> to latest 1.8.x</p>";
|
||||
echo "<p><a href='?confirm=yes' style='background:#4CAF50;color:white;padding:10px 20px;text-decoration:none;border-radius:4px;'>Confirm Upgrade</a></p>";
|
||||
echo "</body></html>";
|
||||
exit(0);
|
||||
}
|
||||
out("[2/7] Upgrade confirmed.", $isCli);
|
||||
}
|
||||
out("", $isCli);
|
||||
|
||||
// Step 3: Download update package
|
||||
out("[3/7] Downloading Grav update package...", $isCli);
|
||||
$tmpDir = $gravRoot . '/tmp';
|
||||
if (!is_dir($tmpDir)) {
|
||||
@mkdir($tmpDir, 0775, true);
|
||||
}
|
||||
$zipPath = $tmpDir . '/grav-upgrade-' . date('YmdHis') . '.zip';
|
||||
|
||||
$downloaded = false;
|
||||
|
||||
// Try curl first
|
||||
if (function_exists('curl_init')) {
|
||||
$ch = curl_init($gravDownloadUrl);
|
||||
$fp = fopen($zipPath, 'w');
|
||||
if ($fp) {
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 300);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
|
||||
if ($httpCode === 200 && filesize($zipPath) > 0) {
|
||||
$downloaded = true;
|
||||
} else {
|
||||
@unlink($zipPath);
|
||||
if ($error) {
|
||||
out(" curl failed: {$error}", $isCli);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to file_get_contents
|
||||
if (!$downloaded && ini_get('allow_url_fopen')) {
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'timeout' => 300,
|
||||
'follow_location' => true,
|
||||
]
|
||||
]);
|
||||
$data = @file_get_contents($gravDownloadUrl, false, $context);
|
||||
if ($data !== false && strlen($data) > 0) {
|
||||
file_put_contents($zipPath, $data);
|
||||
$downloaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$downloaded) {
|
||||
outError("Failed to download update package.", $isCli);
|
||||
outError("You can manually download it and place it at: {$zipPath}", $isCli);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$sizeMb = round(filesize($zipPath) / 1048576, 1);
|
||||
out(" Downloaded {$sizeMb}MB to {$zipPath}", $isCli);
|
||||
out("", $isCli);
|
||||
|
||||
// Step 4: Create backup of system/ and vendor/
|
||||
out("[4/7] Backing up system/ and vendor/...", $isCli);
|
||||
$backupDir = $gravRoot . '/backup/pre-upgrade-' . date('YmdHis');
|
||||
@mkdir($backupDir, 0775, true);
|
||||
|
||||
function recursiveCopy($src, $dst) {
|
||||
if (is_link($src)) {
|
||||
$target = readlink($src);
|
||||
if ($target !== false) {
|
||||
@symlink($target, $dst);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_dir($src)) {
|
||||
@mkdir($dst, 0775, true);
|
||||
$files = scandir($src);
|
||||
foreach ($files as $file) {
|
||||
if ($file === '.' || $file === '..') continue;
|
||||
recursiveCopy($src . '/' . $file, $dst . '/' . $file);
|
||||
}
|
||||
} else {
|
||||
@copy($src, $dst);
|
||||
}
|
||||
}
|
||||
|
||||
recursiveCopy($gravRoot . '/system', $backupDir . '/system');
|
||||
out(" Backed up system/", $isCli);
|
||||
recursiveCopy($gravRoot . '/vendor', $backupDir . '/vendor');
|
||||
out(" Backed up vendor/", $isCli);
|
||||
|
||||
// Also backup key root files
|
||||
foreach (['index.php', 'composer.json', 'composer.lock'] as $rootFile) {
|
||||
if (is_file($gravRoot . '/' . $rootFile)) {
|
||||
@copy($gravRoot . '/' . $rootFile, $backupDir . '/' . $rootFile);
|
||||
}
|
||||
}
|
||||
out(" Backed up root files (index.php, composer.json, composer.lock)", $isCli);
|
||||
out(" Backup location: {$backupDir}", $isCli);
|
||||
out("", $isCli);
|
||||
|
||||
// Step 5: Set maintenance mode and clear opcache
|
||||
out("[5/7] Setting maintenance mode...", $isCli);
|
||||
@file_put_contents($gravRoot . '/.upgrading', date('Y-m-d H:i:s'));
|
||||
if (function_exists('opcache_reset')) {
|
||||
@opcache_reset();
|
||||
out(" OPcache cleared", $isCli);
|
||||
}
|
||||
out(" Maintenance mode enabled", $isCli);
|
||||
out("", $isCli);
|
||||
|
||||
// Step 6: Extract update
|
||||
out("[6/7] Extracting update package...", $isCli);
|
||||
$zip = new ZipArchive();
|
||||
$result = $zip->open($zipPath);
|
||||
if ($result !== true) {
|
||||
outError("Failed to open zip file (error code: {$result})", $isCli);
|
||||
@unlink($gravRoot . '/.upgrading');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$extractDir = $tmpDir . '/grav-extract-' . uniqid();
|
||||
@mkdir($extractDir, 0775, true);
|
||||
$zip->extractTo($extractDir);
|
||||
$zip->close();
|
||||
|
||||
// Find the extracted package root (usually grav-update/ or grav/)
|
||||
$extractedRoot = null;
|
||||
$entries = scandir($extractDir);
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry === '.' || $entry === '..') continue;
|
||||
if (is_dir($extractDir . '/' . $entry)) {
|
||||
$extractedRoot = $extractDir . '/' . $entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$extractedRoot || !is_file($extractedRoot . '/system/defines.php')) {
|
||||
outError("Invalid update package structure.", $isCli);
|
||||
@unlink($gravRoot . '/.upgrading');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Read target version
|
||||
$targetVersion = 'unknown';
|
||||
$targetDefines = file_get_contents($extractedRoot . '/system/defines.php');
|
||||
if (preg_match("/define\('GRAV_VERSION',\s*'([^']+)'\)/", $targetDefines, $m)) {
|
||||
$targetVersion = $m[1];
|
||||
}
|
||||
out(" Target version: {$targetVersion}", $isCli);
|
||||
|
||||
// Items to skip during upgrade (user data, caches, etc.)
|
||||
$ignores = ['backup', 'cache', 'images', 'logs', 'tmp', 'user', '.htaccess', 'robots.txt'];
|
||||
|
||||
// Copy files from extracted package to Grav root
|
||||
$sourceEntries = scandir($extractedRoot);
|
||||
$copied = 0;
|
||||
foreach ($sourceEntries as $entry) {
|
||||
if ($entry === '.' || $entry === '..' || in_array($entry, $ignores)) continue;
|
||||
|
||||
$src = $extractedRoot . '/' . $entry;
|
||||
$dst = $gravRoot . '/' . $entry;
|
||||
|
||||
if (is_dir($src)) {
|
||||
// Remove old directory and copy new one
|
||||
function recursiveDelete($dir) {
|
||||
if (!is_dir($dir)) return;
|
||||
$files = scandir($dir);
|
||||
foreach ($files as $file) {
|
||||
if ($file === '.' || $file === '..') continue;
|
||||
$path = $dir . '/' . $file;
|
||||
if (is_dir($path) && !is_link($path)) {
|
||||
recursiveDelete($path);
|
||||
} else {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
@rmdir($dir);
|
||||
}
|
||||
|
||||
if (is_dir($dst) && !is_link($dst)) {
|
||||
recursiveDelete($dst);
|
||||
}
|
||||
recursiveCopy($src, $dst);
|
||||
|
||||
// Set executable permissions on bin files
|
||||
if ($entry === 'bin') {
|
||||
$binFiles = glob($dst . '/*');
|
||||
if ($binFiles) {
|
||||
foreach ($binFiles as $binFile) {
|
||||
@chmod($binFile, 0755);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@unlink($dst);
|
||||
@copy($src, $dst);
|
||||
}
|
||||
$copied++;
|
||||
}
|
||||
out(" Copied {$copied} items", $isCli);
|
||||
out("", $isCli);
|
||||
|
||||
// Step 7: Cleanup and finalize
|
||||
out("[7/7] Finalizing...", $isCli);
|
||||
|
||||
// Remove maintenance mode
|
||||
@unlink($gravRoot . '/.upgrading');
|
||||
out(" Maintenance mode disabled", $isCli);
|
||||
|
||||
// Clear opcache
|
||||
if (function_exists('opcache_reset')) {
|
||||
@opcache_reset();
|
||||
out(" OPcache cleared", $isCli);
|
||||
}
|
||||
|
||||
// Clear Grav cache
|
||||
$cacheDir = $gravRoot . '/cache';
|
||||
if (is_dir($cacheDir)) {
|
||||
$cacheDirs = glob($cacheDir . '/*');
|
||||
if ($cacheDirs) {
|
||||
foreach ($cacheDirs as $dir) {
|
||||
if (is_dir($dir) && !is_link($dir) && basename($dir) !== '.gitkeep') {
|
||||
recursiveDelete($dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
out(" Cache cleared", $isCli);
|
||||
}
|
||||
|
||||
// Clean up temp files
|
||||
@unlink($zipPath);
|
||||
if (is_dir($extractDir)) {
|
||||
recursiveDelete($extractDir);
|
||||
}
|
||||
out(" Temporary files cleaned up", $isCli);
|
||||
|
||||
// Clear stat cache
|
||||
clearstatcache(true);
|
||||
|
||||
out("", $isCli);
|
||||
outSuccess("Upgrade complete! Grav {$currentVersion} -> {$targetVersion}", $isCli);
|
||||
out("", $isCli);
|
||||
out("Backup saved to: {$backupDir}", $isCli);
|
||||
out("", $isCli);
|
||||
out("IMPORTANT: Delete this upgrade.php file from your Grav root for security.", $isCli);
|
||||
|
||||
if (!$isCli) {
|
||||
echo "</pre></body></html>";
|
||||
}
|
||||
Reference in New Issue
Block a user